From 9b11b9bb68aa417ec836db4f45a37d8cd53bd042 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Thu, 8 May 2025 18:27:19 +0000 Subject: [PATCH 1/9] get single identity agent and list identity agent support added --- pkg/cmd/get/get.go | 1 + pkg/cmd/get/identity_agents.go | 193 +++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 pkg/cmd/get/identity_agents.go diff --git a/pkg/cmd/get/get.go b/pkg/cmd/get/get.go index 43b76df..f0e021f 100644 --- a/pkg/cmd/get/get.go +++ b/pkg/cmd/get/get.go @@ -88,6 +88,7 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string) cmd.AddCommand(NewIdentitysourceCommand(config, streams)) cmd.AddCommand(NewAPIClientsCommand(config, streams)) cmd.AddCommand(NewApplicationsCommand(config, streams)) + cmd.AddCommand(NewIdentityAgentsCommand(config, streams)) return cmd } diff --git a/pkg/cmd/get/identity_agents.go b/pkg/cmd/get/identity_agents.go new file mode 100644 index 0000000..dd18f72 --- /dev/null +++ b/pkg/cmd/get/identity_agents.go @@ -0,0 +1,193 @@ +package get + +import ( + "io" + + "github.com/ibm-verify/verify-sdk-go/pkg/config/integrations" + errorsx "github.com/ibm-verify/verify-sdk-go/pkg/core/errors" + "github.com/ibm-verify/verify-sdk-go/pkg/i18n" + "github.com/ibm-verify/verifyctl/pkg/cmd/resource" + "github.com/ibm-verify/verifyctl/pkg/config" + cmdutil "github.com/ibm-verify/verifyctl/pkg/util/cmd" + "github.com/ibm-verify/verifyctl/pkg/util/templates" + "github.com/spf13/cobra" +) + +const ( + identityAgentsUsage = `identityagents [flags]` + identityAgentsMessagePrefix = "Getidentityagents" + identityAgentsEntitlements = "Manage identityagents" + identityAgentResourceName = "identityagent" +) + +var ( + identityAgentLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(identityAgentsMessagePrefix, ` + Get Identity Agents based on an optional filter or a specific identityagent. + +Resources managed on Verify have specific entitlements, so ensure that the application or Identity agent used +with the 'auth' command is configured with the appropriate entitlements. + +You can identify the entitlement required by running: + + verifyctl get identityagents --entitlements`)) + + identityAgentsExamples = templates.Examples(cmdutil.TranslateExamples(messagePrefix, ` + # Get an identityAgent and print the output in yaml + verifyctl get identityagent -o=yaml --identityAgentID=testIdentityAgent + + # Get 10 identityAgents based on a given search criteria and sort it in the ascending order by name. + verifyctl get identityagents --count=2 --sort=identityAgentName -o=yaml`)) +) + +type identityAgentsOptions struct { + options + identityAgentID string + config *config.CLIConfig +} + +func NewIdentityAgentsCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command { + o := &identityAgentsOptions{ + config: config, + } + + cmd := &cobra.Command{ + Use: identityAgentsUsage, + Short: cmdutil.TranslateShortDesc(identityAgentsMessagePrefix, "Get Identity Agents based on an optional filter or a specific Identity Agent."), + Long: identityAgentLongDesc, + Example: identityAgentsExamples, + Aliases: []string{"identityagent"}, + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.ExitOnError(cmd, o.Complete(cmd, args)) + cmdutil.ExitOnError(cmd, o.Validate(cmd, args)) + cmdutil.ExitOnError(cmd, o.Run(cmd, args)) + }, + } + + cmd.SetOut(streams) + cmd.SetErr(streams) + cmd.SetIn(streams) + + o.AddFlags(cmd) + + return cmd +} + +func (o *identityAgentsOptions) AddFlags(cmd *cobra.Command) { + o.addCommonFlags(cmd, identityAgentResourceName) + cmd.Flags().StringVar(&o.identityAgentID, "identityAgentID", o.identityAgentID, i18n.Translate("identityAgentID to get details")) + o.addSortFlags(cmd, identityAgentResourceName) + o.addCountFlags(cmd, identityAgentResourceName) +} + +func (o *identityAgentsOptions) Complete(cmd *cobra.Command, args []string) error { + return nil +} + +func (o *identityAgentsOptions) Validate(cmd *cobra.Command, args []string) error { + if o.entitlements { + return nil + } + + calledAs := cmd.CalledAs() + if calledAs == "identityagent" && o.identityAgentID == "" { + return errorsx.G11NError("'identityAgentID' flag is required.") + } + return nil +} + +func (o *identityAgentsOptions) Run(cmd *cobra.Command, args []string) error { + if o.entitlements { + cmdutil.WriteString(cmd, entitlementsMessage+" "+identityAgentsEntitlements) + return nil + } + + _, err := o.config.SetAuthToContext(cmd.Context()) + if err != nil { + return err + } + + if cmd.CalledAs() == "identityagent" || len(o.identityAgentID) > 0 { + return o.handleSingleIdentityAgent(cmd, args) + } + + return o.handleIdentityAgentList(cmd, args) +} + +func (o *identityAgentsOptions) handleSingleIdentityAgent(cmd *cobra.Command, _ []string) error { + + c := integrations.NewIdentityAgents() + identityAgent, uri, err := c.GetIdentityAgentByID(cmd.Context(), o.identityAgentID) + if err != nil { + return err + } + + if o.output == "raw" { + cmdutil.WriteAsJSON(cmd, identityAgent, cmd.OutOrStdout()) + return nil + } + + resourceObj := &resource.ResourceObject{ + Kind: resource.ResourceTypePrefix + "IdentityAgent", + APIVersion: "1.0", + Metadata: &resource.ResourceObjectMetadata{ + UID: *identityAgent.ID, + Name: identityAgent.Name, + URI: uri, + }, + Data: identityAgent, + } + + if o.output == "json" { + cmdutil.WriteAsJSON(cmd, resourceObj, cmd.OutOrStdout()) + } else { + cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) + } + + return nil +} + +func (o *identityAgentsOptions) handleIdentityAgentList(cmd *cobra.Command, _ []string) error { + + c := integrations.NewIdentityAgents() + identityAgents, uri, err := c.GetIdentityAgents(cmd.Context(), o.search, o.page, o.limit) + if err != nil { + return err + } + + if o.output == "raw" { + cmdutil.WriteAsJSON(cmd, identityAgents, cmd.OutOrStdout()) + return nil + } + + items := []*resource.ResourceObject{} + for _, agent := range *identityAgents { + items = append(items, &resource.ResourceObject{ + Kind: resource.ResourceTypePrefix + "IdentityAgent", + APIVersion: "1.0", + Metadata: &resource.ResourceObjectMetadata{ + UID: *agent.ID, + Name: agent.Name, + }, + Data: agent, + }) + } + + resourceObj := &resource.ResourceObjectList{ + Kind: resource.ResourceTypePrefix + "List", + APIVersion: "1.0", + Metadata: &resource.ResourceObjectMetadata{ + URI: uri, + Total: 10, + }, + Items: items, + } + + if o.output == "json" { + cmdutil.WriteAsJSON(cmd, resourceObj, cmd.OutOrStdout()) + } else { + cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) + } + + return nil +} From 644d0a5f6c313bb14679f67d1bcba4a918330adf Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Fri, 9 May 2025 09:33:47 +0000 Subject: [PATCH 2/9] delete identity agent support added --- pkg/cmd/delete/delete.go | 1 + pkg/cmd/delete/identity_agent.go | 131 +++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 pkg/cmd/delete/identity_agent.go diff --git a/pkg/cmd/delete/delete.go b/pkg/cmd/delete/delete.go index bc89427..431fb97 100644 --- a/pkg/cmd/delete/delete.go +++ b/pkg/cmd/delete/delete.go @@ -75,6 +75,7 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string) cmd.AddCommand(NewIdentitysourceCommand(config, streams)) cmd.AddCommand(NewAPIClientCommand(config, streams)) cmd.AddCommand(NewApplicationCommand(config, streams)) + cmd.AddCommand(NewIdentityAgentCommand(config, streams)) return cmd } diff --git a/pkg/cmd/delete/identity_agent.go b/pkg/cmd/delete/identity_agent.go new file mode 100644 index 0000000..cd18f46 --- /dev/null +++ b/pkg/cmd/delete/identity_agent.go @@ -0,0 +1,131 @@ +package delete + +import ( + "io" + + "github.com/ibm-verify/verify-sdk-go/pkg/config/integrations" + "github.com/ibm-verify/verify-sdk-go/pkg/i18n" + "github.com/ibm-verify/verifyctl/pkg/config" + cmdutil "github.com/ibm-verify/verifyctl/pkg/util/cmd" + "github.com/ibm-verify/verifyctl/pkg/util/templates" + "github.com/spf13/cobra" + + errorsx "github.com/ibm-verify/verify-sdk-go/pkg/core/errors" +) + +const ( + identityAgentUsage = `identityagent [flags]` + identityAgentMessagePrefix = "DeleteIdentityAgent" + identityAgentEntitlements = "Manage identityAgents" + identityAgentResourceName = "identityagent" +) + +var ( + identityAgentLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(identityAgentMessagePrefix, ` + Delete Identity Agent based on identityAgentID. + +Resources managed on Verify have specific entitlements, so ensure that the Identity agents used +with the 'auth' command is configured with the appropriate entitlements. + +You can identify the entitlement required by running: + + verifyctl delete identityagent --entitlements`)) + + identityAgentExamples = templates.Examples(cmdutil.TranslateExamples(messagePrefix, ` + # Delete an Identity Agent by ID + verifyctl delete identityagent --identityAgentID="12345"`, + )) +) + +type identityAgentsOptions struct { + options + identityAgentID string + config *config.CLIConfig +} + +func NewIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command { + o := &identityAgentsOptions{ + config: config, + } + + cmd := &cobra.Command{ + Use: identityAgentUsage, + Short: cmdutil.TranslateShortDesc(identityAgentMessagePrefix, "Delete Identity Agent based on its id."), + Long: identityAgentLongDesc, + Example: identityAgentExamples, + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.ExitOnError(cmd, o.Complete(cmd, args)) + cmdutil.ExitOnError(cmd, o.Validate(cmd, args)) + cmdutil.ExitOnError(cmd, o.Run(cmd, args)) + }, + } + + cmd.SetOut(streams) + cmd.SetErr(streams) + cmd.SetIn(streams) + + o.AddFlags(cmd) + + return cmd +} + +func (o *identityAgentsOptions) AddFlags(cmd *cobra.Command) { + o.addCommonFlags(cmd) + cmd.Flags().StringVar(&o.identityAgentID, "identityAgentID", o.identityAgentID, i18n.Translate("identityAgentID to be deleted")) +} + +func (o *identityAgentsOptions) Complete(cmd *cobra.Command, args []string) error { + return nil +} + +func (o *identityAgentsOptions) Validate(cmd *cobra.Command, args []string) error { + if o.entitlements { + return nil + } + + calledAs := cmd.CalledAs() + if calledAs == "identityagent" && o.identityAgentID == "" { + return errorsx.G11NError("'identityAgentID' flag is required") + } + return nil +} + +func (o *identityAgentsOptions) Run(cmd *cobra.Command, args []string) error { + if o.entitlements { + cmdutil.WriteString(cmd, entitlementsMessage+" "+identityAgentEntitlements) + return nil + } + + _, err := o.config.SetAuthToContext(cmd.Context()) + if err != nil { + return err + } + + // invoke the operation + if cmd.CalledAs() == "identityagent" { + // deal with single Identity Agent + return o.handleSingleIdentityAgent(cmd, args) + } + return nil +} + +func (o *identityAgentsOptions) handleSingleIdentityAgent(cmd *cobra.Command, _ []string) error { + c := integrations.NewIdentityAgents() + var id string + var err error + + if o.identityAgentID != "" { + id = o.identityAgentID + err = c.DeleteIdentityAgentsById(cmd.Context(), id) + if err != nil { + return err + } + } else { + return errorsx.G11NError("either clientName or clientId must be provided") + } + + resourceIdentifier := o.identityAgentID + cmdutil.WriteString(cmd, "Resource deleted with ID: "+resourceIdentifier) + return nil +} From 0f46f3322316110ff9a2b79db146fafcfba0e226 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Fri, 9 May 2025 10:27:11 +0000 Subject: [PATCH 3/9] enhanced to support identity agent creation --- pkg/cmd/create/create.go | 5 + pkg/cmd/create/identity_agent.go | 190 +++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 pkg/cmd/create/identity_agent.go diff --git a/pkg/cmd/create/create.go b/pkg/cmd/create/create.go index e1680e8..742de73 100644 --- a/pkg/cmd/create/create.go +++ b/pkg/cmd/create/create.go @@ -94,6 +94,7 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string) cmd.AddCommand(newIdentitysourceCommand(config, streams)) cmd.AddCommand(newAPIClientCommand(config, streams)) cmd.AddCommand(newApplicationCommand(config, streams)) + cmd.AddCommand(newIdentityAgentCommand(config, streams)) return cmd } @@ -164,6 +165,10 @@ func (o *options) Run(cmd *cobra.Command, args []string) error { case resource.ResourceTypePrefix + "Applications": options := &applicationOptions{} err = options.createApplicationFromDataMap(cmd, resourceObject.Data.(map[string]interface{})) + + case resource.ResourceTypePrefix + "IdentityAgent": + options := &identityAgentOptions{} + err = options.createIdentityAgentFromDataMap(cmd, resourceObject.Data.(map[string]interface{})) } return err diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go new file mode 100644 index 0000000..2c6de3e --- /dev/null +++ b/pkg/cmd/create/identity_agent.go @@ -0,0 +1,190 @@ +package create + +import ( + "encoding/json" + "io" + "os" + + "github.com/ibm-verify/verify-sdk-go/pkg/config/integrations" + "github.com/ibm-verify/verifyctl/pkg/cmd/resource" + "github.com/ibm-verify/verifyctl/pkg/config" + cmdutil "github.com/ibm-verify/verifyctl/pkg/util/cmd" + "github.com/ibm-verify/verifyctl/pkg/util/templates" + + contextx "github.com/ibm-verify/verify-sdk-go/pkg/core/context" + errorsx "github.com/ibm-verify/verify-sdk-go/pkg/core/errors" + "github.com/spf13/cobra" +) + +const ( + identityAgentUsage = "identityagent [options]" + identityAgentMessagePrefix = "CreateIdentityAgent" + identityAgentEntitlements = "Manage Identity Agents" + identityAgentResourceName = "identityagent" +) + +var ( + identityAgentShortDesc = cmdutil.TranslateShortDesc(identityAgentMessagePrefix, "Options to create an Identity Agent.") + + identityAgentLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(identityAgentMessagePrefix, ` + Options to create an Identity Agent. + + API clients on Verify require specific entitlements, so ensure that the Identity Agent used + with the 'auth' command has the required entitlements. + + An empty resource file can be generated using: + + verifyctl create identityagent --boilerplate + + You can check required entitlements by running: + + verifyctl create identityagent --entitlements`)) + + identityAgentExamples = templates.Examples(cmdutil.TranslateExamples(identityAgentMessagePrefix, ` + # Create an empty Identity agent resource. + verifyctl create identityagent --boilerplate + + # Create an API client using a JSON file. + verifyctl create identityagent -f=./identityagent.json`)) +) + +type identityAgentOptions struct { + options + config *config.CLIConfig +} + +func newIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command { + o := &identityAgentOptions{ + config: config, + } + + cmd := &cobra.Command{ + Use: identityAgentUsage, + Short: identityAgentShortDesc, + Long: identityAgentLongDesc, + Example: identityAgentExamples, + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.ExitOnError(cmd, o.Complete(cmd, args)) + cmdutil.ExitOnError(cmd, o.Validate(cmd, args)) + cmdutil.ExitOnError(cmd, o.Run(cmd, args)) + }, + } + + cmd.SetOut(streams) + cmd.SetErr(streams) + cmd.SetIn(streams) + + o.AddFlags(cmd) + + return cmd +} + +func (o *identityAgentOptions) AddFlags(cmd *cobra.Command) { + o.addCommonFlags(cmd, identityAgentResourceName) + cmd.Flags().StringVarP(&o.file, "file", "f", "", "Path to the yaml file containing API client data.") +} + +func (o *identityAgentOptions) Complete(cmd *cobra.Command, args []string) error { + return nil +} + +func (o *identityAgentOptions) Validate(cmd *cobra.Command, args []string) error { + if o.entitlements || o.boilerplate { + return nil + } + + if len(o.file) == 0 { + return errorsx.G11NError("The 'file' option is required if no other options are used.") + } + return nil +} + +func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { + if o.entitlements { + cmdutil.WriteString(cmd, entitlementsMessage+" "+identityAgentEntitlements) + return nil + } + + if o.boilerplate { + resourceObj := &resource.ResourceObject{ + Kind: resource.ResourceTypePrefix + "IdentityAgent", + APIVersion: "1.0", + Data: &integrations.IdentityAgentsConfig{}, + } + + cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) + return nil + } + + _, err := o.config.GetCurrentAuth() + if err != nil { + return err + } + + return o.createIdentityAgent(cmd) +} + +func (o *identityAgentOptions) createIdentityAgent(cmd *cobra.Command) error { + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + b, err := os.ReadFile(o.file) + if err != nil { + vc.Logger.Errorf("unable to read file; filename=%s, err=%v", o.file, err) + return err + } + + return o.createIdentityAgentWithData(cmd, b) +} + +func (o *identityAgentOptions) createIdentityAgentWithData(cmd *cobra.Command, data []byte) error { + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + identityAgentConfig := &integrations.IdentityAgentsConfig{} + if err := json.Unmarshal(data, &identityAgentConfig); err != nil { + vc.Logger.Errorf("unable to unmarshal API client; err=%v", err) + return err + } + + client := integrations.NewIdentityAgents() + resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) + if err != nil { + vc.Logger.Errorf("failed to create API client; err=%v", err) + return err + } + + cmdutil.WriteString(cmd, "Resource created: "+resourceURI) + return nil +} + +func (o *identityAgentOptions) createIdentityAgentFromDataMap(cmd *cobra.Command, data map[string]interface{}) error { + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + // Convert map data to JSON + identityAgentConfig := &integrations.IdentityAgentsConfig{} + b, err := json.Marshal(data) + if err != nil { + vc.Logger.Errorf("failed to marshal data; err=%v", err) + return err + } + + if err := json.Unmarshal(b, identityAgentConfig); err != nil { + vc.Logger.Errorf("unable to unmarshal data to Identoty Agent; err=%v", err) + return err + } + + // Create API client + client := integrations.NewIdentityAgents() + resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) + if err != nil { + vc.Logger.Errorf("failed to create API client; err=%v", err) + return err + } + + // Directly return the created resource URI + cmdutil.WriteString(cmd, "Resource created: "+resourceURI) + return nil +} From 4449dbbcf1e75c5f0672266e0ee339a1fefa5362 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Fri, 9 May 2025 10:43:38 +0000 Subject: [PATCH 4/9] struct type name changed --- pkg/cmd/create/identity_agent.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index 2c6de3e..c03dde3 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -110,7 +110,7 @@ func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { resourceObj := &resource.ResourceObject{ Kind: resource.ResourceTypePrefix + "IdentityAgent", APIVersion: "1.0", - Data: &integrations.IdentityAgentsConfig{}, + Data: &integrations.IdentityAgentConfig{}, } cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) @@ -142,7 +142,7 @@ func (o *identityAgentOptions) createIdentityAgentWithData(cmd *cobra.Command, d ctx := cmd.Context() vc := contextx.GetVerifyContext(ctx) - identityAgentConfig := &integrations.IdentityAgentsConfig{} + identityAgentConfig := &integrations.IdentityAgentConfig{} if err := json.Unmarshal(data, &identityAgentConfig); err != nil { vc.Logger.Errorf("unable to unmarshal API client; err=%v", err) return err @@ -164,7 +164,7 @@ func (o *identityAgentOptions) createIdentityAgentFromDataMap(cmd *cobra.Command vc := contextx.GetVerifyContext(ctx) // Convert map data to JSON - identityAgentConfig := &integrations.IdentityAgentsConfig{} + identityAgentConfig := &integrations.IdentityAgentConfig{} b, err := json.Marshal(data) if err != nil { vc.Logger.Errorf("failed to marshal data; err=%v", err) From 7a48de475333aac636d8e572304ab9e483592d09 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Fri, 9 May 2025 17:45:02 +0000 Subject: [PATCH 5/9] modified boilerplate generation and added purpose flag for identity agent --- pkg/cmd/create/identity_agent.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index c03dde3..849acbe 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -2,6 +2,7 @@ package create import ( "encoding/json" + "fmt" "io" "os" @@ -29,12 +30,12 @@ var ( identityAgentLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(identityAgentMessagePrefix, ` Options to create an Identity Agent. - API clients on Verify require specific entitlements, so ensure that the Identity Agent used + Identity Agents on Verify require specific entitlements, so ensure that the Identity Agent used with the 'auth' command has the required entitlements. An empty resource file can be generated using: - verifyctl create identityagent --boilerplate + verifyctl create identityagent --purpose=PROV --boilerplate You can check required entitlements by running: @@ -44,13 +45,14 @@ var ( # Create an empty Identity agent resource. verifyctl create identityagent --boilerplate - # Create an API client using a JSON file. + # Create an Identity Agent using a JSON file. verifyctl create identityagent -f=./identityagent.json`)) ) type identityAgentOptions struct { options - config *config.CLIConfig + purpose string + config *config.CLIConfig } func newIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command { @@ -82,7 +84,8 @@ func newIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *c func (o *identityAgentOptions) AddFlags(cmd *cobra.Command) { o.addCommonFlags(cmd, identityAgentResourceName) - cmd.Flags().StringVarP(&o.file, "file", "f", "", "Path to the yaml file containing API client data.") + cmd.Flags().StringVarP(&o.file, "file", "f", "", "Path to the yaml file containing Identity Agent data.") + cmd.Flags().StringVarP(&o.purpose, "purpose", "p", "", "Purpose of the Identity Agent, [PROV, LDAPAUTH]") } func (o *identityAgentOptions) Complete(cmd *cobra.Command, args []string) error { @@ -107,10 +110,16 @@ func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { } if o.boilerplate { + identityAgent := &integrations.IdentityAgentConfig{} + if o.purpose == "PROV" || o.purpose == "LDAPAUTH" { + integrations.AddModule(identityAgent, o.purpose) + } else { + return fmt.Errorf("unknown purpose") + } resourceObj := &resource.ResourceObject{ Kind: resource.ResourceTypePrefix + "IdentityAgent", APIVersion: "1.0", - Data: &integrations.IdentityAgentConfig{}, + Data: identityAgent, } cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) @@ -144,14 +153,14 @@ func (o *identityAgentOptions) createIdentityAgentWithData(cmd *cobra.Command, d identityAgentConfig := &integrations.IdentityAgentConfig{} if err := json.Unmarshal(data, &identityAgentConfig); err != nil { - vc.Logger.Errorf("unable to unmarshal API client; err=%v", err) + vc.Logger.Errorf("unable to unmarshal Identity Agent; err=%v", err) return err } client := integrations.NewIdentityAgents() resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) if err != nil { - vc.Logger.Errorf("failed to create API client; err=%v", err) + vc.Logger.Errorf("failed to create Identity Agent; err=%v", err) return err } @@ -176,11 +185,11 @@ func (o *identityAgentOptions) createIdentityAgentFromDataMap(cmd *cobra.Command return err } - // Create API client + // Create Identity Agent client := integrations.NewIdentityAgents() resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) if err != nil { - vc.Logger.Errorf("failed to create API client; err=%v", err) + vc.Logger.Errorf("failed to create Identity Agent; err=%v", err) return err } From fb620369c2aab6319383fcbf8050214638c3d3d1 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Tue, 13 May 2025 13:13:21 +0000 Subject: [PATCH 6/9] update support for identity agent added --- pkg/cmd/create/identity_agent.go | 4 +- pkg/cmd/delete/identity_agent.go | 4 +- pkg/cmd/get/identity_agents.go | 4 +- pkg/cmd/replace/identity_agent.go | 209 +++++++++++++++++++++++++++++ pkg/cmd/replace/password_policy.go | 2 +- pkg/cmd/replace/replace.go | 5 + 6 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 pkg/cmd/replace/identity_agent.go diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index 849acbe..2351d03 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -157,7 +157,7 @@ func (o *identityAgentOptions) createIdentityAgentWithData(cmd *cobra.Command, d return err } - client := integrations.NewIdentityAgents() + client := integrations.NewIdentityAgentClient() resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) if err != nil { vc.Logger.Errorf("failed to create Identity Agent; err=%v", err) @@ -186,7 +186,7 @@ func (o *identityAgentOptions) createIdentityAgentFromDataMap(cmd *cobra.Command } // Create Identity Agent - client := integrations.NewIdentityAgents() + client := integrations.NewIdentityAgentClient() resourceURI, err := client.CreateIdentityAgent(ctx, identityAgentConfig) if err != nil { vc.Logger.Errorf("failed to create Identity Agent; err=%v", err) diff --git a/pkg/cmd/delete/identity_agent.go b/pkg/cmd/delete/identity_agent.go index cd18f46..7f77a63 100644 --- a/pkg/cmd/delete/identity_agent.go +++ b/pkg/cmd/delete/identity_agent.go @@ -111,13 +111,13 @@ func (o *identityAgentsOptions) Run(cmd *cobra.Command, args []string) error { } func (o *identityAgentsOptions) handleSingleIdentityAgent(cmd *cobra.Command, _ []string) error { - c := integrations.NewIdentityAgents() + c := integrations.NewIdentityAgentClient() var id string var err error if o.identityAgentID != "" { id = o.identityAgentID - err = c.DeleteIdentityAgentsById(cmd.Context(), id) + err = c.DeleteIdentityAgentByID(cmd.Context(), id) if err != nil { return err } diff --git a/pkg/cmd/get/identity_agents.go b/pkg/cmd/get/identity_agents.go index dd18f72..e0786fe 100644 --- a/pkg/cmd/get/identity_agents.go +++ b/pkg/cmd/get/identity_agents.go @@ -116,7 +116,7 @@ func (o *identityAgentsOptions) Run(cmd *cobra.Command, args []string) error { func (o *identityAgentsOptions) handleSingleIdentityAgent(cmd *cobra.Command, _ []string) error { - c := integrations.NewIdentityAgents() + c := integrations.NewIdentityAgentClient() identityAgent, uri, err := c.GetIdentityAgentByID(cmd.Context(), o.identityAgentID) if err != nil { return err @@ -149,7 +149,7 @@ func (o *identityAgentsOptions) handleSingleIdentityAgent(cmd *cobra.Command, _ func (o *identityAgentsOptions) handleIdentityAgentList(cmd *cobra.Command, _ []string) error { - c := integrations.NewIdentityAgents() + c := integrations.NewIdentityAgentClient() identityAgents, uri, err := c.GetIdentityAgents(cmd.Context(), o.search, o.page, o.limit) if err != nil { return err diff --git a/pkg/cmd/replace/identity_agent.go b/pkg/cmd/replace/identity_agent.go new file mode 100644 index 0000000..360e9de --- /dev/null +++ b/pkg/cmd/replace/identity_agent.go @@ -0,0 +1,209 @@ +package replace + +import ( + "io" + "os" + + "github.com/ibm-verify/verify-sdk-go/pkg/config/integrations" + + contextx "github.com/ibm-verify/verify-sdk-go/pkg/core/context" + errorsx "github.com/ibm-verify/verify-sdk-go/pkg/core/errors" + "github.com/ibm-verify/verify-sdk-go/pkg/i18n" + "github.com/ibm-verify/verifyctl/pkg/cmd/resource" + "github.com/ibm-verify/verifyctl/pkg/config" + cmdutil "github.com/ibm-verify/verifyctl/pkg/util/cmd" + "github.com/ibm-verify/verifyctl/pkg/util/templates" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +const ( + identityAgentUsage = `identityagent [options]` + identityAgentMessagePrefix = "UpdateIdentityAgent" + identityAgentEntitlements = "Manage identity agent" + identityAgentResourceName = "identityagent" +) + +var ( + identityAgentShortDesc = cmdutil.TranslateShortDesc(identityAgentMessagePrefix, "Update an identity agent resource.") + + identityAgentLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(identityAgentMessagePrefix, ` + Update an identity agent resource. + +Resources managed on Verify require specific entitlements, so ensure that the application or API client used +with the 'auth' command is configured with the appropriate entitlements. + +An empty resource file can be generated using: + + verifyctl replace identityagent --boilerplate + +You can identify the entitlement required by running: + + verifyctl replace identityagent --entitlements`)) + + identityAgentExamples = templates.Examples(cmdutil.TranslateExamples(identityAgentMessagePrefix, ` + # Generate an empty identityAgent resource template + verifyctl replace identityagent --boilerplate + + # Update a identity agent from a JSON file + verifyctl replace identityagent -f=./identity_agent.json`)) +) + +type identityAgentOptions struct { + options + + config *config.CLIConfig +} + +func newIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command { + o := &identityAgentOptions{ + config: config, + } + + cmd := &cobra.Command{ + Use: identityAgentUsage, + Short: identityAgentShortDesc, + Long: identityAgentLongDesc, + Example: identityAgentExamples, + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.ExitOnError(cmd, o.Complete(cmd, args)) + cmdutil.ExitOnError(cmd, o.Validate(cmd, args)) + cmdutil.ExitOnError(cmd, o.Run(cmd, args)) + }, + } + + cmd.SetOut(streams) + cmd.SetErr(streams) + cmd.SetIn(streams) + + o.AddFlags(cmd) + + return cmd +} + +func (o *identityAgentOptions) AddFlags(cmd *cobra.Command) { + o.addCommonFlags(cmd, identityAgentResourceName) + cmd.Flags().StringVarP(&o.file, "file", "f", "", i18n.Translate("Path to the file that contains the input data. The contents of the file are expected to be formatted to match the API contract.")) +} + +func (o *identityAgentOptions) Complete(cmd *cobra.Command, args []string) error { + return nil +} + +func (o *identityAgentOptions) Validate(cmd *cobra.Command, args []string) error { + if o.entitlements || o.boilerplate { + return nil + } + + if len(o.file) == 0 { + return errorsx.G11NError(i18n.Translate("'file' option is required if no other options are used.")) + } + return nil +} + +func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { + if o.entitlements { + cmdutil.WriteString(cmd, entitlementsMessage+" "+identityAgentEntitlements) + return nil + } + + idStr := "" + if o.boilerplate { + resourceObj := &resource.ResourceObject{ + Kind: resource.ResourceTypePrefix + "IdentityAgent", + APIVersion: "3.0", + Data: &integrations.IdentityAgentConfig{ + ID: &idStr, + }, + } + + cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) + return nil + } + + _, err := o.config.SetAuthToContext(cmd.Context()) + if err != nil { + return err + } + + return o.updateIdentityAgent(cmd) +} + +func (o *identityAgentOptions) updateIdentityAgent(cmd *cobra.Command) error { + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + b, err := os.ReadFile(o.file) + + if err != nil { + vc.Logger.Errorf("unable to read file; filename=%s, err=%v", o.file, err) + return err + } + return o.updateIdentityAgentWithData(cmd, b) +} + +func (o *identityAgentOptions) updateIdentityAgentWithData(cmd *cobra.Command, data []byte) error { + + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + resourceObj := &resource.ResourceObject{} + if err := yaml.Unmarshal(data, resourceObj); err != nil { + vc.Logger.Errorf("unable to unmarshal YAML to resource object; err=%v", err) + return err + } + identityAgent, ok := resourceObj.Data.(*integrations.IdentityAgentConfig) + if !ok { + appData, err := yaml.Marshal(resourceObj.Data) + if err != nil { + vc.Logger.Errorf("unable to marshal resource data; err=%v", err) + return err + } + identityAgent = &integrations.IdentityAgentConfig{} + if err := yaml.Unmarshal(appData, identityAgent); err != nil { + vc.Logger.Errorf("unable to unmarshal data to identity agent; err=%v", err) + return err + } + } + if err := yaml.Unmarshal(data, &identityAgent); err != nil { + vc.Logger.Errorf("unable to unmarshal the identityAgent; err=%v", err) + return err + } + + client := integrations.NewIdentityAgentClient() + if err := client.UpdateIdentityAgent(ctx, identityAgent); err != nil { + vc.Logger.Errorf("unable to update the identity agent; err=%v, identityAgent=%+v", err, identityAgent) + return err + } + + cmdutil.WriteString(cmd, "Identity Agent updated successfully") + return nil +} + +func (o *identityAgentOptions) updateIdentityAgentFromDataMap(cmd *cobra.Command, data map[string]interface{}) error { + ctx := cmd.Context() + vc := contextx.GetVerifyContext(ctx) + + identityAgent := &integrations.IdentityAgentConfig{} + b, err := yaml.Marshal(data) + + if err != nil { + vc.Logger.Errorf("failed to marshal the data map; err=%v", err) + return err + } + + if err := yaml.Unmarshal(b, identityAgent); err != nil { + vc.Logger.Errorf("unable to unmarshal to a identity agent; err=%v", err) + return err + } + + client := integrations.NewIdentityAgentClient() + if err := client.UpdateIdentityAgent(ctx, identityAgent); err != nil { + vc.Logger.Errorf("unable to update identity agent; err=%v, identityAgent=%+v", err, identityAgent) + return err + } + + cmdutil.WriteString(cmd, "Identity Agent updated successfully") + return nil +} diff --git a/pkg/cmd/replace/password_policy.go b/pkg/cmd/replace/password_policy.go index b97c379..b7e08be 100644 --- a/pkg/cmd/replace/password_policy.go +++ b/pkg/cmd/replace/password_policy.go @@ -187,6 +187,6 @@ func (o *passwordPolicyOptions) updatePasswordPolicyFromDataMap(cmd *cobra.Comma return err } - cmdutil.WriteString(cmd, "Resource updated") + cmdutil.WriteString(cmd, "Password Policy updated successfully") return nil } diff --git a/pkg/cmd/replace/replace.go b/pkg/cmd/replace/replace.go index 3b038fc..5f4a38c 100644 --- a/pkg/cmd/replace/replace.go +++ b/pkg/cmd/replace/replace.go @@ -92,6 +92,7 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string) cmd.AddCommand(newAPIClientCommand(config, streams)) cmd.AddCommand(newApplicationCommand(config, streams)) cmd.AddCommand(newPasswordPolicyCommand(config, streams)) + cmd.AddCommand(newIdentityAgentCommand(config, streams)) return cmd } @@ -166,6 +167,10 @@ func (o *options) Run(cmd *cobra.Command, args []string) error { case resource.ResourceTypePrefix + "PasswordPolicy": options := &passwordPolicyOptions{} err = options.updatePasswordPolicyFromDataMap(cmd, resourceObject.Data.(map[string]interface{})) + + case resource.ResourceTypePrefix + "IdentityAgent": + options := &identityAgentOptions{} + err = options.updateIdentityAgentFromDataMap(cmd, resourceObject.Data.(map[string]interface{})) } return err From aaffbdb5a5ba4f18134fd06353f5ed7197fecb58 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Tue, 13 May 2025 13:41:48 +0000 Subject: [PATCH 7/9] generating boilerplate based on the type of the agent --- pkg/cmd/create/identity_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index 2351d03..1135887 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -85,7 +85,7 @@ func newIdentityAgentCommand(config *config.CLIConfig, streams io.ReadWriter) *c func (o *identityAgentOptions) AddFlags(cmd *cobra.Command) { o.addCommonFlags(cmd, identityAgentResourceName) cmd.Flags().StringVarP(&o.file, "file", "f", "", "Path to the yaml file containing Identity Agent data.") - cmd.Flags().StringVarP(&o.purpose, "purpose", "p", "", "Purpose of the Identity Agent, [PROV, LDAPAUTH]") + cmd.Flags().StringVarP(&o.purpose, "purpose", "p", "", "Purpose of the Identity Agent, [PROV, LDAPAUTH, EXTAUTHN]") } func (o *identityAgentOptions) Complete(cmd *cobra.Command, args []string) error { @@ -111,7 +111,7 @@ func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { if o.boilerplate { identityAgent := &integrations.IdentityAgentConfig{} - if o.purpose == "PROV" || o.purpose == "LDAPAUTH" { + if o.purpose == "PROV" || o.purpose == "LDAPAUTH" || o.purpose == "EXTAUTHN" || o.purpose == "" { integrations.AddModule(identityAgent, o.purpose) } else { return fmt.Errorf("unknown purpose") From e0a4c35f6976e5c320ca3b8e80ca894938ba3db1 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Wed, 21 May 2025 11:54:42 +0000 Subject: [PATCH 8/9] boilerplate generation code for identity agent added --- pkg/cmd/create/identity_agent.go | 2 +- pkg/cmd/resource/boilerplate-generate.go | 55 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index 1135887..b6aacf5 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -112,7 +112,7 @@ func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { if o.boilerplate { identityAgent := &integrations.IdentityAgentConfig{} if o.purpose == "PROV" || o.purpose == "LDAPAUTH" || o.purpose == "EXTAUTHN" || o.purpose == "" { - integrations.AddModule(identityAgent, o.purpose) + resource.CreateIdentityAgentBoilerplate(identityAgent, o.purpose) } else { return fmt.Errorf("unknown purpose") } diff --git a/pkg/cmd/resource/boilerplate-generate.go b/pkg/cmd/resource/boilerplate-generate.go index 60a3224..85b2c9b 100644 --- a/pkg/cmd/resource/boilerplate-generate.go +++ b/pkg/cmd/resource/boilerplate-generate.go @@ -3,6 +3,7 @@ package resource import ( "github.com/ibm-verify/verify-sdk-go/pkg/config/applications" "github.com/ibm-verify/verify-sdk-go/pkg/config/authentication" + "github.com/ibm-verify/verify-sdk-go/pkg/config/integrations" "github.com/ibm-verify/verify-sdk-go/pkg/config/security" ) @@ -303,3 +304,57 @@ func GetSignInOptionsBoilerplate() *SignInOptions { } return signInOptions } + +func CreateIdentityAgentBoilerplate(identityAgent *integrations.IdentityAgentConfig, identityType string) { + identityAgent.Purpose = (*integrations.OnpremAgentConfigurationPurpose)(&identityType) + timeoutVal := int32(1) + identityAgent.AuthnCacheTimeout = &timeoutVal + certLavel := " " + identityAgent.CertLabel = &certLavel + identityAgent.References = &[]integrations.OnpremAgentConfigReference{{}} + if identityType == "PROV" { + identityAgent.Modules = append(identityAgent.Modules, map[string]map[string]interface{}{ + "external": { + "caCerts": "", + "id": "", + "password": "", + "uri": []string{}, + }, + }) + } else if identityType == "LDAPAUTH" { + identityAgent.Modules = append(identityAgent.Modules, map[string]map[string]interface{}{ + "ldapauth": { + "ldapBindPwd": "", + "ldapBindDn": "", + "ldapCACerts": "", + "ldapConnIdleTime": 0, + "ldapConnMaxTime": 0, + "ldapFetchAttributes": []string{}, + "ldapFetchBinaryAttributes": []string{}, + "ldapMaxConnections": 0, + "ldapRequestTimeout": 0, + "ldapSearchBase": "o=ibm,c=us", + "ldapStartTls": false, + "ldapUri": []string{}, + "ldapUsernameAttribute": "", + "ldapUserSearchObjectclass": []string{}, + }, + }) + } else if identityType == "EXTAUTHN" { + identityAgent.Modules = append(identityAgent.Modules, map[string]map[string]interface{}{ + "extauthn": { + "authentication": map[string]interface{}{ + "type": "", + "basic": map[string]interface{}{ + "username": "", + "password": "", + }, + }, + + "caCerts": "", + "uris": []string{}, + "fetchAttributes": []string{}, + }, + }) + } +} From d6a8e3b094ccb20172babe858e7b898f977a5aa1 Mon Sep 17 00:00:00 2001 From: Sabuj Mondal Date: Mon, 26 May 2025 17:40:12 +0000 Subject: [PATCH 9/9] identity agent boilerplate generation --- pkg/cmd/create/identity_agent.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pkg/cmd/create/identity_agent.go b/pkg/cmd/create/identity_agent.go index b6aacf5..9e7225a 100644 --- a/pkg/cmd/create/identity_agent.go +++ b/pkg/cmd/create/identity_agent.go @@ -2,7 +2,6 @@ package create import ( "encoding/json" - "fmt" "io" "os" @@ -110,20 +109,18 @@ func (o *identityAgentOptions) Run(cmd *cobra.Command, args []string) error { } if o.boilerplate { - identityAgent := &integrations.IdentityAgentConfig{} if o.purpose == "PROV" || o.purpose == "LDAPAUTH" || o.purpose == "EXTAUTHN" || o.purpose == "" { - resource.CreateIdentityAgentBoilerplate(identityAgent, o.purpose) + resourceObj := &resource.ResourceObject{ + Kind: resource.ResourceTypePrefix + "IdentityAgent", + APIVersion: "1.0", + Data: integrations.IdentityAgentExample(o.purpose), + } + + cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) + return nil } else { - return fmt.Errorf("unknown purpose") + return errorsx.G11NError("unknown purpose") } - resourceObj := &resource.ResourceObject{ - Kind: resource.ResourceTypePrefix + "IdentityAgent", - APIVersion: "1.0", - Data: identityAgent, - } - - cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout()) - return nil } _, err := o.config.GetCurrentAuth()