From 10d67ad960275df47c27bbd542e810ab3eb6b3e2 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Sat, 4 Nov 2023 18:29:21 -0500 Subject: [PATCH 1/7] PLTCONN-4069: Limit number of account reads --- cmd/connector/validate/account_read.go | 8 ++++++-- cmd/connector/validate/entitlement_read.go | 8 ++++++-- cmd/connector/validate/util.go | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cmd/connector/validate/account_read.go b/cmd/connector/validate/account_read.go index 77c9071..b27b8c6 100644 --- a/cmd/connector/validate/account_read.go +++ b/cmd/connector/validate/account_read.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/kr/pretty" + connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client" ) @@ -24,14 +25,17 @@ var accountReadChecks = []Check{ res.err(err) return } - + count := 0 for _, account := range accounts { + if count > accountReadLimit { + break + } acct, _, err := cc.AccountRead(ctx, account.ID(), account.UniqueID(), nil) if err != nil { res.err(err) return } - + count++ if acct.Identity != account.Identity { res.errf("want %q; got %q", account.Identity, acct.Identity) } diff --git a/cmd/connector/validate/entitlement_read.go b/cmd/connector/validate/entitlement_read.go index 4ffaf64..08306d1 100644 --- a/cmd/connector/validate/entitlement_read.go +++ b/cmd/connector/validate/entitlement_read.go @@ -4,6 +4,7 @@ import ( "context" "github.com/kr/pretty" + connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client" ) @@ -42,14 +43,17 @@ var entitlementReadChecks = []Check{ res.warnf("no entitlements") return } - + count := 0 for _, e := range entitlements { + if count > accountReadLimit { + break + } eRead, _, err := cc.EntitlementRead(ctx, e.ID(), e.UniqueID(), "group", nil) if err != nil { res.errf("failed to read entitlement %q: %s", e.Identity, err.Error()) return } - + count++ if e.Identity != eRead.Identity { res.errf("want %q; got %q", e.Identity, eRead.Identity) } diff --git a/cmd/connector/validate/util.go b/cmd/connector/validate/util.go index adc91c9..165f76d 100644 --- a/cmd/connector/validate/util.go +++ b/cmd/connector/validate/util.go @@ -12,6 +12,10 @@ import ( connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client" ) +const ( + accountReadLimit = 8 +) + // entitlementAttr returns the attribute for entitlements func entitlementAttr(spec *connclient.ConnSpec) string { for _, attr := range spec.AccountSchema.Attributes { From fbb601dfa7c37caa7dcea39084090372f19f0d31 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Sun, 5 Nov 2023 16:54:04 -0600 Subject: [PATCH 2/7] PLTCONN-4069: Set read limit for account and entitlement read --- cmd/connector/conn_validate.go | 9 ++++++--- cmd/connector/conn_validate_sources.go | 18 +++++++++++------- cmd/connector/validate/account_create.go | 9 +++++---- cmd/connector/validate/account_read.go | 8 ++++---- cmd/connector/validate/account_update.go | 4 ++-- cmd/connector/validate/check.go | 2 +- cmd/connector/validate/entitlement_read.go | 8 ++++---- cmd/connector/validate/test_conn.go | 4 ++-- cmd/connector/validate/validate.go | 6 +++++- 9 files changed, 40 insertions(+), 28 deletions(-) diff --git a/cmd/connector/conn_validate.go b/cmd/connector/conn_validate.go index 62ea3e1..d0fbe64 100644 --- a/cmd/connector/conn_validate.go +++ b/cmd/connector/conn_validate.go @@ -8,9 +8,10 @@ import ( "github.com/logrusorgru/aurora" "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" + connvalidate "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/validate" "github.com/sailpoint-oss/sailpoint-cli/internal/client" - "github.com/spf13/cobra" ) func newConnValidateCmd(apiClient client.Client) *cobra.Command { @@ -43,9 +44,11 @@ func newConnValidateCmd(apiClient client.Client) *cobra.Command { check := cmd.Flags().Lookup("check").Value.String() isReadOnly, _ := strconv.ParseBool(cmd.Flags().Lookup("read-only").Value.String()) + isReadLimit, _ := strconv.ParseBool(cmd.Flags().Lookup("read-limit").Value.String()) valid := connvalidate.NewValidator(connvalidate.Config{ - Check: check, - ReadOnly: isReadOnly, + Check: check, + ReadOnly: isReadOnly, + ReadLimit: isReadLimit, }, cc) results, err := valid.Run(ctx) diff --git a/cmd/connector/conn_validate_sources.go b/cmd/connector/conn_validate_sources.go index c5c1d88..9ef63ca 100644 --- a/cmd/connector/conn_validate_sources.go +++ b/cmd/connector/conn_validate_sources.go @@ -11,18 +11,20 @@ import ( "net/http" "os" "os/exec" + "strconv" "strings" "syscall" "time" "github.com/logrusorgru/aurora" "github.com/olekukonko/tablewriter" - connvalidate "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/validate" - "github.com/sailpoint-oss/sailpoint-cli/internal/client" - "github.com/sailpoint-oss/sailpoint-cli/internal/util" "github.com/spf13/cobra" "gopkg.in/alessio/shellescape.v1" "gopkg.in/yaml.v2" + + connvalidate "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/validate" + "github.com/sailpoint-oss/sailpoint-cli/internal/client" + "github.com/sailpoint-oss/sailpoint-cli/internal/util" ) type Source struct { @@ -68,6 +70,7 @@ func newConnValidateSourcesCmd(apiClient client.Client) *cobra.Command { ctx := cmd.Context() endpoint := cmd.Flags().Lookup("conn-endpoint").Value.String() + isReadLimit, _ := strconv.ParseBool(cmd.Flags().Lookup("read-limit").Value.String()) listOfSources, err := getSourceFromFile(sourceFile) if err != nil { @@ -83,7 +86,7 @@ func newConnValidateSourcesCmd(apiClient client.Client) *cobra.Command { return err } - res, err := validateConnectors(ctx, apiClient, source, endpoint) + res, err := validateConnectors(ctx, apiClient, source, endpoint, isReadLimit) if err != nil { return err } @@ -132,7 +135,7 @@ func getSourceFromFile(filePath string) ([]Source, error) { return config, err } -func validateConnectors(ctx context.Context, apiClient client.Client, source Source, endpoint string) (*ValidationResults, error) { +func validateConnectors(ctx context.Context, apiClient client.Client, source Source, endpoint string, isReadLimit bool) (*ValidationResults, error) { resp, err := apiClient.Get(ctx, endpoint) if err != nil { return nil, err @@ -170,8 +173,9 @@ func validateConnectors(ctx context.Context, apiClient client.Client, source Sou } validator := connvalidate.NewValidator(connvalidate.Config{ - Check: "", - ReadOnly: source.ReadOnly, + Check: "", + ReadOnly: source.ReadOnly, + ReadLimit: isReadLimit, }, cc) results, err := validator.Run(ctx) diff --git a/cmd/connector/validate/account_create.go b/cmd/connector/validate/account_create.go index bddef71..d3319c0 100644 --- a/cmd/connector/validate/account_create.go +++ b/cmd/connector/validate/account_create.go @@ -5,6 +5,7 @@ import ( "time" "github.com/kr/pretty" + connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client" ) @@ -16,7 +17,7 @@ var accountCreateChecks = []Check{ RequiredCommands: []string{ "std:account:create", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { input := map[string]interface{}{} _, _, err := cc.AccountCreate(ctx, nil, input, nil) if err == nil { @@ -33,7 +34,7 @@ var accountCreateChecks = []Check{ "std:account:read", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { input := map[string]interface{}{} for _, field := range spec.AccountCreateTemplate.Fields { if field.Required { @@ -79,7 +80,7 @@ var accountCreateChecks = []Check{ "std:account:read", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { input := map[string]interface{}{} for _, field := range spec.AccountCreateTemplate.Fields { input[getFieldName(field)] = genCreateField(field) @@ -124,7 +125,7 @@ var accountCreateChecks = []Check{ "std:account:delete", "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { accountsPreCreate, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) diff --git a/cmd/connector/validate/account_read.go b/cmd/connector/validate/account_read.go index b27b8c6..c4cdc32 100644 --- a/cmd/connector/validate/account_read.go +++ b/cmd/connector/validate/account_read.go @@ -19,7 +19,7 @@ var accountReadChecks = []Check{ "std:account:read", "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { accounts, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) @@ -27,7 +27,7 @@ var accountReadChecks = []Check{ } count := 0 for _, account := range accounts { - if count > accountReadLimit { + if readLimit && count > accountReadLimit { break } acct, _, err := cc.AccountRead(ctx, account.ID(), account.UniqueID(), nil) @@ -59,7 +59,7 @@ var accountReadChecks = []Check{ RequiredCommands: []string{ "std:account:read", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { _, _, err := cc.AccountRead(ctx, "__sailpoint__not__found__", "", nil) if err == nil { res.errf("expected error for non-existant identity") @@ -73,7 +73,7 @@ var accountReadChecks = []Check{ RequiredCommands: []string{ "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { additionalAttributes := map[string]string{} attrsByName := map[string]connclient.AccountSchemaAttribute{} diff --git a/cmd/connector/validate/account_update.go b/cmd/connector/validate/account_update.go index d06c918..414b133 100644 --- a/cmd/connector/validate/account_update.go +++ b/cmd/connector/validate/account_update.go @@ -17,7 +17,7 @@ var accountUpdateChecks = []Check{ "std:account:list", "std:account:update", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { accounts, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) @@ -72,7 +72,7 @@ var accountUpdateChecks = []Check{ "std:account:update", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { entitlementAttr := entitlementAttr(spec) if entitlementAttr == "" { res.warnf("no entitlement attribute") diff --git a/cmd/connector/validate/check.go b/cmd/connector/validate/check.go index 10ebc74..fee388b 100644 --- a/cmd/connector/validate/check.go +++ b/cmd/connector/validate/check.go @@ -24,7 +24,7 @@ type Check struct { // IsDataModifier determines a checking that will modify connectors data after applying IsDataModifier bool - Run func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) + Run func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) // RequiredCommands represents a list of commands that use for this check RequiredCommands []string } diff --git a/cmd/connector/validate/entitlement_read.go b/cmd/connector/validate/entitlement_read.go index 08306d1..fbde182 100644 --- a/cmd/connector/validate/entitlement_read.go +++ b/cmd/connector/validate/entitlement_read.go @@ -16,7 +16,7 @@ var entitlementReadChecks = []Check{ RequiredCommands: []string{ "std:entitlement:read", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { _, _, err := cc.EntitlementRead(ctx, "__sailpoint__not__found__", "", "group", nil) if err == nil { res.errf("expected error for non-existant entitlement") @@ -32,7 +32,7 @@ var entitlementReadChecks = []Check{ "std:entitlement:read", "std:entitlement:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { entitlements, _, _, err := cc.EntitlementList(ctx, "group", nil, nil, nil) if err != nil { res.err(err) @@ -45,7 +45,7 @@ var entitlementReadChecks = []Check{ } count := 0 for _, e := range entitlements { - if count > accountReadLimit { + if readLimit && count > accountReadLimit { break } eRead, _, err := cc.EntitlementRead(ctx, e.ID(), e.UniqueID(), "group", nil) @@ -73,7 +73,7 @@ var entitlementReadChecks = []Check{ RequiredCommands: []string{ "std:entitlement:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { additionalAttributes := map[string]string{} attrsByName := map[string]connclient.EntitlementSchemaAttribute{} diff --git a/cmd/connector/validate/test_conn.go b/cmd/connector/validate/test_conn.go index f9bcddb..9dc57ae 100644 --- a/cmd/connector/validate/test_conn.go +++ b/cmd/connector/validate/test_conn.go @@ -15,7 +15,7 @@ var testConnChecks = []Check{ RequiredCommands: []string{ "std:test-connection", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { err := cc.TestConnectionWithConfig(ctx, json.RawMessage("{}")) if err == nil { res.errf("expected test-connection failure for empty config") @@ -29,7 +29,7 @@ var testConnChecks = []Check{ RequiredCommands: []string{ "std:test-connection", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { _, err := cc.TestConnection(ctx) if err != nil { res.err(err) diff --git a/cmd/connector/validate/validate.go b/cmd/connector/validate/validate.go index 78fac59..6a4c1f1 100644 --- a/cmd/connector/validate/validate.go +++ b/cmd/connector/validate/validate.go @@ -28,6 +28,10 @@ type Config struct { // ReadOnly specifies a type of validation. // If ReadOnly set 'true' validator will run all checks that don't make any modifications. ReadOnly bool + + // ReadLimit specifies whether to limit the number of account read + // If ReadLimit set 'true', check for account and entitlement read will only read 8 accounts + ReadLimit bool } // NewValidator creates a new validator with provided config and ConnClient @@ -62,7 +66,7 @@ func (v *Validator) Run(ctx context.Context) (results []CheckResult, err error) } if ok, results := isCheckPossible(spec.Commands, check.RequiredCommands); ok { - check.Run(ctx, spec, v.cc, res) + check.Run(ctx, spec, v.cc, res, v.cfg.ReadLimit) } else { res.skipf("Skipping check due to unimplemented commands on a connector: %s", strings.Join(results, ", ")) } From 1aa83191bc958ef0c5a9f8bac5b1f4072efe7e45 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Mon, 6 Nov 2023 09:54:59 -0600 Subject: [PATCH 3/7] PLTCONN-4069: Refactoring --- cmd/connector/validate/account_create.go | 8 ++++---- cmd/connector/validate/account_read.go | 19 ++++++++++++++----- cmd/connector/validate/account_update.go | 4 ++-- cmd/connector/validate/check.go | 2 +- cmd/connector/validate/entitlement_read.go | 16 +++++++++++----- cmd/connector/validate/test_conn.go | 4 ++-- cmd/connector/validate/util.go | 4 ---- cmd/connector/validate/validate.go | 2 +- 8 files changed, 35 insertions(+), 24 deletions(-) diff --git a/cmd/connector/validate/account_create.go b/cmd/connector/validate/account_create.go index d3319c0..8d5bc0b 100644 --- a/cmd/connector/validate/account_create.go +++ b/cmd/connector/validate/account_create.go @@ -17,7 +17,7 @@ var accountCreateChecks = []Check{ RequiredCommands: []string{ "std:account:create", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { input := map[string]interface{}{} _, _, err := cc.AccountCreate(ctx, nil, input, nil) if err == nil { @@ -34,7 +34,7 @@ var accountCreateChecks = []Check{ "std:account:read", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { input := map[string]interface{}{} for _, field := range spec.AccountCreateTemplate.Fields { if field.Required { @@ -80,7 +80,7 @@ var accountCreateChecks = []Check{ "std:account:read", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { input := map[string]interface{}{} for _, field := range spec.AccountCreateTemplate.Fields { input[getFieldName(field)] = genCreateField(field) @@ -125,7 +125,7 @@ var accountCreateChecks = []Check{ "std:account:delete", "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { accountsPreCreate, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) diff --git a/cmd/connector/validate/account_read.go b/cmd/connector/validate/account_read.go index c4cdc32..863adc7 100644 --- a/cmd/connector/validate/account_read.go +++ b/cmd/connector/validate/account_read.go @@ -3,6 +3,7 @@ package connvalidate import ( "context" "fmt" + "math/rand" "strconv" "github.com/kr/pretty" @@ -19,15 +20,23 @@ var accountReadChecks = []Check{ "std:account:read", "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { accounts, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) return } - count := 0 + if len(accounts) == 0 { + res.warnf("no entitlements") + return + } + + rand.Shuffle(len(accounts), func(i, j int) { + accounts[i], accounts[j] = accounts[j], accounts[i] + }) + count := int64(0) for _, account := range accounts { - if readLimit && count > accountReadLimit { + if count > readLimit { break } acct, _, err := cc.AccountRead(ctx, account.ID(), account.UniqueID(), nil) @@ -59,7 +68,7 @@ var accountReadChecks = []Check{ RequiredCommands: []string{ "std:account:read", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { _, _, err := cc.AccountRead(ctx, "__sailpoint__not__found__", "", nil) if err == nil { res.errf("expected error for non-existant identity") @@ -73,7 +82,7 @@ var accountReadChecks = []Check{ RequiredCommands: []string{ "std:account:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { additionalAttributes := map[string]string{} attrsByName := map[string]connclient.AccountSchemaAttribute{} diff --git a/cmd/connector/validate/account_update.go b/cmd/connector/validate/account_update.go index 414b133..a1d319c 100644 --- a/cmd/connector/validate/account_update.go +++ b/cmd/connector/validate/account_update.go @@ -17,7 +17,7 @@ var accountUpdateChecks = []Check{ "std:account:list", "std:account:update", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { accounts, _, _, err := cc.AccountList(ctx, nil, nil, nil) if err != nil { res.err(err) @@ -72,7 +72,7 @@ var accountUpdateChecks = []Check{ "std:account:update", "std:account:delete", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { entitlementAttr := entitlementAttr(spec) if entitlementAttr == "" { res.warnf("no entitlement attribute") diff --git a/cmd/connector/validate/check.go b/cmd/connector/validate/check.go index fee388b..6b5a658 100644 --- a/cmd/connector/validate/check.go +++ b/cmd/connector/validate/check.go @@ -24,7 +24,7 @@ type Check struct { // IsDataModifier determines a checking that will modify connectors data after applying IsDataModifier bool - Run func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) + Run func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) // RequiredCommands represents a list of commands that use for this check RequiredCommands []string } diff --git a/cmd/connector/validate/entitlement_read.go b/cmd/connector/validate/entitlement_read.go index fbde182..1dade4b 100644 --- a/cmd/connector/validate/entitlement_read.go +++ b/cmd/connector/validate/entitlement_read.go @@ -2,6 +2,7 @@ package connvalidate import ( "context" + "math/rand" "github.com/kr/pretty" @@ -16,7 +17,7 @@ var entitlementReadChecks = []Check{ RequiredCommands: []string{ "std:entitlement:read", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { _, _, err := cc.EntitlementRead(ctx, "__sailpoint__not__found__", "", "group", nil) if err == nil { res.errf("expected error for non-existant entitlement") @@ -32,7 +33,7 @@ var entitlementReadChecks = []Check{ "std:entitlement:read", "std:entitlement:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { entitlements, _, _, err := cc.EntitlementList(ctx, "group", nil, nil, nil) if err != nil { res.err(err) @@ -43,9 +44,14 @@ var entitlementReadChecks = []Check{ res.warnf("no entitlements") return } - count := 0 + + rand.Shuffle(len(entitlements), func(i, j int) { + entitlements[i], entitlements[j] = entitlements[j], entitlements[i] + }) + + count := int64(0) for _, e := range entitlements { - if readLimit && count > accountReadLimit { + if count > readLimit { break } eRead, _, err := cc.EntitlementRead(ctx, e.ID(), e.UniqueID(), "group", nil) @@ -73,7 +79,7 @@ var entitlementReadChecks = []Check{ RequiredCommands: []string{ "std:entitlement:list", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { additionalAttributes := map[string]string{} attrsByName := map[string]connclient.EntitlementSchemaAttribute{} diff --git a/cmd/connector/validate/test_conn.go b/cmd/connector/validate/test_conn.go index 9dc57ae..2433f70 100644 --- a/cmd/connector/validate/test_conn.go +++ b/cmd/connector/validate/test_conn.go @@ -15,7 +15,7 @@ var testConnChecks = []Check{ RequiredCommands: []string{ "std:test-connection", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { err := cc.TestConnectionWithConfig(ctx, json.RawMessage("{}")) if err == nil { res.errf("expected test-connection failure for empty config") @@ -29,7 +29,7 @@ var testConnChecks = []Check{ RequiredCommands: []string{ "std:test-connection", }, - Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit bool) { + Run: func(ctx context.Context, spec *connclient.ConnSpec, cc *connclient.ConnClient, res *CheckResult, readLimit int64) { _, err := cc.TestConnection(ctx) if err != nil { res.err(err) diff --git a/cmd/connector/validate/util.go b/cmd/connector/validate/util.go index 165f76d..adc91c9 100644 --- a/cmd/connector/validate/util.go +++ b/cmd/connector/validate/util.go @@ -12,10 +12,6 @@ import ( connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client" ) -const ( - accountReadLimit = 8 -) - // entitlementAttr returns the attribute for entitlements func entitlementAttr(spec *connclient.ConnSpec) string { for _, attr := range spec.AccountSchema.Attributes { diff --git a/cmd/connector/validate/validate.go b/cmd/connector/validate/validate.go index 6a4c1f1..5acebdc 100644 --- a/cmd/connector/validate/validate.go +++ b/cmd/connector/validate/validate.go @@ -31,7 +31,7 @@ type Config struct { // ReadLimit specifies whether to limit the number of account read // If ReadLimit set 'true', check for account and entitlement read will only read 8 accounts - ReadLimit bool + ReadLimit int64 } // NewValidator creates a new validator with provided config and ConnClient From d93ab01925e281df7a609f9bd4f13f43d878def4 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Mon, 6 Nov 2023 09:56:52 -0600 Subject: [PATCH 4/7] PLTCONN-4069: Refactoring --- cmd/connector/conn_validate.go | 28 ++++++++++++++++++++++++-- cmd/connector/conn_validate_sources.go | 18 ++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cmd/connector/conn_validate.go b/cmd/connector/conn_validate.go index d0fbe64..2b14436 100644 --- a/cmd/connector/conn_validate.go +++ b/cmd/connector/conn_validate.go @@ -14,6 +14,10 @@ import ( "github.com/sailpoint-oss/sailpoint-cli/internal/client" ) +const ( + accountReadLimit = 8 +) + func newConnValidateCmd(apiClient client.Client) *cobra.Command { cmd := &cobra.Command{ Use: "validate", @@ -44,11 +48,20 @@ func newConnValidateCmd(apiClient client.Client) *cobra.Command { check := cmd.Flags().Lookup("check").Value.String() isReadOnly, _ := strconv.ParseBool(cmd.Flags().Lookup("read-only").Value.String()) - isReadLimit, _ := strconv.ParseBool(cmd.Flags().Lookup("read-limit").Value.String()) + readLimit := cmd.Flags().Lookup("read-limit").Value.String() + readLimitVal := int64(accountReadLimit) + + if readLimit != "" { + readLimitVal, err = getReadLimitVal(readLimit) + if err != nil { + return fmt.Errorf("invalid value of readLimit: %v", err) + } + } + valid := connvalidate.NewValidator(connvalidate.Config{ Check: check, ReadOnly: isReadOnly, - ReadLimit: isReadLimit, + ReadLimit: readLimitVal, }, cc) results, err := valid.Run(ctx) @@ -101,3 +114,14 @@ func newConnValidateCmd(apiClient client.Client) *cobra.Command { return cmd } + +func getReadLimitVal(readLimit string) (int64, error) { + readLimitVal, err := strconv.ParseInt(readLimit, 10, 64) + if err != nil { + return 0, err + } + if readLimitVal <= 0 { + return 0, fmt.Errorf("readLimit value cannot be smaller than or equal to 0") + } + return readLimitVal, nil +} diff --git a/cmd/connector/conn_validate_sources.go b/cmd/connector/conn_validate_sources.go index 9ef63ca..f3d2cc2 100644 --- a/cmd/connector/conn_validate_sources.go +++ b/cmd/connector/conn_validate_sources.go @@ -11,7 +11,6 @@ import ( "net/http" "os" "os/exec" - "strconv" "strings" "syscall" "time" @@ -70,7 +69,16 @@ func newConnValidateSourcesCmd(apiClient client.Client) *cobra.Command { ctx := cmd.Context() endpoint := cmd.Flags().Lookup("conn-endpoint").Value.String() - isReadLimit, _ := strconv.ParseBool(cmd.Flags().Lookup("read-limit").Value.String()) + readLimit := cmd.Flags().Lookup("read-limit").Value.String() + readLimitVal := int64(accountReadLimit) + var err error + + if readLimit != "" { + readLimitVal, err = getReadLimitVal(readLimit) + if err != nil { + return fmt.Errorf("invalid value of readLimit: %v", err) + } + } listOfSources, err := getSourceFromFile(sourceFile) if err != nil { @@ -86,7 +94,7 @@ func newConnValidateSourcesCmd(apiClient client.Client) *cobra.Command { return err } - res, err := validateConnectors(ctx, apiClient, source, endpoint, isReadLimit) + res, err := validateConnectors(ctx, apiClient, source, endpoint, readLimitVal) if err != nil { return err } @@ -135,7 +143,7 @@ func getSourceFromFile(filePath string) ([]Source, error) { return config, err } -func validateConnectors(ctx context.Context, apiClient client.Client, source Source, endpoint string, isReadLimit bool) (*ValidationResults, error) { +func validateConnectors(ctx context.Context, apiClient client.Client, source Source, endpoint string, readLimit int64) (*ValidationResults, error) { resp, err := apiClient.Get(ctx, endpoint) if err != nil { return nil, err @@ -175,7 +183,7 @@ func validateConnectors(ctx context.Context, apiClient client.Client, source Sou validator := connvalidate.NewValidator(connvalidate.Config{ Check: "", ReadOnly: source.ReadOnly, - ReadLimit: isReadLimit, + ReadLimit: readLimit, }, cc) results, err := validator.Run(ctx) From 0193e1e329cff6e91feed1b432429ccaa38e5c31 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Tue, 7 Nov 2023 11:19:46 -0600 Subject: [PATCH 5/7] PLTCONN-4069: Refactoring --- cmd/connector/conn.go | 8 +++++--- cmd/connector/validate/account_read.go | 4 ++-- cmd/connector/validate/entitlement_read.go | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/connector/conn.go b/cmd/connector/conn.go index 18aa343..7150da3 100644 --- a/cmd/connector/conn.go +++ b/cmd/connector/conn.go @@ -7,12 +7,13 @@ import ( "log" "os" - "github.com/sailpoint-oss/sailpoint-cli/internal/client" - "github.com/sailpoint-oss/sailpoint-cli/internal/config" - "github.com/sailpoint-oss/sailpoint-cli/internal/terminal" "github.com/spf13/cobra" "github.com/spf13/pflag" "gopkg.in/yaml.v2" + + "github.com/sailpoint-oss/sailpoint-cli/internal/client" + "github.com/sailpoint-oss/sailpoint-cli/internal/config" + "github.com/sailpoint-oss/sailpoint-cli/internal/terminal" ) const ( @@ -40,6 +41,7 @@ func NewConnCmd(term terminal.Terminal) *cobra.Command { Client := client.NewSpClient(Config) conn.PersistentFlags().StringP("conn-endpoint", "e", connectorsEndpoint, "Override connectors endpoint") + conn.PersistentFlags().Int64("read-limit", accountReadLimit, "Set read limit for accounts and entitlements read") conn.AddCommand( newConnInitCommand(), diff --git a/cmd/connector/validate/account_read.go b/cmd/connector/validate/account_read.go index 863adc7..664c70f 100644 --- a/cmd/connector/validate/account_read.go +++ b/cmd/connector/validate/account_read.go @@ -27,7 +27,7 @@ var accountReadChecks = []Check{ return } if len(accounts) == 0 { - res.warnf("no entitlements") + res.warnf("no accounts") return } @@ -36,7 +36,7 @@ var accountReadChecks = []Check{ }) count := int64(0) for _, account := range accounts { - if count > readLimit { + if count == readLimit { break } acct, _, err := cc.AccountRead(ctx, account.ID(), account.UniqueID(), nil) diff --git a/cmd/connector/validate/entitlement_read.go b/cmd/connector/validate/entitlement_read.go index 1dade4b..6182ab5 100644 --- a/cmd/connector/validate/entitlement_read.go +++ b/cmd/connector/validate/entitlement_read.go @@ -51,7 +51,7 @@ var entitlementReadChecks = []Check{ count := int64(0) for _, e := range entitlements { - if count > readLimit { + if count == readLimit { break } eRead, _, err := cc.EntitlementRead(ctx, e.ID(), e.UniqueID(), "group", nil) From 78fad111cde41a34f36e79c40dcc687054ddede1 Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Tue, 7 Nov 2023 14:47:27 -0600 Subject: [PATCH 6/7] PLTCONN-4069: Refactoring --- cmd/connector/conn_validate.go | 15 +++++---------- cmd/connector/conn_validate_sources.go | 12 +++--------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/cmd/connector/conn_validate.go b/cmd/connector/conn_validate.go index 2b14436..6d51184 100644 --- a/cmd/connector/conn_validate.go +++ b/cmd/connector/conn_validate.go @@ -48,14 +48,9 @@ func newConnValidateCmd(apiClient client.Client) *cobra.Command { check := cmd.Flags().Lookup("check").Value.String() isReadOnly, _ := strconv.ParseBool(cmd.Flags().Lookup("read-only").Value.String()) - readLimit := cmd.Flags().Lookup("read-limit").Value.String() - readLimitVal := int64(accountReadLimit) - - if readLimit != "" { - readLimitVal, err = getReadLimitVal(readLimit) - if err != nil { - return fmt.Errorf("invalid value of readLimit: %v", err) - } + readLimitVal, err := getReadLimitVal(cmd) + if err != nil { + return fmt.Errorf("invalid value of readLimit: %v", err) } valid := connvalidate.NewValidator(connvalidate.Config{ @@ -115,8 +110,8 @@ func newConnValidateCmd(apiClient client.Client) *cobra.Command { return cmd } -func getReadLimitVal(readLimit string) (int64, error) { - readLimitVal, err := strconv.ParseInt(readLimit, 10, 64) +func getReadLimitVal(cmd *cobra.Command) (int64, error) { + readLimitVal, err := cmd.Flags().GetInt64("read-limit") if err != nil { return 0, err } diff --git a/cmd/connector/conn_validate_sources.go b/cmd/connector/conn_validate_sources.go index f3d2cc2..f5e79dc 100644 --- a/cmd/connector/conn_validate_sources.go +++ b/cmd/connector/conn_validate_sources.go @@ -69,15 +69,9 @@ func newConnValidateSourcesCmd(apiClient client.Client) *cobra.Command { ctx := cmd.Context() endpoint := cmd.Flags().Lookup("conn-endpoint").Value.String() - readLimit := cmd.Flags().Lookup("read-limit").Value.String() - readLimitVal := int64(accountReadLimit) - var err error - - if readLimit != "" { - readLimitVal, err = getReadLimitVal(readLimit) - if err != nil { - return fmt.Errorf("invalid value of readLimit: %v", err) - } + readLimitVal, err := getReadLimitVal(cmd) + if err != nil { + return fmt.Errorf("invalid value of readLimit: %v", err) } listOfSources, err := getSourceFromFile(sourceFile) From 38a71b3924c65db4f360d81dab55495dc74af9ef Mon Sep 17 00:00:00 2001 From: Megan Tran Date: Tue, 7 Nov 2023 14:56:25 -0600 Subject: [PATCH 7/7] PLTCONN-4069: Refactoring --- cmd/connector/validate/account_read.go | 7 +++---- cmd/connector/validate/entitlement_read.go | 6 ++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cmd/connector/validate/account_read.go b/cmd/connector/validate/account_read.go index 664c70f..84424d9 100644 --- a/cmd/connector/validate/account_read.go +++ b/cmd/connector/validate/account_read.go @@ -34,9 +34,9 @@ var accountReadChecks = []Check{ rand.Shuffle(len(accounts), func(i, j int) { accounts[i], accounts[j] = accounts[j], accounts[i] }) - count := int64(0) - for _, account := range accounts { - if count == readLimit { + + for index, account := range accounts { + if int64(index) == readLimit { break } acct, _, err := cc.AccountRead(ctx, account.ID(), account.UniqueID(), nil) @@ -44,7 +44,6 @@ var accountReadChecks = []Check{ res.err(err) return } - count++ if acct.Identity != account.Identity { res.errf("want %q; got %q", account.Identity, acct.Identity) } diff --git a/cmd/connector/validate/entitlement_read.go b/cmd/connector/validate/entitlement_read.go index 6182ab5..ca2cb06 100644 --- a/cmd/connector/validate/entitlement_read.go +++ b/cmd/connector/validate/entitlement_read.go @@ -49,9 +49,8 @@ var entitlementReadChecks = []Check{ entitlements[i], entitlements[j] = entitlements[j], entitlements[i] }) - count := int64(0) - for _, e := range entitlements { - if count == readLimit { + for index, e := range entitlements { + if int64(index) == readLimit { break } eRead, _, err := cc.EntitlementRead(ctx, e.ID(), e.UniqueID(), "group", nil) @@ -59,7 +58,6 @@ var entitlementReadChecks = []Check{ res.errf("failed to read entitlement %q: %s", e.Identity, err.Error()) return } - count++ if e.Identity != eRead.Identity { res.errf("want %q; got %q", e.Identity, eRead.Identity) }