From 7edfaa0647c6021faf898b17cab42ee916e2978e Mon Sep 17 00:00:00 2001 From: thycotic-rd Date: Tue, 25 Oct 2022 09:17:42 +0000 Subject: [PATCH] Automated from: 9af0663605f10067bcfa1bce91fd13474df3a2bd --- README.md | 6 +- auth/auth.go | 3 +- cicd-integration/integration_test.go | 2 +- commands/audit.go | 6 +- commands/auth.go | 16 +- commands/auth_provider.go | 200 ++++++------- commands/base.go | 280 ++++++++---------- commands/base_test.go | 96 +----- commands/breakglass.go | 50 ++-- commands/cli-config.go | 31 +- commands/cli-config_test.go | 5 + commands/client.go | 38 +-- commands/config.go | 16 +- commands/config_test.go | 34 ++- commands/encryption_auto.go | 33 +-- commands/encryption_manual.go | 33 +-- commands/encryption_manual_test.go | 35 +++ commands/engine.go | 27 +- commands/group.go | 36 +-- commands/home.go | 56 ++-- commands/home_test.go | 45 +++ commands/misc.go | 8 +- commands/pki.go | 47 +-- commands/policy.go | 48 +-- commands/policy_test.go | 2 +- commands/pool.go | 40 ++- commands/report.go | 15 +- commands/report_test.go | 21 +- commands/role.go | 38 +-- commands/secret.go | 65 ++-- commands/siem.go | 44 ++- commands/siem_test.go | 5 + commands/usage.go | 4 +- commands/usage_test.go | 8 +- commands/user.go | 38 +-- constants/commands.go | 1 + constants/config.go | 12 +- constants/doc.go | 2 - .../predictor/auth_provider_type_predictor.go | 11 + internal/predictor/wrappers.go | 37 +-- main.go | 27 +- utils/errors.go | 15 - vaultcli/args.go | 7 +- vaultcli/config.go | 36 --- vaultcli/viper.go | 46 +++ 45 files changed, 679 insertions(+), 946 deletions(-) create mode 100644 internal/predictor/auth_provider_type_predictor.go create mode 100644 vaultcli/viper.go diff --git a/README.md b/README.md index 614a80fa..275382bf 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,13 @@ An automation tool for the management of credentials for applications, databases, CI/CD tools, and services. +## Installation + +Prebuilt binaries for Linux, macOS and Windows can be downloaded from https://dsv.secretsvaultcloud.com/downloads. + ## Documentation -The documentation is available at https://docs.delinea.com/dsv/current +The documentation is available at https://docs.delinea.com/dsv/current. ## Development environment setup diff --git a/auth/auth.go b/auth/auth.go index 0007cb15..5cdc1963 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -13,7 +13,6 @@ import ( "thy/paths" "thy/requests" "thy/store" - "thy/utils" "github.com/spf13/viper" ) @@ -315,7 +314,7 @@ func (r *requestBody) validate(at AuthType) error { for _, k := range paramSpecDict[at] { f := ref.FieldByName(k.PropName) if f.String() == "" { - return utils.NewMissingArgError(k.ArgName) + return errors.NewF("--%s must be set", k.ArgName) } } return nil diff --git a/cicd-integration/integration_test.go b/cicd-integration/integration_test.go index 62b507a9..fc4a7b72 100644 --- a/cicd-integration/integration_test.go +++ b/cicd-integration/integration_test.go @@ -380,7 +380,7 @@ func init() { }, {"sign-with-root-cert", []string{"pki", "sign", "--rootcapath", existingRootSecret, - "--csrpath", "@" + csrPath, "--maxttl", "100H", + "--csrpath", "@" + csrPath, "--ttl", "100H", }, outputPattern("certificate"), }, diff --git a/commands/audit.go b/commands/audit.go index 049e2edf..f0f24c2b 100644 --- a/commands/audit.go +++ b/commands/audit.go @@ -29,7 +29,7 @@ Usage: FlagsPredictor: []*predictor.Params{ {Name: cst.StartDate, Shorthand: "s", Usage: "Start date from which to fetch audit data (required)"}, {Name: cst.EndDate, Usage: "End date to which to fetch audit data (optional)"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, {Name: cst.Path, Usage: "Path (optional)"}, {Name: cst.NounPrincipal, Usage: "Principal name (optional)"}, @@ -37,9 +37,7 @@ Usage: {Name: cst.Sort, Usage: "Change result sorting order (asc|desc) [default: desc] when search field is specified (optional)"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuditSearch(vcli, args) - }, + RunFunc: handleAuditSearch, }) } diff --git a/commands/auth.go b/commands/auth.go index db775570..23b27c39 100644 --- a/commands/auth.go +++ b/commands/auth.go @@ -32,9 +32,7 @@ Usage: • auth --auth-username %[3]s --auth-password %[4]s • auth --auth-type %[5]s --auth-client-id %[6]s --domain %[7]s --auth-client-secret %[8]s `, cst.NounAuth, cst.ProductName, cst.ExampleUser, cst.ExamplePassword, cst.ExampleAuthType, cst.ExampleAuthClientID, cst.ExampleDomain, cst.ExampleAuthClientSecret, string(auth.FederatedAws)), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuth(vcli, args) - }, + RunFunc: handleAuth, }) } @@ -48,9 +46,7 @@ Usage: • auth clear `, cst.NounAuth, cst.ProductName, cst.NounToken), NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthClear(vcli, args) - }, + RunFunc: handleAuthClear, }) } @@ -64,9 +60,7 @@ Usage: • auth list `, cst.NounAuth, cst.ProductName, cst.NounToken), NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthList(vcli, args) - }, + RunFunc: handleAuthList, }) } @@ -78,9 +72,7 @@ func GetAuthChangePasswordCmd() (cli.Command, error) { Usage: • auth change-password`, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthChangePassword(vcli, args) - }, + RunFunc: handleAuthChangePassword, }) } diff --git a/commands/auth_provider.go b/commands/auth_provider.go index 82f64e0e..1da0c05b 100644 --- a/commands/auth_provider.go +++ b/commands/auth_provider.go @@ -48,20 +48,18 @@ func GetAuthProviderReadCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Read}, SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n)", cst.NounConfig, cst.NounAuthProvider, cst.Read), - HelpText: fmt.Sprintf(`Read a %[1]s + HelpText: `Read an authentication provider Usage: - • %[1]s %[2]s %[4]s %[3]s - • %[1]s %[2]s %[4]s --name %[3]s -`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Read), + • config auth-provider read aws-dev + • config auth-provider read --name aws-dev +`, FlagsPredictor: []*predictor.Params{ {Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}, {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderReadCmd(vcli, args) - }, + RunFunc: handleAuthProviderReadCmd, }) } @@ -69,20 +67,18 @@ func GetAuthProviderDeleteCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Delete}, SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n)", cst.Config, cst.NounAuthProvider, cst.Delete), - HelpText: fmt.Sprintf(`Delete %[1]s + HelpText: `Delete an authentication provider Usage: - • %[1]s %[2]s %[3]s %[4]s --all - • %[1]s %[2]s %[3]s --name %[4]s --force -`, cst.NounConfig, cst.NounAuthProvider, cst.Delete, cst.ExampleAuthProviderName), + • config auth-provider delete aws-dev + • config auth-provider delete --name aws-dev --force +`, FlagsPredictor: []*predictor.Params{ {Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}, {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounAuthProvider), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderDeleteCmd(vcli, args) - }, + RunFunc: handleAuthProviderDeleteCmd, }) } @@ -90,39 +86,38 @@ func GetAuthProviderRestoreCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Restore}, SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n)", cst.Config, cst.NounAuthProvider, cst.Restore), - HelpText: fmt.Sprintf(`Restore %[1]s + HelpText: `Restore an authentication provider Usage: - • %[1]s %[2]s %[3]s %[4]s - • %[1]s %[2]s %[3]s --name %[4]s -`, cst.NounConfig, cst.NounAuthProvider, cst.Restore, cst.ExampleAuthProviderName), + • config auth-provider restore aws-dev + • config auth-provider restore --name aws-dev +`, FlagsPredictor: []*predictor.Params{ {Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderRestoreCmd(vcli, args) - }, + RunFunc: handleAuthProviderRestoreCmd, }) } func GetAuthProviderCreateCmd() (cli.Command, error) { return NewCommand(CommandArgs{ - Path: []string{cst.NounAuthProvider, cst.Create}, - SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n) (--type) ((--data|-d) | --aws-account-id | --azure-tenant-id | --gcp-project-id | --root-ca-path | --assumed-role)", cst.NounConfig, cst.NounAuthProvider, cst.Create), - HelpText: fmt.Sprintf(`Add %[1]s provider + Path: []string{cst.Config, cst.NounAuthProvider, cst.Create}, + SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n) (--type) ((--data|-d) | --aws-account-id | --azure-tenant-id | --gcp-project-id)", cst.NounConfig, cst.NounAuthProvider, cst.Create), + HelpText: `Add an authentication provider Usage: - • %[1]s %[2]s %[4]s %[3]s --aws-account-id 11652944433808 --type aws - • %[1]s %[2]s %[4]s --name azure-prod --azure-tenant-id 164543 --type azure - • %[1]s %[2]s %[4]s --name GCP-prod --gcp-project-id test-proj --type gcp - • %[1]s %[2]s %[4]s --data %[5]s - - %[6]s -`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Create, cst.ExampleDataPath, cst.GCPNote), + • config auth-provider create aws-dev --aws-account-id 11652944433808 --type aws + • config auth-provider create --name azure-prod --azure-tenant-id 164543 --type azure + • config auth-provider create --name GCP-prod --gcp-project-id test-proj --type gcp + • config auth-provider create --data @/tmp/data.json + +GCP GCE metadata auth provider can be created in the command line, but a GCP Service Account must be done using a file. +See the Authentication:GCP portion of the documentation. +`, FlagsPredictor: []*predictor.Params{ {Name: cst.Data, Shorthand: "d", Usage: fmt.Sprintf("%s to be stored in an auth provider. Prefix with '@' to denote filepath", strings.Title(cst.Data)), Predictor: predictor.NewPrefixFilePredictor("*")}, - {Name: cst.DataType, Usage: "Auth provider type (azure,aws,gcp,thycoticone)", Predictor: predictor.AuthTypePredictor{}}, + {Name: cst.DataType, Usage: "Auth provider type (azure,aws,gcp,thycoticone)", Predictor: predictor.AuthProviderTypePredictor{}}, {Name: cst.DataName, Shorthand: "n", Usage: "Auth provider friendly name"}, {Name: cst.DataTenantID, Usage: "Azure Tenant ID"}, {Name: cst.DataAccountID, Usage: "AWS Account ID"}, @@ -132,30 +127,26 @@ Usage: {Name: cst.ThyOneAuthClientSecret, Usage: "Thycotic One client secret"}, {Name: cst.SendWelcomeEmail, Usage: "Whether to send welcome email for thycotic-one users linked to the auth provider (true or false)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleAuthProviderCreateWizard(vcli) - } - return handleAuthProviderCreate(vcli, args) - }, + RunFunc: handleAuthProviderCreate, + WizardFunc: handleAuthProviderCreateWizard, }) } func GetAuthProviderUpdateCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Update}, - SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n) (--type) ((--data|-d) | --aws-account-id | --azure-tenant-id | --gcp-project-id | --root-ca-path | --assumed-role)", cst.NounConfig, cst.NounAuthProvider, cst.Update), - HelpText: fmt.Sprintf(`Update %[1]s properties + SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n) (--type) ((--data|-d) | --aws-account-id | --azure-tenant-id | --gcp-project-id)", cst.NounConfig, cst.NounAuthProvider, cst.Update), + HelpText: `Update an authentication provider Usage: - • %[1]s %[2]s %[4]s %[3]s --aws-account-id 11652944433808 --type aws - • %[1]s %[2]s %[4]s --name azure-prod --azure-tenant-id 164543 --type azure - • %[1]s %[2]s %[4]s --name GCP-prod --gcp-project-id test-proj --type gcp - • %[1]s %[2]s %[4]s --data %[5]s -`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Update, cst.ExampleDataPath), + • config auth-provider update aws-dev --aws-account-id 11652944433808 --type aws + • config auth-provider update --name azure-prod --azure-tenant-id 164543 --type azure + • config auth-provider update --name GCP-prod --gcp-project-id test-proj --type gcp + • config auth-provider update --data @/tmp/data.json +`, FlagsPredictor: []*predictor.Params{ {Name: cst.Data, Shorthand: "d", Usage: fmt.Sprintf("%s to be stored in an auth provider. Prefix with '@' to denote filepath", strings.Title(cst.Data)), Predictor: predictor.NewPrefixFilePredictor("*")}, - {Name: cst.DataType, Usage: "Auth provider type (azure,aws,gcp,thycoticone)", Predictor: predictor.AuthTypePredictor{}}, + {Name: cst.DataType, Usage: "Auth provider type (azure,aws,gcp,thycoticone)", Predictor: predictor.AuthProviderTypePredictor{}}, {Name: cst.DataName, Shorthand: "n", Usage: "Auth provider friendly name"}, {Name: cst.DataTenantID, Usage: "Azure Tenant ID"}, {Name: cst.DataAccountID, Usage: "AWS Account ID"}, @@ -165,12 +156,8 @@ Usage: {Name: cst.ThyOneAuthClientSecret, Usage: "Thycotic One client secret"}, {Name: cst.SendWelcomeEmail, Usage: "Whether to send welcome email for thycotic-one users linked to the auth provider (true or false)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleAuthProviderUpdateWizard(vcli) - } - return handleAuthProviderUpdate(vcli, args) - }, + RunFunc: handleAuthProviderUpdate, + WizardFunc: handleAuthProviderUpdateWizard, }) } @@ -178,19 +165,17 @@ func GetAuthProviderEditCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Edit}, SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n)", cst.NounConfig, cst.NounAuthProvider, cst.Edit), - HelpText: fmt.Sprintf(`Edit an auth provider + HelpText: `Edit an authentication provider Usage: - • %[1]s %[2]s %[4]s %[3]s - • %[1]s %[2]s %[4]s --name %[3]s -`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Edit), + • config auth-provider edit aws-dev + • config auth-provider edit --name aws-dev +`, FlagsPredictor: []*predictor.Params{ {Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderEdit(vcli, args) - }, + RunFunc: handleAuthProviderEdit, }) } @@ -198,20 +183,18 @@ func GetAuthProviderRollbackCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Rollback}, SynopsisText: fmt.Sprintf("%s %s %s ( | --name|-n)", cst.NounConfig, cst.NounAuthProvider, cst.Rollback), - HelpText: fmt.Sprintf(`Rollback %[1]s properties + HelpText: `Rollback an authentication provider Usage: - • %[1]s %[2]s %[4]s %[3]s - • %[1]s %[2]s %[4]s --version %[5]s 1 -`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Rollback, cst.Version), + • config auth-provider rollback aws-dev + • config auth-provider rollback --name aws-dev --version 1 +`, FlagsPredictor: []*predictor.Params{ {Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}, {Name: cst.Version, Usage: "The version to which to rollback"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderRollbackCmd(vcli, args) - }, + RunFunc: handleAuthProviderRollbackCmd, }) } @@ -219,64 +202,61 @@ func GetAuthProviderSearchCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.Config, cst.NounAuthProvider, cst.Search}, SynopsisText: fmt.Sprintf("%s %s %s ( | --query)", cst.NounConfig, cst.NounAuthProvider, cst.Search), - HelpText: fmt.Sprintf(`Search for a %[1]s + HelpText: `Search for an authentication provider Usage: - • %[1]s %[2]s %[3]s %[4]s - • %[1]s %[2]s %[3]s --query %[4]s -`, cst.NounConfig, cst.NounAuthProvider, cst.Search, cst.ExampleAuthProviderName), + • config auth-provider search aws-dev + • config auth-provider search --query aws-dev +`, FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("Filter %s of items to fetch (required)", strings.Title(cst.Query))}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - MinNumberArgs: 0, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAuthProviderSearchCmd(vcli, args) - }, + RunFunc: handleAuthProviderSearchCmd, }) } func handleAuthProviderReadCmd(vcli vaultcli.CLI, args []string) int { - path, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } - path = paths.ProcessResource(path) + name = paths.ProcessResource(name) ver := viper.GetString(cst.Version) if strings.TrimSpace(ver) != "" { - path = fmt.Sprint(path, "/", cst.Version, "/", ver) + name = fmt.Sprint(name, "/", cst.Version, "/", ver) } - data, apiErr := authProviderRead(vcli, path) + data, apiErr := authProviderRead(vcli, name) vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) } func handleAuthProviderDeleteCmd(vcli vaultcli.CLI, args []string) int { - path, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } - data, apiErr := authProviderDelete(vcli, path, viper.GetBool(cst.Force)) + data, apiErr := authProviderDelete(vcli, name, viper.GetBool(cst.Force)) vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) } func handleAuthProviderRestoreCmd(vcli vaultcli.CLI, args []string) int { - path, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } - data, apiErr := authProviderRestore(vcli, path) + data, apiErr := authProviderRestore(vcli, name) vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) } func handleAuthProviderCreate(vcli vaultcli.CLI, args []string) int { - name, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } var model authProviderCreateRequest @@ -315,9 +295,9 @@ func handleAuthProviderCreate(vcli vaultcli.CLI, args []string) int { } func handleAuthProviderUpdate(vcli vaultcli.CLI, args []string) int { - name, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } var model authProviderUpdateRequest @@ -355,16 +335,16 @@ func handleAuthProviderUpdate(vcli vaultcli.CLI, args []string) int { } func handleAuthProviderRollbackCmd(vcli vaultcli.CLI, args []string) int { - path, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } version := viper.GetString(cst.Version) // If version is not provided, get the current auth-provider item and parse the version from it. // Submit a request for a version that's previous relative to the one found. if version == "" { - data, apiErr := authProviderRead(vcli, path) + data, apiErr := authProviderRead(vcli, name) if apiErr != nil { vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) @@ -378,7 +358,7 @@ func handleAuthProviderRollbackCmd(vcli vaultcli.CLI, args []string) int { version = v } - data, apiErr := authProviderRollback(vcli, path, version) + data, apiErr := authProviderRollback(vcli, name, version) vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) } @@ -398,12 +378,12 @@ func handleAuthProviderSearchCmd(vcli vaultcli.CLI, args []string) int { } func handleAuthProviderEdit(vcli vaultcli.CLI, args []string) int { - path, status := getAuthProviderParams(args) - if status != 0 { - return status + name := getAuthProviderName(args) + if name == "" { + return cli.RunResultHelp } - data, apiErr := authProviderRead(vcli, paths.ProcessResource(path)) + data, apiErr := authProviderRead(vcli, paths.ProcessResource(name)) if apiErr != nil { vcli.Out().WriteResponse(data, apiErr) return utils.GetExecStatus(apiErr) @@ -414,7 +394,7 @@ func handleAuthProviderEdit(vcli vaultcli.CLI, args []string) int { if mErr := json.Unmarshal(data, &model); mErr != nil { return nil, errors.New(mErr).Grow("invalid format for auth provider") } - _, apiErr := authProviderUpdate(vcli, paths.ProcessResource(path), &model) + _, apiErr := authProviderUpdate(vcli, paths.ProcessResource(name), &model) return nil, apiErr } @@ -639,16 +619,12 @@ func authProviderThycoticOneWizard() (*AuthProviderProperties, error) { // Helpers: -func getAuthProviderParams(args []string) (name string, status int) { - status = 0 - name = viper.GetString(cst.DataName) +func getAuthProviderName(args []string) string { + name := viper.GetString(cst.DataName) if name == "" && len(args) > 0 && !strings.HasPrefix(args[0], "-") { name = args[0] } - if name == "" { - status = cli.RunResultHelp - } - return name, status + return name } type authProvider struct { diff --git a/commands/base.go b/commands/base.go index e4512bde..ed43e14c 100644 --- a/commands/base.go +++ b/commands/base.go @@ -2,6 +2,7 @@ package cmd import ( "bytes" + stderrors "errors" "fmt" "io" "log" @@ -16,7 +17,6 @@ import ( "thy/auth" cst "thy/constants" "thy/errors" - "thy/format" "thy/internal/predictor" "thy/utils" "thy/vaultcli" @@ -34,7 +34,7 @@ func BasePredictorWrappers() []*predictor.Params { homePath = "%USERPROFILE%" } return []*predictor.Params{ - {Name: cst.Callback, Usage: fmt.Sprintf("Callback URL for oidc authentication [default: %s", cst.DefaultCallback), Global: true, Hidden: true}, + {Name: cst.Callback, Usage: fmt.Sprintf("Callback URL for oidc authentication [default: %s]", cst.DefaultCallback), Global: true, Hidden: true}, {Name: cst.AuthProvider, Usage: "Authentication provider name for federated authentication", Global: true, Hidden: true}, {Name: cst.Profile, Usage: "Configuration Profile [default:default]", Global: true}, {Name: cst.Tenant, Shorthand: "t", Usage: "Tenant used for auth", Global: true}, @@ -61,72 +61,107 @@ func BasePredictorWrappers() []*predictor.Params { } type CommandArgs struct { - Path []string - RunFunc func(vcli vaultcli.CLI, args []string) int - HelpText string - SynopsisText string - ArgsPredictorFunc func(complete.Args) []string - FlagsPredictor []*predictor.Params - NoPreAuth bool - MinNumberArgs int + Path []string + RunFunc func(vcli vaultcli.CLI, args []string) int + WizardFunc func(vcli vaultcli.CLI) int + HelpText string + SynopsisText string + ArgsPredictor complete.Predictor + FlagsPredictor []*predictor.Params + NoConfigRead bool + NoPreAuth bool + MinNumberArgs int } -// NewCommand creates new baseCommand func NewCommand(args CommandArgs) (cli.Command, error) { if len(args.Path) == 0 { - return nil, utils.NewMissingArgError(cst.Path) + return nil, fmt.Errorf("command path must be defined") } + + runFunc := args.RunFunc + if runFunc == nil { + // Show help by default. + runFunc = func(vcli vaultcli.CLI, args []string) int { return cli.RunResultHelp } + } + cmd := &baseCommand{ - path: args.Path, - runFunc: args.RunFunc, - helpText: args.HelpText, - synopsisText: args.SynopsisText, - noPreAuth: args.NoPreAuth, - minNumberArgs: args.MinNumberArgs, + path: args.Path, + runFunc: runFunc, + wizardFunc: args.WizardFunc, + helpText: args.HelpText, + synopsisText: args.SynopsisText, + noConfigRead: args.NoConfigRead, + noPreAuth: args.NoPreAuth, + minNumberArgs: args.MinNumberArgs, + argsPredictor: args.ArgsPredictor, + flagsPredictor: make(map[string]*predictor.Wrapper), } - cmd.flagsPredictor = make(map[string]*predictor.Wrapper) for _, v := range BasePredictorWrappers() { w := predictor.New(v) - cmd.flagsPredictor["--"+w.FriendlyName] = w + cmd.flagsPredictor[w.FriendlyName] = w } for _, v := range args.FlagsPredictor { w := predictor.New(v) - cmd.flagsPredictor["--"+w.FriendlyName] = w + cmd.flagsPredictor[w.FriendlyName] = w } - if args.ArgsPredictorFunc != nil { - cmd.argsPredictorFunc = func() complete.Predictor { - return complete.PredictFunc(args.ArgsPredictorFunc) - } - } return cmd, nil } type baseCommand struct { - path []string - runFunc func(vcli vaultcli.CLI, args []string) int - helpText string - synopsisText string - argsPredictorFunc func() complete.Predictor - flagsPredictor map[string]*predictor.Wrapper - noPreAuth bool - minNumberArgs int + path []string + runFunc func(vcli vaultcli.CLI, args []string) int + wizardFunc func(vcli vaultcli.CLI) int + helpText string + synopsisText string + argsPredictor complete.Predictor + flagsPredictor map[string]*predictor.Wrapper + noConfigRead bool + noPreAuth bool + minNumberArgs int } -func (c *baseCommand) preRun(args []string) int { +// Synopsis satisfies cli.Command interface. +func (c *baseCommand) Synopsis() string { return c.synopsisText } + +// Run satisfies cli.Command interface. +func (c *baseCommand) Run(args []string) int { if len(args) < c.minNumberArgs { return cli.RunResultHelp } - c.SetFlags() - setVerbosity() + onlyGlobalFlags, err := c.parseFlags() + if err != nil { + fmt.Fprintf(os.Stderr, "Flags error: %v.\n", err) + fmt.Fprintf(os.Stderr, "See %s %s --help.\n", os.Args[0], strings.Join(c.path, " ")) + return 1 + } - if viper.GetBool(cst.Verbose) { - log.Printf("DSV CLI version %s", version.Version) - log.Printf("\t- platform: %s/%s", runtime.GOOS, runtime.GOARCH) - log.Printf("\t- gitCommit: %s", version.GitCommit) - log.Printf("\t- buildDate: %s", version.GetBuildDate()) + doVerbose := viper.GetBool(cst.Verbose) + + if doVerbose { + log.SetOutput(os.Stderr) + + fmt.Fprintf(os.Stderr, "DSV CLI version %s\n", version.Version) + fmt.Fprintf(os.Stderr, "\t- platform: %s/%s\n", runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(os.Stderr, "\t- gitCommit: %s\n", version.GitCommit) + fmt.Fprintf(os.Stderr, "\t- buildDate: %s\n\n", version.GetBuildDate()) + } + + vcli := vaultcli.New() + + if !c.noConfigRead { + err := vaultcli.ViperInit() + // If tenant is set then probably it is ok to run without configuration file. + if err != nil && viper.GetString(cst.Tenant) == "" { + if stderrors.Is(err, vaultcli.ErrFileNotFound) { + vcli.Out().FailS("Run 'dsv init' to initiate CLI configuration - cannot find config.") + } else { + vcli.Out().FailF("Error: %v.", err) + } + return 1 + } } encoding := viper.GetString(cst.Encoding) @@ -146,71 +181,74 @@ func (c *baseCommand) preRun(args []string) int { } else if upd != nil { log.SetOutput(os.Stderr) log.Println(upd) - setVerbosity() + + if !doVerbose { + log.SetOutput(io.Discard) + } } if !c.noPreAuth { - tokenResponse, err := auth.NewAuthenticatorDefault().GetToken() + tokenResponse, err := vcli.Authenticator().GetToken() if err != nil || tokenResponse == nil || tokenResponse.Token == "" { - format.NewDefaultOutClient().WriteResponse(nil, err) - os.Exit(1) + vcli.Out().WriteResponse(nil, err) + return 1 } viper.Set("token", tokenResponse.Token) } - return 0 -} -func setVerbosity() { - if viper.GetBool(cst.Verbose) { - log.SetOutput(os.Stderr) - } else { - log.SetOutput(io.Discard) + if onlyGlobalFlags && c.wizardFunc != nil { + return c.wizardFunc(vcli) } + return c.runFunc(vcli, args) } -func (c *baseCommand) SetFlags() { - flag.Parse() +func (c *baseCommand) parseFlags() (bool, error) { + // Return an error when parsing flags, so it can be handled outside of spf13/pflag. + flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) + err := flag.CommandLine.Parse(os.Args[1:]) + if err != nil { + return false, err + } + + onlyGlobals := true - for _, e := range c.flagsPredictor { - v := e.Val - if v.Name == "" { + for _, flg := range c.flagsPredictor { + if flg.Name == "" { continue } - viperVal := viper.Get(v.Name) - if v.String() != "" || (viperVal == "" && v.DefaultValue != "") { - val := v.String() - if val == "" { - val = v.DefaultValue + + v := flg.Val + viperVal := viper.Get(flg.Name) + flagVal := flg.Val.String() + + if flagVal != "" && !flg.Global { + onlyGlobals = false + } + + if flagVal != "" || (viperVal == "" && v.DefaultValue != "") { + if flagVal == "" { + flagVal = flg.Val.DefaultValue } if v.Type() == "bool" { - if b, err := strconv.ParseBool(val); err == nil { - viper.Set(v.Name, b) + if b, err := strconv.ParseBool(flagVal); err == nil { + viper.Set(flg.Name, b) } } else { - viper.Set(v.Name, val) + viper.Set(flg.Name, flagVal) } // HACK: There should be a better way to tell authenticator not to read from // cache and not to save to cache. This hack uses viper as a global storage // and passes configuration to authenticator through it. // This hack helps to skip cache when global auth related flag used. - if e.Global && strings.HasPrefix(v.Name, cst.NounAuth) { + if flg.Global && strings.HasPrefix(flg.Name, cst.NounAuth) { viper.Set(cst.AuthSkipCache, true) } } } -} - -// Run satisfies cli.Command interface -func (c *baseCommand) Run(args []string) int { - sig := c.preRun(args) - if sig != 0 { - return sig - } - vcli := vaultcli.New() - return c.runFunc(vcli, args) + return onlyGlobals, nil } // Help satisfies cli.Command interface. @@ -271,105 +309,39 @@ Global:{{ range $value := .FlagsGlobal }} return b.String() } -// Synopsis satisfies cli.Command interface -func (c *baseCommand) Synopsis() string { - return c.synopsisText -} - // AutocompleteFlags satisfies cli.CommandAutocomplete interface func (c *baseCommand) AutocompleteFlags() complete.Flags { if c.flagsPredictor == nil { return nil } - // TODO : THIS IS INEFFICENT. Need to change complete.command.go nil checks becuase they think that - // a derived type is not nil (complete.PredictNothing) when they are nil + // Ignore error since not all autocomplete funcs require config. + _ = vaultcli.ViperInit() + flags := complete.Flags{} for k, v := range c.flagsPredictor { - if !cst.DontAutocompleteGlobals || !v.Global { - flags[k] = v.Predictor - } + flags["--"+k] = v.Predictor } return flags } // AutocompleteArgs satisfies cli.CommandAutocomplete interface func (c *baseCommand) AutocompleteArgs() complete.Predictor { - if c.argsPredictorFunc == nil { + if c.argsPredictor == nil { return nil } - return c.argsPredictorFunc() + + // Ignore error since not all autocomplete funcs require config. + _ = vaultcli.ViperInit() + + return c.argsPredictor } func ValidateParams(params map[string]string, requiredKeys []string) *errors.ApiError { for _, k := range requiredKeys { if val, ok := params[k]; !ok || val == "" { - return utils.NewMissingArgError(k) + return errors.NewF("--%s must be set", k) } } return nil } - -// IsInit checks if passed in command line args contain an init command. IsInit supports both cli.Args and os.Args. -func IsInit(args []string) bool { - if len(args) == 0 { - return false - } - - var a []string - if args[0] == cst.CmdRoot { - a = args[1:] - } else { - a = args - } - - if len(a) == 0 { - return false - } - if a[0] == cst.Init { - return true - } - if len(a) > 1 && strings.Contains(strings.Join(a, " "), cst.NounCliConfig+" "+cst.Init) { - return true - } - return false -} - -// IsInstall checks if passed in command line args contain an install command. -func IsInstall(args []string) bool { - for _, a := range args { - if a == "--install" || a == "-install" { - return true - } - } - return false -} - -// OnlyGlobalArgs checks if passed in command line args to a subcommand are only global flags. -// It assumes viper had already set values for relevant flags like profile and config. -func OnlyGlobalArgs(args []string) bool { - globalFlags := BasePredictorWrappers() - - var isGlobal bool - for _, arg := range args { - if !strings.HasPrefix(arg, "-") { - continue // skip, not a flag - } - - f := strings.TrimPrefix(arg, "--") - f = strings.TrimPrefix(f, "-") - f = strings.Split(f, "=")[0] - - isGlobal = false - for _, g := range globalFlags { - if f == vaultcli.ToFlagName(g.Name) || f == g.Shorthand { - isGlobal = g.Global - break - } - } - if !isGlobal { - return false - } - } - return true -} diff --git a/commands/base_test.go b/commands/base_test.go index 97180b0a..edeb97e8 100644 --- a/commands/base_test.go +++ b/commands/base_test.go @@ -1,100 +1,12 @@ package cmd import ( - "strings" "testing" - cst "thy/constants" + "github.com/stretchr/testify/assert" ) -func TestIsInit(t *testing.T) { - tests := []struct { - args string - expected bool - }{ - {"", false}, - {cst.CmdRoot, false}, - {cst.CmdRoot + " " + "secret", false}, - {cst.CmdRoot + " " + "secret init", false}, - {cst.CmdRoot + " " + "cli-config", false}, - - {"init", true}, - {"init --dev devbambe.com", true}, - {cst.CmdRoot + " " + "init", true}, - {cst.CmdRoot + " " + "init --dev devbambe.com", true}, - - {"cli-config init", true}, - {"cli-config init --profile local", true}, - {cst.CmdRoot + " " + "cli-config init", true}, - {cst.CmdRoot + " " + "cli-config init --profile local", true}, - } - - for _, tc := range tests { - args := strings.Split(tc.args, " ") - got := IsInit(args) - if got != tc.expected { - t.Errorf("Expected IsInit(%v) to return %v, but got %v", args, tc.expected, got) - } - } -} - -func TestIsInstall(t *testing.T) { - tests := []struct { - args string - expected bool - }{ - {"", false}, - {cst.CmdRoot, false}, - {cst.CmdRoot + " " + "secret", false}, - {cst.CmdRoot + " " + "secret init", false}, - {cst.CmdRoot + " " + "cli-config", false}, - - {"--install", true}, - {"-install", true}, - {cst.CmdRoot + " " + "--install", true}, - {cst.CmdRoot + " " + "-install", true}, - } - - for _, tc := range tests { - args := strings.Split(tc.args, " ") - got := IsInstall(args) - if got != tc.expected { - t.Errorf("Expected IsInstall(%v) to return %v, but got %v", args, tc.expected, got) - } - } -} - -func TestOnlyGlobalArgs(t *testing.T) { - allGlobals := func(flags string) { - t.Helper() - result := OnlyGlobalArgs(strings.Split(flags, " ")) - if !result { - t.Errorf("OnlyGlobalArgs(%v) must return true", flags) - } - } - notGlobals := func(flags string) { - t.Helper() - result := OnlyGlobalArgs(strings.Split(flags, " ")) - if result { - t.Errorf("OnlyGlobalArgs(%v) must return false", flags) - } - } - - allGlobals("--profile local") - allGlobals("--profile=local") - allGlobals("--config cfg") - allGlobals("--profile local --config cfg") - allGlobals("-v") - allGlobals("--verbose") - allGlobals("--profile local -v") - allGlobals("--profile local --config cfg--verbose") - allGlobals("--auth-type password -v") - allGlobals("--auth-type password --auth-username tom --auth-password r1ddle") - allGlobals("--auth-type password --auth-username=tom --auth-password r1ddle") - allGlobals("-a password -u tom -p r1ddle") - - notGlobals("--data") - notGlobals("--path databases/mongo-db01 --data '{\"Key\":\"Value\"}'") - notGlobals("--effect allow --auth-type password --auth-username tom --auth-password r1ddle") - notGlobals("--effect allow -a password -u tom -p r1ddle") +func TestNewCommand(t *testing.T) { + _, err := NewCommand(CommandArgs{}) + assert.Error(t, err) } diff --git a/commands/breakglass.go b/commands/breakglass.go index 052498c8..79d6eb1e 100644 --- a/commands/breakglass.go +++ b/commands/breakglass.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "net/http" "strconv" "strings" @@ -23,23 +22,20 @@ func GetBreakGlassCmd() (cli.Command, error) { Path: []string{cst.NounBreakGlass}, SynopsisText: "Manage Break-Glass setup", HelpText: "Initiate restoration of admin users", - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return cli.RunResultHelp - }, + NoConfigRead: true, + NoPreAuth: true, }) } func GetBreakGlassGetStatusCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounBreakGlass, cst.Status}, - SynopsisText: "Check whether Break Glass feature is set up for the tenant", - HelpText: fmt.Sprintf(` + SynopsisText: "Check whether Break-Glass feature is set up for the tenant", + HelpText: ` Usage: - • %[1]s %[2]s - `, cst.NounBreakGlass, cst.Status), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleBreakGlassGetStatusCmd(vcli, args) - }, + • breakglass status +`, + RunFunc: handleBreakGlassGetStatusCmd, }) } @@ -47,20 +43,16 @@ func GetBreakGlassGenerateCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounBreakGlass, cst.Generate}, SynopsisText: "Generate and store admin secret and new admins' shares", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s --%[3]s 'newAdminUsername1,newAdminUsername2' --%[4]s 2 - `, cst.NounBreakGlass, cst.Generate, cst.NewAdmins, cst.MinNumberOfShares), + • breakglass generate --new-admins 'newAdminUsername1,newAdminUsername2' --min-number-of-shares 2 +`, FlagsPredictor: []*predictor.Params{ {Name: cst.NewAdmins, Usage: "New admins list (required)"}, {Name: cst.MinNumberOfShares, Usage: "Minimum number of shares to apply (required)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleBreakGlassGenerateWizard(vcli, args) - } - return handleBreakGlassGenerateCmd(vcli, args) - }, + RunFunc: handleBreakGlassGenerateCmd, + WizardFunc: handleBreakGlassGenerateWizard, }) } @@ -68,19 +60,15 @@ func GetBreakGlassApplyCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounBreakGlass, cst.Apply}, SynopsisText: "Apply shares and break glass", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s --%[3]s '{share1},{share2},...,{shareN}' - `, cst.NounBreakGlass, cst.Apply, cst.Shares), + • breakglass apply --shares '{share1},{share2},...,{shareN}' +`, FlagsPredictor: []*predictor.Params{ {Name: cst.Shares, Usage: "List of shares to apply Break Glass action (required)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleBreakGlassApplyWizard(vcli, args) - } - return handleBreakGlassApplyCmd(vcli, args) - }, + RunFunc: handleBreakGlassApplyCmd, + WizardFunc: handleBreakGlassApplyWizard, }) } @@ -143,7 +131,7 @@ func handleBreakGlassApplyCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleBreakGlassGenerateWizard(vcli vaultcli.CLI, args []string) int { +func handleBreakGlassGenerateWizard(vcli vaultcli.CLI) int { newAdmins := []string{} minNumberOfShares := 0 @@ -212,7 +200,7 @@ func handleBreakGlassGenerateWizard(vcli vaultcli.CLI, args []string) int { return utils.GetExecStatus(err) } -func handleBreakGlassApplyWizard(vcli vaultcli.CLI, args []string) int { +func handleBreakGlassApplyWizard(vcli vaultcli.CLI) int { data, err := breakGlassStatus(vcli) if err != nil { vcli.Out().WriteResponse(nil, err) diff --git a/commands/cli-config.go b/commands/cli-config.go index 24f5ee71..d5651df4 100644 --- a/commands/cli-config.go +++ b/commands/cli-config.go @@ -31,10 +31,8 @@ func GetCliConfigCmd() (cli.Command, error) { Path: []string{cst.NounCliConfig}, SynopsisText: "Manage the CLI configuration", HelpText: "Execute an action on the cli config for " + cst.ProductName, + NoConfigRead: true, NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return cli.RunResultHelp - }, }) } @@ -98,7 +96,8 @@ Examples: --auth-certificate '@./path/to/certificate' \ --auth-privateKey '@./path/to/private_key' `, - NoPreAuth: true, + NoConfigRead: true, + NoPreAuth: true, FlagsPredictor: []*predictor.Params{ // Configuration path and profile name. {Name: cst.Config, Shorthand: "c", Usage: fmt.Sprintf("Set config file path [default:%s]", defaultConfigPath)}, @@ -127,9 +126,7 @@ Examples: {Name: cst.Dev, Hidden: true, Usage: "Specify dev domain upon initialization (ignored when '--domain' is used)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigInitCmd(vcli, args) - }, + RunFunc: handleCliConfigInitCmd, }) } @@ -149,9 +146,7 @@ Usage: {Name: cst.Value, Usage: "Value of setting to be udpated (required)"}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigUpdateCmd(vcli, args) - }, + RunFunc: handleCliConfigUpdateCmd, }) } @@ -161,9 +156,7 @@ func GetCliConfigClearCmd() (cli.Command, error) { SynopsisText: strings.Join([]string{cst.NounCliConfig, cst.Clear}, " "), HelpText: "Clear the cli config for " + cst.ProductName, NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigClearCmd(vcli, args) - }, + RunFunc: handleCliConfigClearCmd, }) } @@ -173,9 +166,7 @@ func GetCliConfigReadCmd() (cli.Command, error) { SynopsisText: strings.Join([]string{cst.NounCliConfig, cst.Read}, " "), HelpText: "Read the cli config for " + cst.ProductName, NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigReadCmd(vcli, args) - }, + RunFunc: handleCliConfigReadCmd, }) } @@ -185,9 +176,7 @@ func GetCliConfigEditCmd() (cli.Command, error) { SynopsisText: strings.Join([]string{cst.NounCliConfig, cst.Edit}, " "), HelpText: "Edit the cli config for " + cst.ProductName, NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigEditCmd(vcli, args) - }, + RunFunc: handleCliConfigEditCmd, }) } @@ -202,9 +191,7 @@ func GetCliConfigUseProfileCmd() (cli.Command, error) { For interactive mode provide no arguments: • cli-config use-profile`, NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCliConfigUseProfileCmd(vcli, args) - }, + RunFunc: handleCliConfigUseProfileCmd, }) } diff --git a/commands/cli-config_test.go b/commands/cli-config_test.go index dbe56e1e..1ebd8a34 100644 --- a/commands/cli-config_test.go +++ b/commands/cli-config_test.go @@ -35,3 +35,8 @@ func TestGetCliConfigEditCmd(t *testing.T) { _, err := GetCliConfigEditCmd() assert.Nil(t, err) } + +func TestGetCliConfigUseProfileCmd(t *testing.T) { + _, err := GetCliConfigUseProfileCmd() + assert.Nil(t, err) +} diff --git a/commands/client.go b/commands/client.go index aaecfbc6..4d3920e1 100644 --- a/commands/client.go +++ b/commands/client.go @@ -29,11 +29,11 @@ Usage: • %[1]s --client-id %[3]s `, cst.NounClient, cst.ProductName, cst.ExampleClientID), FlagsPredictor: []*predictor.Params{ - {Name: cst.ClientID, Usage: fmt.Sprintf("ID of the %s ", cst.NounClient)}, + {Name: cst.ClientID, Usage: "ID of the client"}, }, MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { - name := viper.GetString(cst.DataName) + name := viper.GetString(cst.ClientID) if name == "" && len(args) > 0 && !strings.HasPrefix(args[0], "-") { name = args[0] } @@ -56,12 +56,10 @@ Usage: • %[1]s %[4]s --client-id %[3]s `, cst.NounClient, cst.ProductName, cst.ExampleClientID, cst.Read), FlagsPredictor: []*predictor.Params{ - {Name: cst.ClientID, Usage: fmt.Sprintf("ID of the %s ", cst.NounClient)}, + {Name: cst.ClientID, Usage: "ID of the client"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleClientReadCmd(vcli, args) - }, + RunFunc: handleClientReadCmd, }) } @@ -76,13 +74,11 @@ Usage: • %[1]s %[4]s --client-id %[3]s --force `, cst.NounClient, cst.ProductName, cst.ExampleClientID, cst.Delete), FlagsPredictor: []*predictor.Params{ - {Name: cst.ClientID, Usage: fmt.Sprintf("ID of the %s ", cst.NounClient)}, + {Name: cst.ClientID, Usage: "ID of the client"}, {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounClient), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleClientDeleteCmd(vcli, args) - }, + RunFunc: handleClientDeleteCmd, }) } @@ -96,12 +92,10 @@ Usage: • %[1]s %[4]s %[3]s `, cst.NounClient, cst.ProductName, cst.ExampleClientID, cst.Restore), FlagsPredictor: []*predictor.Params{ - {Name: cst.ClientID, Usage: fmt.Sprintf("ID of the %s ", cst.NounClient)}, + {Name: cst.ClientID, Usage: "ID of the client"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleClientRestoreCmd(vcli, args) - }, + RunFunc: handleClientRestoreCmd, }) } @@ -123,12 +117,8 @@ Usage: {Name: cst.NounClientDesc, Usage: "Client credential description (optional)"}, {Name: cst.NounClientTTL, Usage: "How long until the client credential expires. If set to 0, it can be used indefinitely. Default is 0 (optional)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleClientCreateWizard(vcli, args) - } - return handleClientCreateCmd(vcli, args) - }, + RunFunc: handleClientCreateCmd, + WizardFunc: handleClientCreateWizard, }) } @@ -144,13 +134,11 @@ Usage: `, cst.NounClient, cst.ProductName, cst.ExampleRoleName, cst.Search, cst.NounRole), FlagsPredictor: []*predictor.Params{ {Name: cst.NounRole, Usage: "Role that has attached clients (required)"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleClientSearchCmd(vcli, args) - }, + RunFunc: handleClientSearchCmd, }) } @@ -245,7 +233,7 @@ func handleClientSearchCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleClientCreateWizard(vcli vaultcli.CLI, args []string) int { +func handleClientCreateWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "Role", diff --git a/commands/config.go b/commands/config.go index fac65746..1f337c75 100644 --- a/commands/config.go +++ b/commands/config.go @@ -31,9 +31,7 @@ Usage: • config %[1]s • config %[4]s --data %[5]s `, cst.Read, cst.NounConfig, cst.ProductName, cst.Update, cst.ExampleConfigPath), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleConfigReadCmd(vcli, args) - }, + RunFunc: handleConfigReadCmd, }) } @@ -48,9 +46,7 @@ Usage: FlagsPredictor: []*predictor.Params{ {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleConfigReadCmd(vcli, args) - }, + RunFunc: handleConfigReadCmd, }) } @@ -68,9 +64,7 @@ Usage: {Name: cst.Data, Shorthand: "d", Usage: fmt.Sprintf("%s to be stored in the %s. Prefix with '@' to denote filepath (required)", strings.Title(cst.Data), cst.Config), Predictor: predictor.NewPrefixFilePredictor("*")}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleConfigUpdateCmd(vcli, args) - }, + RunFunc: handleConfigUpdateCmd, }) } @@ -83,9 +77,7 @@ Usage: • config edit • config edit --encoding yml `, cst.NounConfig, cst.ProductName), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleConfigEditCmd(vcli, args) - }, + RunFunc: handleConfigEditCmd, }) } diff --git a/commands/config_test.go b/commands/config_test.go index c230c511..cbf542ef 100644 --- a/commands/config_test.go +++ b/commands/config_test.go @@ -14,6 +14,26 @@ import ( "github.com/stretchr/testify/assert" ) +func TestGetConfigCmd(t *testing.T) { + _, err := GetConfigCmd() + assert.Nil(t, err) +} + +func TestGetConfigReadCmd(t *testing.T) { + _, err := GetConfigReadCmd() + assert.Nil(t, err) +} + +func TestGetConfigUpdateCmd(t *testing.T) { + _, err := GetConfigUpdateCmd() + assert.Nil(t, err) +} + +func TestGetConfigEditCmd(t *testing.T) { + _, err := GetConfigEditCmd() + assert.Nil(t, err) +} + func TestHandleConfigUpdateCmd(t *testing.T) { testCase := []struct { name string @@ -51,9 +71,6 @@ func TestHandleConfigUpdateCmd(t *testing.T) { }, } - _, err := GetConfigUpdateCmd() - assert.Nil(t, err) - viper.Set(cst.Version, "v1") for _, tt := range testCase { @@ -120,9 +137,6 @@ func TestHandleConfigReadCmd(t *testing.T) { }, } - _, err := GetConfigReadCmd() - assert.Nil(t, err) - viper.Set(cst.Version, "v1") for _, tt := range testCase { tt := tt @@ -190,9 +204,6 @@ func TestHandleConfigEditCmd(t *testing.T) { }, } - _, err := GetConfigEditCmd() - assert.Nil(t, err) - for _, tt := range testCase { tt := tt t.Run(tt.name, func(t *testing.T) { @@ -240,8 +251,3 @@ func TestHandleConfigEditCmd(t *testing.T) { }) } } - -func TestGetConfigCmd(t *testing.T) { - _, err := GetConfigCmd() - assert.Nil(t, err) -} diff --git a/commands/encryption_auto.go b/commands/encryption_auto.go index c48a5556..002a281c 100644 --- a/commands/encryption_auto.go +++ b/commands/encryption_auto.go @@ -28,9 +28,8 @@ func GetCryptoCmd() (cli.Command, error) { Path: []string{cst.NounEncryption}, SynopsisText: "Encryption-as-a-Service", HelpText: "Encryption-as-a-Service", - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return cli.RunResultHelp - }, + NoConfigRead: true, + NoPreAuth: true, }) } @@ -46,9 +45,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounKey), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleCreateAutoKey(vcli, args) - }, + RunFunc: handleCreateAutoKey, }) } @@ -69,9 +66,7 @@ Usage: {Name: cst.Output, Usage: "Output file for encrypted value and metadata"}, }, MinNumberArgs: 5, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRotate(vcli, args) - }, + RunFunc: handleRotate, }) } @@ -87,9 +82,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounKey), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleReadAutoKey(vcli, args) - }, + RunFunc: handleReadAutoKey, }) } @@ -106,9 +99,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s and all its versions", cst.NounKey), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleDeleteAutoKey(vcli, args) - }, + RunFunc: handleDeleteAutoKey, }) } @@ -124,9 +115,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounKey), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRestoreAutoKey(vcli, args) - }, + RunFunc: handleRestoreAutoKey, }) } @@ -146,9 +135,7 @@ Usage: {Name: cst.Output, Usage: "Output file for encrypted value and metadata"}, }, MinNumberArgs: 4, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEncrypt(vcli, args) - }, + RunFunc: handleEncrypt, }) } @@ -168,9 +155,7 @@ Usage: {Name: cst.Output, Usage: "Output file for decrypted value and metadata"}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleDecrypt(vcli, args) - }, + RunFunc: handleDecrypt, }) } diff --git a/commands/encryption_manual.go b/commands/encryption_manual.go index 55543b4f..99a74313 100644 --- a/commands/encryption_manual.go +++ b/commands/encryption_manual.go @@ -24,9 +24,8 @@ func GetCryptoManualCmd() (cli.Command, error) { Path: []string{cst.NounEncryption, cst.Manual}, SynopsisText: "Encryption-as-a-Service with a Manual Key", HelpText: "Encryption-as-a-Service with a Manual Key", - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return cli.RunResultHelp - }, + NoConfigRead: true, + NoPreAuth: true, }) } @@ -47,9 +46,7 @@ Usage: {Name: cst.Metadata, Usage: "Metadata as a JSON object (optional)"}, }, MinNumberArgs: 6, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUploadManualKey(vcli, args) - }, + RunFunc: handleUploadManualKey, }) } @@ -69,9 +66,7 @@ Usage: {Name: cst.Metadata, Usage: "Metadata as a JSON object (optional)"}, }, MinNumberArgs: 4, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUpdateManualKey(vcli, args) - }, + RunFunc: handleUpdateManualKey, }) } @@ -87,9 +82,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounKey), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleReadManualKey(vcli, args) - }, + RunFunc: handleReadManualKey, }) } @@ -106,9 +99,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s and all its versions", cst.NounKey), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleDeleteManualKey(vcli, args) - }, + RunFunc: handleDeleteManualKey, }) } @@ -124,9 +115,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounKey), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRestoreManualKey(vcli, args) - }, + RunFunc: handleRestoreManualKey, }) } @@ -146,9 +135,7 @@ Usage: {Name: cst.Output, Usage: "Output file for encrypted value and metadata"}, }, MinNumberArgs: 4, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleManualKeyEncrypt(vcli, args) - }, + RunFunc: handleManualKeyEncrypt, }) } @@ -168,9 +155,7 @@ Usage: {Name: cst.Output, Usage: "Output file for decrypted value and metadata"}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleManualKeyDecrypt(vcli, args) - }, + RunFunc: handleManualKeyDecrypt, }) } diff --git a/commands/encryption_manual_test.go b/commands/encryption_manual_test.go index 8786fb8d..b795d616 100644 --- a/commands/encryption_manual_test.go +++ b/commands/encryption_manual_test.go @@ -10,3 +10,38 @@ func TestGetCryptoManualCmd(t *testing.T) { _, err := GetCryptoManualCmd() assert.Nil(t, err) } + +func TestGetManualKeyUploadCmd(t *testing.T) { + _, err := GetManualKeyUploadCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyUpdateCmd(t *testing.T) { + _, err := GetManualKeyUpdateCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyReadCmd(t *testing.T) { + _, err := GetManualKeyReadCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyDeleteCmd(t *testing.T) { + _, err := GetManualKeyDeleteCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyRestoreCmd(t *testing.T) { + _, err := GetManualKeyRestoreCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyEncryptCmd(t *testing.T) { + _, err := GetManualKeyEncryptCmd() + assert.Nil(t, err) +} + +func TestGetManualKeyDecryptCmd(t *testing.T) { + _, err := GetManualKeyDecryptCmd() + assert.Nil(t, err) +} diff --git a/commands/engine.go b/commands/engine.go index f4f0441b..9ba8d945 100644 --- a/commands/engine.go +++ b/commands/engine.go @@ -47,9 +47,7 @@ Usage: {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounEngine)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEngineReadCmd(vcli, args) - }, + RunFunc: handleEngineReadCmd, }) } @@ -60,9 +58,7 @@ func GetEngineListCmd() (cli.Command, error) { HelpText: fmt.Sprintf(` Usage: • %[1]s %[2]s`, cst.NounEngine, cst.List), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEngineListCmd(vcli, args) - }, + RunFunc: handleEngineListCmd, }) } @@ -77,9 +73,7 @@ Usage: {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounEngine)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEngineDeleteCmd(vcli, args) - }, + RunFunc: handleEngineDeleteCmd, }) } @@ -94,9 +88,8 @@ Usage: {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounEngine)}, {Name: cst.DataPoolName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounPool)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEngineCreateCmd(vcli, args) - }, + RunFunc: handleEngineCreateCmd, + WizardFunc: handleEngineCreateWizard, }) } @@ -111,9 +104,7 @@ Usage: {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounEngine)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEnginePingCmd(vcli, args) - }, + RunFunc: handleEnginePingCmd, }) } @@ -172,10 +163,6 @@ func handleEnginePingCmd(vcli vaultcli.CLI, args []string) int { } func handleEngineCreateCmd(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleEngineCreateWizard(vcli, args) - } - engineName := viper.GetString(cst.DataName) poolName := viper.GetString(cst.DataPoolName) if engineName == "" || poolName == "" { @@ -195,7 +182,7 @@ func handleEngineCreateCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleEngineCreateWizard(vcli vaultcli.CLI, args []string) int { +func handleEngineCreateWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "EngineName", diff --git a/commands/group.go b/commands/group.go index 042145b6..76b21ab5 100644 --- a/commands/group.go +++ b/commands/group.go @@ -89,12 +89,8 @@ Usage: {Name: cst.DataGroupName, Usage: fmt.Sprintf("%s of %s (required)", strings.Title(cst.DataName), cst.NounGroup)}, {Name: cst.Members, Usage: "Group members (comma-separated, optional)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleGroupCreateWizard(vcli, args) - } - return handleGroupCreateCmd(vcli, args) - }, + RunFunc: handleGroupCreateCmd, + WizardFunc: handleGroupCreateWizard, }) } @@ -113,9 +109,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounGroup), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGroupDeleteCmd(vcli, args) - }, + RunFunc: handleGroupDeleteCmd, }) } @@ -133,9 +127,7 @@ Usage: {Name: cst.DataGroupName, Usage: fmt.Sprintf("%s of %s (required)", strings.Title(cst.DataName), cst.NounGroup)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGroupRestoreCmd(vcli, args) - }, + RunFunc: handleGroupRestoreCmd, }) } @@ -156,9 +148,7 @@ Usage: {Name: cst.Members, Usage: "Group members (comma-separated, optional)"}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleAddMembersCmd(vcli, args) - }, + RunFunc: handleAddMembersCmd, }) } @@ -179,9 +169,7 @@ Usage: {Name: cst.Members, Usage: "Group members (comma-separated, optional)"}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleDeleteMembersCmd(vcli, args) - }, + RunFunc: handleDeleteMembersCmd, }) } @@ -198,9 +186,7 @@ Usage: {Name: cst.DataUsername, Usage: fmt.Sprintf("%s of %s (required)", strings.Title(cst.DataUsername), cst.NounUser)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUsersGroupReadCmd(vcli, args) - }, + RunFunc: handleUsersGroupReadCmd, }) } @@ -216,12 +202,10 @@ Usage: `, cst.Search, cst.NounGroup, cst.ProductName, cst.ExampleUserSearch), FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("%s of %ss to fetch (optional)", strings.Title(cst.Query), cst.NounGroup)}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGroupSearchCmd(vcli, args) - }, + RunFunc: handleGroupSearchCmd, }) } @@ -451,7 +435,7 @@ func handleGroupSearchCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleGroupCreateWizard(vcli vaultcli.CLI, args []string) int { +func handleGroupCreateWizard(vcli vaultcli.CLI) int { var groupName string groupNamePrompt := &survey.Input{Message: "Group name:"} groupNameValidation := func(ans interface{}) error { diff --git a/commands/home.go b/commands/home.go index f15d3312..94b4219a 100644 --- a/commands/home.go +++ b/commands/home.go @@ -6,6 +6,7 @@ import ( cst "thy/constants" "thy/internal/predictor" + "thy/utils" "thy/vaultcli" "github.com/mitchellh/cli" @@ -52,11 +53,8 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounSecret), Predictor: predictor.NewSecretPathPredictorDefault()}, {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeRead(vcli, args) - }, + MinNumberArgs: 1, + RunFunc: handleHomeRead, }) } @@ -77,9 +75,8 @@ Usage: {Name: cst.DataAttributes, Usage: fmt.Sprintf("Attributes of a %s", cst.NounSecret)}, }, MinNumberArgs: 2, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeCreate(vcli, args) - }, + RunFunc: handleHomeCreate, + WizardFunc: handleHomeCreateWizard, }) } @@ -97,9 +94,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounSecret), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeDelete(vcli, args) - }, + RunFunc: handleHomeDelete, }) } @@ -115,9 +110,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounHome), Predictor: predictor.NewSecretPathPredictorDefault()}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeRestore(vcli, args) - }, + RunFunc: handleHomeRestore, }) } @@ -139,9 +132,8 @@ Usage: {Name: cst.Overwrite, Usage: fmt.Sprintf("Overwrite all the contents of %s data", cst.NounSecret), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeUpdate(vcli, args) - }, + RunFunc: handleHomeUpdate, + WizardFunc: handleHomeUpdateWizard, }) } @@ -159,9 +151,7 @@ Usage: {Name: cst.Version, Usage: "The version to which to rollback"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeRollback(vcli, args) - }, + RunFunc: handleHomeRollback, }) } @@ -180,9 +170,7 @@ Usage: • %[2]s %[1]s --query 900 --search-field attributes.ttl --search-type number • %[2]s %[1]s --query production --search-field attributes.stage --search-comparison equal `, cst.Search, cst.NounHome, cst.ProductName, cst.ExampleUserSearch), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeSearch(vcli, args) - }, + RunFunc: handleHomeSearch, }) } @@ -199,9 +187,7 @@ Usage: • %[1]s %[2]s --%[3]s %[4]s `, cst.NounHome, cst.Describe, cst.Path, cst.ExamplePath), MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeDescribe(vcli, args) - }, + RunFunc: handleHomeDescribe, }) } @@ -218,9 +204,7 @@ Usage: • %[1]s %[2]s --%[3]s %[4]s `, cst.NounHome, cst.Edit, cst.Path, cst.ExamplePath), MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleHomeEdit(vcli, args) - }, + RunFunc: handleHomeEdit, }) } @@ -259,3 +243,17 @@ func handleHomeDescribe(vcli vaultcli.CLI, args []string) int { func handleHomeEdit(vcli vaultcli.CLI, args []string) int { return handleSecretEditCmd(vcli, cst.NounHome, args) } + +// Wizards: + +func handleHomeCreateWizard(vcli vaultcli.CLI) int { + resp, err := handleGenericSecretCreateWizard(vcli, cst.NounHome) + vcli.Out().WriteResponse(resp, err) + return utils.GetExecStatus(err) +} + +func handleHomeUpdateWizard(vcli vaultcli.CLI) int { + resp, err := handleGenericSecretUpdateWizard(vcli, cst.NounHome) + vcli.Out().WriteResponse(resp, err) + return utils.GetExecStatus(err) +} diff --git a/commands/home_test.go b/commands/home_test.go index c5c28e3c..7467da3c 100644 --- a/commands/home_test.go +++ b/commands/home_test.go @@ -10,3 +10,48 @@ func TestGetHomeCmd(t *testing.T) { _, err := GetHomeCmd() assert.Nil(t, err) } + +func TestGetHomeReadCmd(t *testing.T) { + _, err := GetHomeReadCmd() + assert.Nil(t, err) +} + +func TestGetHomeCreateCmd(t *testing.T) { + _, err := GetHomeCreateCmd() + assert.Nil(t, err) +} + +func TestGetHomeDeleteCmd(t *testing.T) { + _, err := GetHomeDeleteCmd() + assert.Nil(t, err) +} + +func TestGetHomeRestoreCmd(t *testing.T) { + _, err := GetHomeRestoreCmd() + assert.Nil(t, err) +} + +func TestGetHomeUpdateCmd(t *testing.T) { + _, err := GetHomeUpdateCmd() + assert.Nil(t, err) +} + +func TestGetHomeRollbackCmd(t *testing.T) { + _, err := GetHomeRollbackCmd() + assert.Nil(t, err) +} + +func TestGetHomeSearchCmd(t *testing.T) { + _, err := GetHomeSearchCmd() + assert.Nil(t, err) +} + +func TestGetHomeDescribeCmd(t *testing.T) { + _, err := GetHomeDescribeCmd() + assert.Nil(t, err) +} + +func TestGetHomeEditCmd(t *testing.T) { + _, err := GetHomeEditCmd() + assert.Nil(t, err) +} diff --git a/commands/misc.go b/commands/misc.go index ea384299..dc59c387 100644 --- a/commands/misc.go +++ b/commands/misc.go @@ -19,9 +19,7 @@ func GetWhoAmICmd() (cli.Command, error) { SynopsisText: "Show current identity", HelpText: fmt.Sprintf("%s returns the current user identity, accounting for config, env, and flags", cst.NounWhoAmI), NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleWhoAmICmd(vcli, args) - }, + RunFunc: handleWhoAmICmd, }) } @@ -31,9 +29,7 @@ func GetEvaluateFlagCmd() (cli.Command, error) { SynopsisText: "Inspect environment and configuration values", HelpText: fmt.Sprintf("%s returns the value of the variable, accounting for config, env, and flags", cst.EvaluateFlag), NoPreAuth: true, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleEvaluateFlag(vcli, args) - }, + RunFunc: handleEvaluateFlag, }) } diff --git a/commands/pki.go b/commands/pki.go index dc220dd1..efff1612 100644 --- a/commands/pki.go +++ b/commands/pki.go @@ -27,7 +27,8 @@ func GetPkiCmd() (cli.Command, error) { Path: []string{cst.NounPki}, SynopsisText: "Manage certificates", HelpText: "Work with certificates", - RunFunc: func(vcli vaultcli.CLI, args []string) int { return cli.RunResultHelp }, + NoConfigRead: true, + NoPreAuth: true, }) } @@ -47,9 +48,8 @@ Usage: {Name: cst.MaxTTL, Usage: "Maximum number of hours for which a signed certificate on behalf of the root CA can be valid (required)"}, {Name: cst.CRL, Usage: "URL of the CRL from which the revocation of leaf certificates can be checked"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRegisterRootCmd(vcli, args) - }, + RunFunc: handleRegisterRootCmd, + WizardFunc: handleRegisterRootWizard, }) } @@ -68,9 +68,8 @@ Usage: {Name: cst.TTL, Usage: "Number of hours for which a signed certificate on behalf of the root CA can be valid"}, {Name: cst.Chain, Usage: "Include root certificate in response", ValueType: "bool"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSignCmd(vcli, args) - }, + RunFunc: handleSignCmd, + WizardFunc: handleSignWizard, }) } @@ -97,9 +96,8 @@ Usage: {Name: cst.TTL, Usage: "Number of hours for which a signed certificate on behalf of the root CA can be valid"}, {Name: cst.Chain, Usage: "Include root certificate in response", ValueType: "bool"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleLeafCmd(vcli, args) - }, + RunFunc: handleLeafCmd, + WizardFunc: handleLeafWizard, }) } @@ -126,9 +124,8 @@ Usage: {Name: cst.MaxTTL, Usage: "Number of hours for which a generated root certificate can be valid (required)"}, {Name: cst.CRL, Usage: "URL of the CRL from which the revocation of leaf certificates can be checked"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGenerateRootCmd(vcli, args) - }, + RunFunc: handleGenerateRootCmd, + WizardFunc: handleGenerateRootWizard, }) } @@ -147,16 +144,11 @@ Usage: {Name: cst.TTL, Usage: "Number of hours for which a signed certificate can be valid (required)"}, }, MinNumberArgs: 8, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGetSSHCertificateCmd(vcli, args) - }, + RunFunc: handleGetSSHCertificateCmd, }) } func handleRegisterRootCmd(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleRegisterRootWorkflow(vcli, args) - } params := map[string]string{ cst.CertPath: viper.GetString(cst.CertPath), cst.PrivKeyPath: viper.GetString(cst.PrivKeyPath), @@ -170,9 +162,6 @@ func handleRegisterRootCmd(vcli vaultcli.CLI, args []string) int { } func handleSignCmd(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleSignWorkflow(vcli, args) - } params := map[string]string{ cst.CSRPath: viper.GetString(cst.CSRPath), cst.RootCAPath: viper.GetString(cst.RootCAPath), @@ -186,9 +175,6 @@ func handleSignCmd(vcli vaultcli.CLI, args []string) int { } func handleLeafCmd(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleLeafWorkflow(vcli, args) - } params := map[string]string{ cst.RootCAPath: viper.GetString(cst.RootCAPath), cst.PkiStorePath: viper.GetString(cst.PkiStorePath), @@ -208,9 +194,6 @@ func handleLeafCmd(vcli vaultcli.CLI, args []string) int { } func handleGenerateRootCmd(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleGenerateRootWorkflow(vcli, args) - } params := map[string]string{ cst.RootCAPath: viper.GetString(cst.RootCAPath), cst.Domains: viper.GetString(cst.Domains), @@ -259,7 +242,7 @@ func handleGetSSHCertificateCmd(vcli vaultcli.CLI, args []string) int { return utils.GetExecStatus(apiErr) } -func handleRegisterRootWorkflow(vcli vaultcli.CLI, args []string) int { +func handleRegisterRootWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "CertFile", @@ -368,7 +351,7 @@ func submitRoot(vcli vaultcli.CLI, params map[string]string) ([]byte, error) { return pkiRegister(vcli, &body) } -func handleSignWorkflow(vcli vaultcli.CLI, args []string) int { +func handleSignWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "CSRPath", @@ -460,7 +443,7 @@ func submitSign(vcli vaultcli.CLI, params map[string]string) ([]byte, error) { return pkiSign(vcli, &body) } -func handleLeafWorkflow(vcli vaultcli.CLI, args []string) int { +func handleLeafWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "RootCAPath", @@ -582,7 +565,7 @@ func submitLeaf(vcli vaultcli.CLI, params map[string]string) ([]byte, error) { return pkiLeaf(vcli, &body) } -func handleGenerateRootWorkflow(vcli vaultcli.CLI, args []string) int { +func handleGenerateRootWizard(vcli vaultcli.CLI) int { qs := []*survey.Question{ { Name: "RootCAPath", diff --git a/commands/policy.go b/commands/policy.go index ba31b77d..93e76364 100644 --- a/commands/policy.go +++ b/commands/policy.go @@ -63,9 +63,7 @@ Usage: {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicyReadCmd(vcli, args) - }, + RunFunc: handlePolicyReadCmd, }) } @@ -83,9 +81,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s", cst.Path, cst.NounPolicy)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicyEditCmd(vcli, args) - }, + RunFunc: handlePolicyEditCmd, }) } @@ -104,9 +100,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounPolicy), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicyDeleteCmd(vcli, args) - }, + RunFunc: handlePolicyDeleteCmd, }) } @@ -123,9 +117,7 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s", cst.Path, cst.NounPolicy)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicyRestoreCmd(vcli, args) - }, + RunFunc: handlePolicyRestoreCmd, }) } @@ -149,12 +141,8 @@ Usage: {Name: cst.DataCidr, Usage: fmt.Sprintf("Policy CIDR condition remote IP to be stored in a %s ", cst.NounPolicy)}, {Name: cst.DataResource, Usage: fmt.Sprintf("Policy resources to be stored in a %s. Defaults to the path plus all paths below (<.*>) ", cst.NounPolicy)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handlePolicyCreateWizard(vcli, args) - } - return handlePolicyCreateCmd(vcli, args) - }, + RunFunc: handlePolicyCreateCmd, + WizardFunc: handlePolicyCreateWizard, }) } @@ -180,12 +168,8 @@ Usage: {Name: cst.DataCidr, Usage: fmt.Sprintf("Policy CIDR condition remote IP to be stored in a %s ", cst.NounPolicy)}, {Name: cst.DataResource, Usage: fmt.Sprintf("Policy resources to be stored in a %s. Defaults to the path plus all paths below (<.*>) ", cst.NounPolicy)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handlePolicyUpdateWizard(vcli, args) - } - return handlePolicyUpdateCmd(vcli, args) - }, + RunFunc: handlePolicyUpdateCmd, + WizardFunc: handlePolicyUpdateWizard, }) } @@ -204,13 +188,11 @@ Usage: {Name: cst.Version, Usage: "The version to which to rollback"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicyRollbackCmd(vcli, args) - }, + RunFunc: handlePolicyRollbackCmd, }) } -func GetPolicySearchCommand() (cli.Command, error) { +func GetPolicySearchCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounPolicy, cst.Search}, SynopsisText: "policy search ( | (--query | -q) ) [(--limit | -l) ] [--cursor ]", @@ -222,12 +204,10 @@ Usage: `, cst.ExamplePolicySearch), FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("Filter %s of items to fetch (required)", strings.Title(cst.Query))}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePolicySearchCmd(vcli, args) - }, + RunFunc: handlePolicySearchCmd, }) } @@ -512,7 +492,7 @@ func setCidrCondition(policy *defaultPolicy, cidr string) *errors.ApiError { // Wizards: -func handlePolicyCreateWizard(vcli vaultcli.CLI, args []string) int { +func handlePolicyCreateWizard(vcli vaultcli.CLI) int { pathPrompt := &survey.Input{Message: "Path to policy:"} pathValidation := func(ans interface{}) error { answer := strings.TrimSpace(ans.(string)) @@ -553,7 +533,7 @@ func handlePolicyCreateWizard(vcli vaultcli.CLI, args []string) int { return utils.GetExecStatus(apiErr) } -func handlePolicyUpdateWizard(vcli vaultcli.CLI, args []string) int { +func handlePolicyUpdateWizard(vcli vaultcli.CLI) int { var policyAtPath []byte pathPrompt := &survey.Input{Message: "Path to policy:"} pathValidation := func(ans interface{}) error { diff --git a/commands/policy_test.go b/commands/policy_test.go index 2c9d2ed6..9cf65523 100644 --- a/commands/policy_test.go +++ b/commands/policy_test.go @@ -54,7 +54,7 @@ func TestGetPolicyRollbackCmd(t *testing.T) { } func TestGetPolicySearchCommand(t *testing.T) { - _, err := GetPolicySearchCommand() + _, err := GetPolicySearchCmd() assert.Nil(t, err) } diff --git a/commands/pool.go b/commands/pool.go index 092bceb9..6a00c563 100644 --- a/commands/pool.go +++ b/commands/pool.go @@ -40,18 +40,15 @@ func GetPoolCreateCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounPool, cst.Create}, SynopsisText: "Create a new empty pool of engines", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s --%[3]s mypool`, cst.NounPool, cst.Create, cst.DataName), + • pool create --name mypool +`, FlagsPredictor: []*predictor.Params{ {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounPool)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handlePoolCreateWizard(vcli) - } - return handlePoolCreate(vcli, args) - }, + RunFunc: handlePoolCreate, + WizardFunc: handlePoolCreateWizard, }) } @@ -59,16 +56,15 @@ func GetPoolReadCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounPool, cst.Read}, SynopsisText: "Get information on an existing pool of engines", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s --%[3]s mypool`, cst.NounPool, cst.Read, cst.DataName), + • pool read --name mypool +`, FlagsPredictor: []*predictor.Params{ {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounPool)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePoolRead(vcli, args) - }, + RunFunc: handlePoolRead, }) } @@ -76,12 +72,11 @@ func GetPoolListCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounPool, cst.List}, SynopsisText: "List the names of all existing pools", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s`, cst.NounPool, cst.List), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePoolList(vcli, args) - }, + • pool list +`, + RunFunc: handlePoolList, }) } @@ -89,16 +84,15 @@ func GetPoolDeleteCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounPool, cst.Delete}, SynopsisText: "Delete an existing pool of engines", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s --%[3]s mypool`, cst.NounPool, cst.Delete, cst.DataName), + • pool delete --name mypool +`, FlagsPredictor: []*predictor.Params{ {Shorthand: "n", Name: cst.DataName, Usage: fmt.Sprintf("Name of the %s (required)", cst.NounPool)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handlePoolDelete(vcli, args) - }, + RunFunc: handlePoolDelete, }) } diff --git a/commands/report.go b/commands/report.go index d3b9dcf0..ec6e0769 100644 --- a/commands/report.go +++ b/commands/report.go @@ -20,7 +20,8 @@ func GetReportCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounReport}, SynopsisText: "Show report records for secrets and groups", - RunFunc: func(vcli vaultcli.CLI, args []string) int { return cli.RunResultHelp }, + NoConfigRead: true, + NoPreAuth: true, }) } @@ -39,13 +40,11 @@ Usage: {Name: cst.NounUser, Usage: "User name (optional)"}, {Name: cst.NounGroup, Usage: "Group name (optional)"}, {Name: cst.NounRole, Usage: "Role name (optional)"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.OffSet, Usage: "Offset for the next secrets (optional)"}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSecretReport(vcli, args) - }, + RunFunc: handleSecretReport, }) } @@ -61,12 +60,10 @@ Usage: `, cst.NounReport, cst.NounUser, cst.Limit, cst.NounGroup), FlagsPredictor: []*predictor.Params{ {Name: cst.NounUser, Usage: "User name (optional)"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.OffSet, Usage: "Offset for the next groups (optional)"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGroupReport(vcli, args) - }, + RunFunc: handleGroupReport, }) } diff --git a/commands/report_test.go b/commands/report_test.go index 1b199428..5f45b139 100644 --- a/commands/report_test.go +++ b/commands/report_test.go @@ -13,6 +13,21 @@ import ( "github.com/stretchr/testify/assert" ) +func TestGetReportCmd(t *testing.T) { + _, err := GetReportCmd() + assert.Nil(t, err) +} + +func TestGetSecretReportCmd(t *testing.T) { + _, err := GetSecretReportCmd() + assert.Nil(t, err) +} + +func TestGetGroupReportCmd(t *testing.T) { + _, err := GetGroupReportCmd() + assert.Nil(t, err) +} + func TestHandleSecretReport(t *testing.T) { testCase := []struct { name string @@ -66,9 +81,6 @@ func TestHandleSecretReport(t *testing.T) { }, } - _, err := GetSecretReportCmd() - assert.Nil(t, err) - viper.Set(cst.Version, "v1") for _, tt := range testCase { tt := tt @@ -154,9 +166,6 @@ func TestHandleGroupReport(t *testing.T) { }, } - _, err := GetSecretReportCmd() - assert.Nil(t, err) - viper.Set(cst.Version, "v1") for _, tt := range testCase { tt := tt diff --git a/commands/role.go b/commands/role.go index 1f0f2e97..a33fe6d3 100644 --- a/commands/role.go +++ b/commands/role.go @@ -62,9 +62,7 @@ Usage: {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRoleReadCmd(vcli, args) - }, + RunFunc: handleRoleReadCmd, }) } @@ -80,12 +78,10 @@ Usage: `, cst.ProductName, cst.ExampleUserSearch), FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: "Query of roles to fetch (optional)"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRoleSearchCmd(vcli, args) - }, + RunFunc: handleRoleSearchCmd, }) } @@ -104,9 +100,7 @@ Usage: {Name: cst.Force, Usage: "Immediately delete the role", ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRoleDeleteCmd(vcli, args) - }, + RunFunc: handleRoleDeleteCmd, }) } @@ -123,9 +117,7 @@ Usage: {Name: cst.DataName, Shorthand: "n", Usage: "Name of the role"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleRoleRestoreCmd(vcli, args) - }, + RunFunc: handleRoleRestoreCmd, }) } @@ -142,12 +134,8 @@ Usage: {Name: cst.DataName, Shorthand: "n", Usage: "Name of the role (required)"}, {Name: cst.DataDescription, Usage: "Description of the role"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleRoleUpdateWizard(vcli, args) - } - return handleRoleUpdateCmd(vcli, args) - }, + RunFunc: handleRoleUpdateCmd, + WizardFunc: handleRoleUpdateWizard, }) } @@ -166,12 +154,8 @@ Usage: {Name: cst.DataExternalID, Usage: "External Id for the role"}, {Name: cst.DataProvider, Usage: "Provider for the role"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleRoleWorkflow(vcli, args) - } - return handleRoleCreateCmd(vcli, args) - }, + RunFunc: handleRoleCreateCmd, + WizardFunc: handleRoleCreateWizard, }) } @@ -288,7 +272,7 @@ func handleRoleUpdateCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleRoleWorkflow(vcli vaultcli.CLI, args []string) int { +func handleRoleCreateWizard(vcli vaultcli.CLI) int { providers, apiError := listAuthProviders(vcli) if apiError != nil { httpResp := apiError.HttpResponse() @@ -382,7 +366,7 @@ func handleRoleWorkflow(vcli vaultcli.CLI, args []string) int { return utils.GetExecStatus(apiErr) } -func handleRoleUpdateWizard(vcli vaultcli.CLI, args []string) int { +func handleRoleUpdateWizard(vcli vaultcli.CLI) int { var roleName string namePrompt := &survey.Input{Message: "Role name:"} survErr := survey.AskOne(namePrompt, &roleName, survey.WithValidator(vaultcli.SurveyRequired)) diff --git a/commands/secret.go b/commands/secret.go index 9d869364..b15f35b7 100644 --- a/commands/secret.go +++ b/commands/secret.go @@ -42,7 +42,7 @@ func GetSearchOpWrappers() []*predictor.Params { return []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("%s of %ss to fetch (optional)", strings.Title(cst.Query), cst.NounSecret)}, {Name: cst.SearchLinks, Usage: "Find secrets that link to the secret path in the query", ValueType: "bool"}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, {Name: cst.SearchField, Usage: "Advanced search on a secret field (optional)"}, {Name: cst.SearchType, Usage: "Specify the value type for advanced field searching, can be 'number' or 'string' (optional)"}, @@ -88,9 +88,9 @@ Usage: • secret %[1]s --path %[4]s -f data.Data.Key • secret %[1]s --version `, cst.Read, cst.NounSecret, cst.ProductName, cst.ExamplePath), - FlagsPredictor: GetNoDataOpWrappers(cst.NounSecret), - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + FlagsPredictor: GetNoDataOpWrappers(cst.NounSecret), + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretReadCmd(vcli, cst.NounSecret, args) }, @@ -107,9 +107,9 @@ Usage: • secret %[1]s %[4]s • secret %[1]s --path %[4]s -f id `, cst.Describe, cst.NounSecret, cst.ProductName, cst.ExamplePath), - FlagsPredictor: DescribeOpWrappers(cst.NounSecret), - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + FlagsPredictor: DescribeOpWrappers(cst.NounSecret), + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretDescribeCmd(vcli, cst.NounSecret, args) }, @@ -131,8 +131,8 @@ Usage: {Name: cst.ID, Shorthand: "i", Usage: fmt.Sprintf("Target %s for a %s", cst.ID, cst.NounSecret)}, {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounSecret), ValueType: "bool"}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretDeleteCmd(vcli, cst.NounSecret, args) }, @@ -152,8 +152,8 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounSecret), Predictor: predictor.NewSecretPathPredictorDefault()}, {Name: cst.ID, Shorthand: "i", Usage: fmt.Sprintf("Target %s for a %s", cst.ID, cst.NounSecret)}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretRestoreCmd(vcli, cst.NounSecret, args) }, @@ -179,10 +179,11 @@ Usage: {Name: cst.Overwrite, Usage: fmt.Sprintf("Overwrite all the contents of %s data", cst.NounSecret), ValueType: "bool"}, {Name: cst.ID, Shorthand: "i", Usage: fmt.Sprintf("Target %s for a %s", cst.ID, cst.NounSecret)}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretUpsertCmd(vcli, cst.NounSecret, cst.Update, args) }, + WizardFunc: handleSecretUpdateWizard, }) } @@ -201,8 +202,8 @@ Usage: {Name: cst.ID, Shorthand: "i", Usage: fmt.Sprintf("Target %s for a %s", cst.ID, cst.NounSecret)}, {Name: cst.Version, Usage: "The version to which to rollback"}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretRollbackCmd(vcli, cst.NounSecret, args) }, @@ -223,8 +224,8 @@ Usage: {Name: cst.Path, Shorthand: "r", Usage: fmt.Sprintf("Target %s to a %s (required)", cst.Path, cst.NounSecret), Predictor: predictor.NewSecretPathPredictorDefault()}, {Name: cst.ID, Shorthand: "i", Usage: fmt.Sprintf("Target %s for a %s", cst.ID, cst.NounSecret)}, }, - ArgsPredictorFunc: predictor.NewSecretPathPredictorDefault().Predict, - MinNumberArgs: 1, + ArgsPredictor: predictor.NewSecretPathPredictorDefault(), + MinNumberArgs: 1, RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretEditCmd(vcli, cst.NounSecret, args) }, @@ -251,6 +252,7 @@ Usage: RunFunc: func(vcli vaultcli.CLI, args []string) int { return handleSecretUpsertCmd(vcli, cst.NounSecret, cst.Create, args) }, + WizardFunc: handleSecretCreateWizard, }) } @@ -476,21 +478,6 @@ func handleSecretRollbackCmd(vcli vaultcli.CLI, secretType string, args []string } func handleSecretUpsertCmd(vcli vaultcli.CLI, secretType string, action string, args []string) int { - if OnlyGlobalArgs(args) { - var ( - resp []byte - err *errors.ApiError - ) - switch action { - case cst.Create: - resp, err = handleCreateWorkflow(vcli, secretType) - case cst.Update: - resp, err = handleUpdateWorkflow(vcli, secretType) - } - vcli.Out().WriteResponse(resp, err) - return utils.GetExecStatus(err) - } - id := viper.GetString(cst.ID) path := viper.GetString(cst.Path) overwrite := viper.GetBool(cst.Overwrite) @@ -555,7 +542,13 @@ func handleSecretUpsertCmd(vcli vaultcli.CLI, secretType string, action string, return utils.GetExecStatus(err) } -func handleCreateWorkflow(vcli vaultcli.CLI, secretType string) ([]byte, *errors.ApiError) { +func handleSecretCreateWizard(vcli vaultcli.CLI) int { + resp, err := handleGenericSecretCreateWizard(vcli, cst.NounSecret) + vcli.Out().WriteResponse(resp, err) + return utils.GetExecStatus(err) +} + +func handleGenericSecretCreateWizard(vcli vaultcli.CLI, secretType string) ([]byte, *errors.ApiError) { dataMap := make(map[string]interface{}) attrMap := make(map[string]interface{}) @@ -640,7 +633,13 @@ func handleCreateWorkflow(vcli vaultcli.CLI, secretType string) ([]byte, *errors return vcli.HTTPClient().DoRequest(http.MethodPost, uri, &postData) } -func handleUpdateWorkflow(vcli vaultcli.CLI, secretType string) ([]byte, *errors.ApiError) { +func handleSecretUpdateWizard(vcli vaultcli.CLI) int { + resp, err := handleGenericSecretUpdateWizard(vcli, cst.NounSecret) + vcli.Out().WriteResponse(resp, err) + return utils.GetExecStatus(err) +} + +func handleGenericSecretUpdateWizard(vcli vaultcli.CLI, secretType string) ([]byte, *errors.ApiError) { var path string pathPrompt := &survey.Input{Message: "Path:"} survErr := survey.AskOne(pathPrompt, &path, survey.WithValidator(vaultcli.SurveyRequired)) diff --git a/commands/siem.go b/commands/siem.go index 41f75060..55d2b984 100644 --- a/commands/siem.go +++ b/commands/siem.go @@ -40,12 +40,11 @@ func GetSiemCreateCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounSiem, cst.Create}, SynopsisText: "Create a new SIEM endpoint", - HelpText: fmt.Sprintf(` + HelpText: ` Usage: - • %[1]s %[2]s`, cst.NounSiem, cst.Create), - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSiemCreate(vcli, args) - }, + • siem create +`, + RunFunc: handleSiemCreate, }) } @@ -55,14 +54,13 @@ func GetSiemUpdateCmd() (cli.Command, error) { SynopsisText: "Update an existing SIEM endpoint", HelpText: fmt.Sprintf(` Usage: - • %[1]s %[2]s %[4]s - • %[1]s %[2]s --%[3]s %[4]s`, cst.NounSiem, cst.Update, cst.Path, cst.ExampleSIEM), + • siem update %[1]s + • siem update --path %[1]s +`, cst.ExampleSIEM), FlagsPredictor: []*predictor.Params{ {Name: cst.Path, Usage: "Path to existing SIEM"}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSiemUpdate(vcli, args) - }, + RunFunc: handleSiemUpdate, }) } @@ -72,15 +70,14 @@ func GetSiemReadCmd() (cli.Command, error) { SynopsisText: "Read an existing SIEM endpoint", HelpText: fmt.Sprintf(` Usage: - • %[1]s %[2]s %[4]s - • %[1]s %[2]s --%[3]s %[4]s`, cst.NounSiem, cst.Read, cst.Path, cst.ExampleSIEM), + • siem read %[1]s + • siem read --path %[1]s +`, cst.ExampleSIEM), FlagsPredictor: []*predictor.Params{ {Name: cst.Path, Usage: "Path to existing SIEM"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSiemRead(vcli, args) - }, + RunFunc: handleSiemRead, }) } @@ -90,19 +87,18 @@ func GetSiemDeleteCmd() (cli.Command, error) { SynopsisText: "Delete an existing SIEM endpoint", HelpText: fmt.Sprintf(` Usage: - • %[1]s %[2]s %[4]s - • %[1]s %[2]s --%[3]s %[4]s`, cst.NounSiem, cst.Delete, cst.Path, cst.ExampleSIEM), + • siem delete %[1]s + • siem delete --path %[1]s +`, cst.ExampleSIEM), FlagsPredictor: []*predictor.Params{ {Name: cst.Path, Usage: "Path to existing SIEM"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSiemDelete(vcli, args) - }, + RunFunc: handleSiemDelete, }) } -func GetSiemSearchCommand() (cli.Command, error) { +func GetSiemSearchCmd() (cli.Command, error) { return NewCommand(CommandArgs{ Path: []string{cst.NounSiem, cst.Search}, SynopsisText: `Search for SIEM endpoints`, @@ -112,12 +108,10 @@ func GetSiemSearchCommand() (cli.Command, error) { `, cst.NounSiem, cst.Search, cst.ExampleSiemSearch), FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("Filter %s of items to fetch (required)", cst.Query)}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleSiemSearchCmd(vcli, args) - }, + RunFunc: handleSiemSearchCmd, }) } diff --git a/commands/siem_test.go b/commands/siem_test.go index 5a0000f6..9cc3b3ec 100644 --- a/commands/siem_test.go +++ b/commands/siem_test.go @@ -30,3 +30,8 @@ func TestGetSiemDeleteCmd(t *testing.T) { _, err := GetSiemDeleteCmd() assert.Nil(t, err) } + +func TestGetSiemSearchCmd(t *testing.T) { + _, err := GetSiemSearchCmd() + assert.Nil(t, err) +} diff --git a/commands/usage.go b/commands/usage.go index e78820d7..91614609 100644 --- a/commands/usage.go +++ b/commands/usage.go @@ -27,9 +27,7 @@ func GetUsageCmd() (cli.Command, error) { {Name: cst.EndDate, Usage: "End date to which to fetch usage data (optional)"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleGetUsageCmd(vcli, args) - }, + RunFunc: handleGetUsageCmd, }) } diff --git a/commands/usage_test.go b/commands/usage_test.go index d56d0ca5..d71f20c4 100644 --- a/commands/usage_test.go +++ b/commands/usage_test.go @@ -15,6 +15,11 @@ import ( "github.com/stretchr/testify/assert" ) +func TestGetUsageCmd(t *testing.T) { + _, err := GetUsageCmd() + assert.Nil(t, err) +} + func TestHandleGetUsageCmd(t *testing.T) { today := fmt.Sprintf("--%s=%s", cst.StartDate, time.Now().Format("2006-01-02")) cases := []struct { @@ -38,9 +43,6 @@ func TestHandleGetUsageCmd(t *testing.T) { }, } - _, err := GetUsageCmd() - assert.Nil(t, err) - viper.Set(cst.Version, "v1") for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { diff --git a/commands/user.go b/commands/user.go index f0b60ada..553d824f 100644 --- a/commands/user.go +++ b/commands/user.go @@ -67,9 +67,7 @@ Usage: {Name: cst.Version, Usage: "List the current and last (n) versions"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUserReadCmd(vcli, args) - }, + RunFunc: handleUserReadCmd, }) } @@ -85,12 +83,10 @@ Usage: `, cst.NounUser, cst.ProductName, cst.ExampleUserSearch), FlagsPredictor: []*predictor.Params{ {Name: cst.Query, Shorthand: "q", Usage: fmt.Sprintf("%s of %ss to fetch (optional)", strings.Title(cst.Query), cst.NounUser)}, - {Name: cst.Limit, Shorthand: "l", Usage: "Maximum number of results per cursor (optional)"}, + {Name: cst.Limit, Shorthand: "l", Usage: cst.LimitHelpMessage}, {Name: cst.Cursor, Usage: cst.CursorHelpMessage}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUserSearchCmd(vcli, args) - }, + RunFunc: handleUserSearchCmd, }) } @@ -109,9 +105,7 @@ Usage: {Name: cst.Force, Usage: fmt.Sprintf("Immediately delete %s", cst.NounUser), ValueType: "bool"}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUserDeleteCmd(vcli, args) - }, + RunFunc: handleUserDeleteCmd, }) } @@ -128,9 +122,7 @@ Usage: {Name: cst.DataUsername, Usage: fmt.Sprintf("%s of %s to fetch (required)", strings.Title(cst.DataUsername), cst.NounUser)}, }, MinNumberArgs: 1, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - return handleUserRestoreCmd(vcli, args) - }, + RunFunc: handleUserRestoreCmd, }) } @@ -151,12 +143,8 @@ Usage: {Name: cst.DataExternalID, Usage: fmt.Sprintf("%s of %s to be updated", strings.Title(strings.Replace(cst.DataExternalID, ".", " ", -1)), cst.NounUser)}, {Name: cst.DataProvider, Usage: fmt.Sprintf("External %s of %s to be updated", strings.Title(cst.DataProvider), cst.NounUser)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleUserCreateWorkflow(vcli, args) - } - return handleUserCreateCmd(vcli, args) - }, + RunFunc: handleUserCreateCmd, + WizardFunc: handleUserCreateWizard, }) } @@ -174,12 +162,8 @@ Usage: {Name: cst.DataUsername, Usage: fmt.Sprintf("%s of %s to be updated (required)", strings.Title(cst.DataUsername), cst.NounUser)}, {Name: cst.DataDisplayname, Usage: fmt.Sprintf("%s of %s to be updated", strings.Title(cst.DataDisplayname), cst.NounUser)}, }, - RunFunc: func(vcli vaultcli.CLI, args []string) int { - if OnlyGlobalArgs(args) { - return handleUserUpdateWorkflow(vcli, args) - } - return handleUserUpdateCmd(vcli, args) - }, + RunFunc: handleUserUpdateCmd, + WizardFunc: handleUserUpdateWizard, }) } @@ -320,7 +304,7 @@ func handleUserUpdateCmd(vcli vaultcli.CLI, args []string) int { // Wizards: -func handleUserCreateWorkflow(vcli vaultcli.CLI, args []string) int { +func handleUserCreateWizard(vcli vaultcli.CLI) int { providers, apiError := listAuthProviders(vcli) if apiError != nil { httpResp := apiError.HttpResponse() @@ -418,7 +402,7 @@ func handleUserCreateWorkflow(vcli vaultcli.CLI, args []string) int { return utils.GetExecStatus(apiError) } -func handleUserUpdateWorkflow(vcli vaultcli.CLI, args []string) int { +func handleUserUpdateWizard(vcli vaultcli.CLI) int { var username string usernamePrompt := &survey.Input{Message: "Username:"} survErr := survey.AskOne(usernamePrompt, &username, survey.WithValidator(vaultcli.SurveyRequired)) diff --git a/constants/commands.go b/constants/commands.go index d5df5711..62fea181 100644 --- a/constants/commands.go +++ b/constants/commands.go @@ -188,6 +188,7 @@ const ( // Common Help Messages const ( CursorHelpMessage = "Next cursor for additional results. The cursor is provided at the end of each body response (\"cursor\": \"MQ==\") (optional)" + LimitHelpMessage = "Maximum number of results per cursor (optional)" ) // Security diff --git a/constants/config.go b/constants/config.go index b0135518..46994d16 100644 --- a/constants/config.go +++ b/constants/config.go @@ -2,11 +2,9 @@ package constants // Configuration-related constants. const ( - CmdRoot = "dsv" - EnvVarPrefix = "thy" - CmdFilePrefix = "@" - DontAutocompleteGlobals = false - DefaultProfile = "default" - DefaultThyOneName = "thy-one" - DefaultCallback = "localhost:8072" + CmdRoot = "dsv" + CmdFilePrefix = "@" + DefaultProfile = "default" + DefaultThyOneName = "thy-one" + DefaultCallback = "localhost:8072" ) diff --git a/constants/doc.go b/constants/doc.go index 5bcca7fa..47123928 100644 --- a/constants/doc.go +++ b/constants/doc.go @@ -9,7 +9,6 @@ const ( ExamplePath = "databases/mongo-db01" ExamplePolicyPath = "secrets/databases/mongo-db01" ExampleRoleName = "gcp-svc-1" - ExampleAuthProviderName = "aws-dev" ExampleDataJSON = `'{"Key":"Value","Key2":"Value2"}'` ExampleDataPath = "@/tmp/data.json" ExampleConfigPath = "@/tmp/config.yml" @@ -27,5 +26,4 @@ const ( ExampleGroup = "administrators" ExampleGroupCreate = `{"groupName": "administrators"}` ExampleGroupAddMembers = `{"members": ["member1","member2"]}` - GCPNote = `GCP GCE metadata auth provider can be created in the command line, but a GCP Service Account must be done using a file. See the Authentication:GCP portion of the documentation.` ) diff --git a/internal/predictor/auth_provider_type_predictor.go b/internal/predictor/auth_provider_type_predictor.go new file mode 100644 index 00000000..8e4aed92 --- /dev/null +++ b/internal/predictor/auth_provider_type_predictor.go @@ -0,0 +1,11 @@ +package predictor + +import ( + "github.com/posener/complete" +) + +type AuthProviderTypePredictor struct{} + +func (p AuthProviderTypePredictor) Predict(a complete.Args) (prediction []string) { + return []string{"azure", "aws", "gcp", "thycoticone"} +} diff --git a/internal/predictor/wrappers.go b/internal/predictor/wrappers.go index 04288df4..a68c307b 100644 --- a/internal/predictor/wrappers.go +++ b/internal/predictor/wrappers.go @@ -26,7 +26,6 @@ type Wrapper struct { type FlagValue struct { Val string FlagType string - Name string DefaultValue string } @@ -66,42 +65,38 @@ func New(params *Params) *Wrapper { w.Val = f.Value.(*FlagValue) } else { - fv := FlagValue{ + w.Val = &FlagValue{ FlagType: params.ValueType, - Name: params.Name, DefaultValue: params.Default, } - w.Val = &fv - f := flag.CommandLine.VarPF(&fv, cmdFriendlyName, params.Shorthand, params.Usage) + flag.VarP(w.Val, cmdFriendlyName, params.Shorthand, params.Usage) if params.ValueType == "bool" { - f.NoOptDefVal = "true" + flag.Lookup(cmdFriendlyName).NoOptDefVal = "true" } } + if w.Hidden { + flag.CommandLine.MarkHidden(cmdFriendlyName) + } + return w } -func (f *FlagValue) Set(v string) error { +func (f *FlagValue) Set(value string) error { if f.FlagType == "" || f.FlagType == "string" { - if v != "" && len(v) > 1 && strings.HasPrefix(v, "@") { + if len(value) > 1 && strings.HasPrefix(value, "@") { f.FlagType = "file" - fname := v[1:] - if b, err := os.ReadFile(fname); err != nil { + fname := value[1:] + b, err := os.ReadFile(fname) + if err != nil { return err - } else { - f.Val = string(b) - return nil } + value = string(b) } } - f.Val = v + f.Val = value return nil } -func (f *FlagValue) Type() string { - return f.FlagType -} - -func (f *FlagValue) String() string { - return f.Val -} +func (f *FlagValue) Type() string { return f.FlagType } +func (f *FlagValue) String() string { return f.Val } diff --git a/main.go b/main.go index fa6884d4..1da0b9a0 100644 --- a/main.go +++ b/main.go @@ -1,19 +1,15 @@ package main import ( - "errors" - "fmt" "io" "log" "os" "runtime/debug" - "strings" cmd "thy/commands" cst "thy/constants" "thy/format" "thy/utils" - "thy/vaultcli" "thy/version" "github.com/mitchellh/cli" @@ -62,7 +58,7 @@ func runCLI(args []string) (exitStatus int, err error) { "secret bustcache": cmd.GetSecretBustCacheCmd, "policy": cmd.GetPolicyCmd, "policy read": cmd.GetPolicyReadCmd, - "policy search": cmd.GetPolicySearchCommand, + "policy search": cmd.GetPolicySearchCmd, "policy delete": cmd.GetPolicyDeleteCmd, "policy restore": cmd.GetPolicyRestoreCmd, "policy create": cmd.GetPolicyCreateCmd, @@ -137,7 +133,7 @@ func runCLI(args []string) (exitStatus int, err error) { "siem create": cmd.GetSiemCreateCmd, "siem update": cmd.GetSiemUpdateCmd, "siem read": cmd.GetSiemReadCmd, - "siem search": cmd.GetSiemSearchCommand, + "siem search": cmd.GetSiemSearchCmd, "siem delete": cmd.GetSiemDeleteCmd, "home": cmd.GetHomeCmd, "home read": cmd.GetHomeReadCmd, @@ -198,24 +194,5 @@ func runCLI(args []string) (exitStatus int, err error) { c.AutocompleteUninstall = "uninstall" log.SetOutput(io.Discard) - if !cmd.IsInit(c.Args) && !cmd.IsInstall(c.Args) { - // TODO : We do this twice to support autocomplete. Investigate how to only do once instead - - cfgFile := vaultcli.GetFlagVal(cst.Config, c.Args) - profile := strings.ToLower(vaultcli.GetFlagVal(cst.Profile, c.Args)) - tenant := vaultcli.GetFlagVal(cst.Tenant, args) - - err := vaultcli.ViperInit(cfgFile, profile, c.Args) - if tenant == "" { - if errors.Is(err, vaultcli.ErrFileNotFound) { - // Do not return the error to allow users to view help text for commands. - out := format.NewDefaultOutClient() - out.FailF("Create CLI config file manually or execute command '%s init' to initiate CLI configuration - cannot find config.", cst.CmdRoot) - } else if err != nil { - return utils.GetExecStatus(err), fmt.Errorf("Error: %v.", err) - } - } - } - return c.Run() } diff --git a/utils/errors.go b/utils/errors.go index 26f84e72..c5d9b72f 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -1,20 +1,5 @@ package utils -import ( - "strings" - - "thy/errors" -) - -func NewMissingArgError(arg ...string) *errors.ApiError { - flagified := []string{} - for _, s := range arg { - flagified = append(flagified, "--"+s) - } - joinedArgs := strings.Join(flagified, " or ") - return errors.NewF("%s must be set", strings.ToLower(joinedArgs)) -} - func GetExecStatus(err error) int { if err == nil || err.Error() == "" { return 0 diff --git a/vaultcli/args.go b/vaultcli/args.go index d108af3e..d14bebea 100644 --- a/vaultcli/args.go +++ b/vaultcli/args.go @@ -1,7 +1,6 @@ package vaultcli import ( - "os" "strings" cst "thy/constants" @@ -32,11 +31,7 @@ func GetFlagVal(flag string, args []string) string { } } - envKey := cst.CmdRoot + "_" + flag - envKey = strings.ReplaceAll(envKey, ".", "_") - envKey = strings.ReplaceAll(envKey, "-", "_") - envKey = strings.ToUpper(envKey) - return os.Getenv(envKey) + return "" } // GetFilenameFromArgs tries to extract a filename from args. If args has a --data or -d flag and diff --git a/vaultcli/config.go b/vaultcli/config.go index c6df7cc1..3cc4889c 100644 --- a/vaultcli/config.go +++ b/vaultcli/config.go @@ -12,7 +12,6 @@ import ( cst "thy/constants" "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" "gopkg.in/yaml.v3" ) @@ -64,41 +63,6 @@ type configFileFormatV2 struct { Profiles map[string]map[string]interface{} `yaml:"profiles"` } -func ViperInit(cfgFile string, profile string, args []string) error { - viper.SetEnvPrefix(cst.EnvVarPrefix) - envReplacer := strings.NewReplacer(".", "_") - viper.SetEnvKeyReplacer(envReplacer) - viper.AutomaticEnv() - - cf, err := ReadConfigFile(cfgFile) - if err != nil { - return err - } - - if profile == "" { - profile = viper.GetString(cst.Profile) - if profile == "" { - profile = cf.DefaultProfile - } - } - - // Set profile name to lower case globally. - profile = strings.ToLower(profile) - viper.Set(cst.Profile, profile) - - config, ok := cf.GetProfile(profile) - if !ok { - return fmt.Errorf("profile %q not found in configuration file %q", profile, cf.GetPath()) - } - - err = viper.MergeConfigMap(config.data) - if err != nil { - return fmt.Errorf("cannot initialize Viper: %w", err) - } - - return nil -} - func ReadConfigFile(path string) (*ConfigFile, error) { cf, err := NewConfigFile(path) if err != nil { diff --git a/vaultcli/viper.go b/vaultcli/viper.go new file mode 100644 index 00000000..3e5e4e1e --- /dev/null +++ b/vaultcli/viper.go @@ -0,0 +1,46 @@ +package vaultcli + +import ( + "fmt" + "strings" + cst "thy/constants" + + "github.com/spf13/viper" +) + +const envVarPrefix = "thy" + +func ViperInit() error { + viper.SetEnvPrefix(envVarPrefix) + envReplacer := strings.NewReplacer(".", "_") + viper.SetEnvKeyReplacer(envReplacer) + viper.AutomaticEnv() + + cfgFile := viper.GetString(cst.Config) + + cf, err := ReadConfigFile(cfgFile) + if err != nil { + return err + } + + profile := viper.GetString(cst.Profile) + if profile == "" { + profile = cf.DefaultProfile + } + + // Set profile name to lower case globally. + profile = strings.ToLower(profile) + viper.Set(cst.Profile, profile) + + config, ok := cf.GetProfile(profile) + if !ok { + return fmt.Errorf("profile %q not found in configuration file %q", profile, cf.GetPath()) + } + + err = viper.MergeConfigMap(config.data) + if err != nil { + return fmt.Errorf("cannot initialize Viper: %w", err) + } + + return nil +}