diff --git a/command/ca/admin/admin.go b/command/ca/admin/admin.go index 73944753e..fb5ccfc11 100644 --- a/command/ca/admin/admin.go +++ b/command/ca/admin/admin.go @@ -142,6 +142,7 @@ func adminPrompt(ctx *cli.Context, client *ca.AdminClient, admins []*linkedca.Ad } i, _, err := ui.Select("Select an admin:", items, + ui.WithField("admin", "admin-subject"), ui.WithSelectTemplates(ui.NamedSelectTemplates("Admin"))) if err != nil { return nil, err diff --git a/command/ca/init.go b/command/ca/init.go index 52871389a..71299ba8f 100644 --- a/command/ca/init.go +++ b/command/ca/init.go @@ -325,60 +325,72 @@ func initAction(ctx *cli.Context) (err error) { iss := ctx.String("issuer") if iss == "" { - create, err = ui.PromptYesNo("Would you like to create a new PKI (y) or use an existing one (n)?") + create, err = ui.PromptYesNo("Would you like to create a new PKI (y) or use an existing one (n)?", + ui.WithField("PKI creation choice", "")) if err != nil { return err } if create { ui.Println("What would you like to name your new PKI?", ui.WithValue(ctx.String("name"))) name, err = ui.Prompt("(e.g. Smallstep)", + ui.WithField("PKI name", "name"), ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("name"))) if err != nil { return err } ui.Println("What is the name of your organization?") org, err = ui.Prompt("(e.g. Smallstep)", + ui.WithField("organization name", ""), ui.WithValidateNotEmpty()) if err != nil { return err } ui.Println("What resource id do you want to use? [we will append -Root-CA or -Intermediate-CA]") resource, err = ui.Prompt("(e.g. Smallstep)", + ui.WithField("resource ID", ""), ui.WithValidateRegexp("^[a-zA-Z0-9-_]+$")) if err != nil { return err } ui.Println("What is the id of your project on Google's Cloud Platform?") project, err = ui.Prompt("(e.g. smallstep-ca)", + ui.WithField("GCP project ID", ""), ui.WithValidateRegexp("^[a-z][a-z0-9-]{4,28}[a-z0-9]$")) if err != nil { return err } ui.Println("What region or location do you want to use?") location, err = ui.Prompt("(e.g. us-west1)", + ui.WithField("GCP location", ""), ui.WithValidateRegexp("^[a-z0-9-]+$")) if err != nil { return err } ui.Println("What CA pool name do you want to use?") caPool, err = ui.Prompt("(e.g. Smallstep)", + ui.WithField("CA pool name", ""), ui.WithValidateRegexp("^[a-zA-Z0-9_-]{1,63}")) if err != nil { return err } - i, _, err := ui.Select("What CA pool tier do you want to use?", caPoolTiers, ui.WithSelectTemplates(ui.NamedSelectTemplates("Tier"))) + i, _, err := ui.Select("What CA pool tier do you want to use?", caPoolTiers, + ui.WithField("CA pool tier", ""), + ui.WithSelectTemplates(ui.NamedSelectTemplates("Tier"))) if err != nil { return err } caPoolTier = caPoolTiers[i].Value ui.Println("What GCS bucket do you want to use? Leave it empty to use a managed one.") - gcsBucket, err = ui.Prompt("(e.g. my-bucket)", ui.WithValidateRegexp("(^$)|(^[a-z0-9._-]{3,222}$)")) + gcsBucket, err = ui.Prompt("(e.g. my-bucket)", + ui.WithField("GCS bucket", ""), + ui.WithValidateRegexp("(^$)|(^[a-z0-9._-]{3,222}$)")) if err != nil { return err } } else { ui.Println("What certificate authority would you like to use?") iss, err = ui.Prompt("(e.g. projects/smallstep-ca/locations/us-west1/caPools/smallstep/certificateAuthorities/intermediate-ca)", + ui.WithField("certificate authority", "issuer"), ui.WithValidateRegexp("^projects/[a-z][a-z0-9-]{4,28}[a-z0-9]/locations/[a-z0-9-]+/caPools/[a-zA-Z0-9-_]+/certificateAuthorities/[a-zA-Z0-9-_]+$")) if err != nil { return err @@ -403,18 +415,21 @@ func initAction(ctx *cli.Context) (err error) { } ui.Println("What is the url of your CA?", ui.WithValue(ctx.String("issuer"))) ca, err := ui.Prompt("(e.g. https://ca.smallstep.com:9000)", + ui.WithField("CA URL", "issuer"), ui.WithValidateRegexp("(?i)^https://.+$"), ui.WithValue(ctx.String("issuer"))) if err != nil { return err } ui.Println("What is the fingerprint of the CA's root file?", ui.WithValue(ctx.String("issuer-fingerprint"))) fingerprint, err := ui.Prompt("(e.g. 4fe5f5ef09e95c803fdcb80b8cf511e2a885eb86f3ce74e3e90e62fa3faf1531)", + ui.WithField("root certificate fingerprint", "issuer-fingerprint"), ui.WithValidateRegexp("^[a-fA-F0-9]{64}$"), ui.WithValue(ctx.String("issuer-fingerprint"))) if err != nil { return err } ui.Println("What is the JWK provisioner you want to use?", ui.WithValue(ctx.String("issuer-provisioner"))) provisioner, err := ui.Prompt("(e.g. you@smallstep.com)", + ui.WithField("issuer provisioner name", "issuer-provisioner"), ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("issuer-provisioner"))) if err != nil { return err @@ -454,7 +469,9 @@ func initAction(ctx *cli.Context) (err error) { } ui.Println("What would you like to name your new PKI?", ui.WithValue(ctx.String("name"))) - name, err = ui.Prompt("(e.g. Smallstep)", ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("name"))) + name, err = ui.Prompt("(e.g. Smallstep)", + ui.WithField("PKI name", "name"), + ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("name"))) if err != nil { return err } @@ -483,6 +500,7 @@ func initAction(ctx *cli.Context) (err error) { if rootKey == nil { ui.Println("What URI would you like to use for the root certificate key?", ui.WithValue(ctx.String("kms-root"))) rootURI, err = ui.Prompt("(e.g. azurekms:name=my-root-key;vault=my-vault)", + ui.WithField("root key URI", "kms-root"), ui.WithValidateFunc(validateFunc), ui.WithValue(ctx.String("kms-root"))) if err != nil { return err @@ -491,6 +509,7 @@ func initAction(ctx *cli.Context) (err error) { ui.Println("What URI would you like to use for the intermediate certificate key?", ui.WithValue(ctx.String("kms-intermediate"))) intermediateURI, err = ui.Prompt("(e.g. azurekms:name=my-intermediate-key;vault=my-vault)", + ui.WithField("intermediate key URI", "kms-intermediate"), ui.WithValidateFunc(validateFunc), ui.WithValue(ctx.String("kms-intermediate"))) if err != nil { return err @@ -499,6 +518,7 @@ func initAction(ctx *cli.Context) (err error) { if ctx.Bool("ssh") { ui.Println("What URI would you like to use for the SSH host key?", ui.WithValue(ctx.String("kms-ssh-host"))) sshHostURI, err = ui.Prompt("(e.g. azurekms:name=my-host-key;vault=my-vault)", + ui.WithField("SSH host key URI", "kms-ssh-host"), ui.WithValidateFunc(validateFunc), ui.WithValue(ctx.String("kms-ssh-host"))) if err != nil { return err @@ -506,6 +526,7 @@ func initAction(ctx *cli.Context) (err error) { ui.Println("What URI would you like to use for the SSH user key?", ui.WithValue(ctx.String("kms-ssh-user"))) sshUserURI, err = ui.Prompt("(e.g. azurekms:name=my-user-key;vault=my-vault)", + ui.WithField("SSH user key URI", "kms-ssh-user"), ui.WithValidateFunc(validateFunc), ui.WithValue(ctx.String("kms-ssh-user"))) if err != nil { return err @@ -533,6 +554,7 @@ func initAction(ctx *cli.Context) (err error) { ui.Println("What DNS names or IP addresses will clients use to reach your CA?", ui.WithSliceValue(ctx.StringSlice("dns"))) dnsValue, err := ui.Prompt("(e.g. ca.example.com[,10.1.2.3,etc.])", + ui.WithField("DNS names or IP addresses", "dns"), ui.WithSliceValue(ctx.StringSlice("dns"))) if err != nil { return err @@ -576,6 +598,7 @@ func initAction(ctx *cli.Context) (err error) { ui.Println("What IP and port will your new CA bind to? (:443 will bind to 0.0.0.0:443)", ui.WithValue(ctx.String("address"))) } address, err = ui.Prompt("(e.g. :443 or 127.0.0.1:443)", + ui.WithField("listen address", "address"), ui.WithValidateFunc(ui.Address()), ui.WithValue(ctx.String("address"))) if err != nil { return err @@ -588,6 +611,7 @@ func initAction(ctx *cli.Context) (err error) { if deploymentType == pki.StandaloneDeployment { ui.Println("What would you like to name the CA's first provisioner?", ui.WithValue(ctx.String("provisioner"))) provisioner, err = ui.Prompt("(e.g. you@smallstep.com)", + ui.WithField("provisioner name", "provisioner"), ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("provisioner"))) if err != nil { return err @@ -651,7 +675,9 @@ func initAction(ctx *cli.Context) (err error) { return err } - pass, err := ui.PromptPasswordGenerate("[leave empty and we'll generate one]", ui.WithRichPrompt(), ui.WithValue(password)) + pass, err := ui.PromptPasswordGenerate("[leave empty and we'll generate one]", + ui.WithField("password", "password-file"), + ui.WithRichPrompt(), ui.WithValue(password)) if err != nil { return err } @@ -800,6 +826,7 @@ func promptDeploymentType(ctx *cli.Context, isRA bool) (pki.DeploymentType, erro } i, _, err := ui.Select("What deployment type would you like to configure?", deploymentTypes, + ui.WithField("deployment type", "deployment-type"), ui.WithSelectTemplates(&promptui.SelectTemplates{ Active: fmt.Sprintf("%s {{ printf \"%%s - %%s\" .Name .Description | underline }}", ui.IconSelect), Inactive: " {{ .Name }} - {{ .Description }}", diff --git a/command/ca/provisioner/add.go b/command/ca/provisioner/add.go index 0c18b50ca..0a35abdba 100644 --- a/command/ca/provisioner/add.go +++ b/command/ca/provisioner/add.go @@ -491,7 +491,9 @@ func createJWKDetails(ctx *cli.Context) (*linkedca.ProvisionerDetails, error) { if ctx.IsSet("private-key") { return nil, errs.IncompatibleFlag(ctx, "create", "private-key") } - pass, err := ui.PromptPasswordGenerate("Please enter a password to encrypt the provisioner private key? [leave empty and we'll generate one]", ui.WithValue(password)) + pass, err := ui.PromptPasswordGenerate("Please enter a password to encrypt the provisioner private key? [leave empty and we'll generate one]", + ui.WithField("provisioner private key password", "provisioner-password-file"), + ui.WithValue(password)) if err != nil { return nil, err } diff --git a/command/ca/provisioner/update.go b/command/ca/provisioner/update.go index 98f6cabe0..1a4e5fbe1 100644 --- a/command/ca/provisioner/update.go +++ b/command/ca/provisioner/update.go @@ -542,7 +542,9 @@ func updateJWKDetails(ctx *cli.Context, p *linkedca.Provisioner) error { if ctx.IsSet("private-key") { return errs.IncompatibleFlag(ctx, "create", "private-key") } - pass, err := ui.PromptPasswordGenerate("Please enter a password to encrypt the provisioner private key? [leave empty and we'll generate one]", ui.WithValue(password)) + pass, err := ui.PromptPasswordGenerate("Please enter a password to encrypt the provisioner private key? [leave empty and we'll generate one]", + ui.WithField("provisioner private key password", "provisioner-password-file"), + ui.WithValue(password)) if err != nil { return err } diff --git a/command/ca/revoke.go b/command/ca/revoke.go index 2d5415eed..b2433bb36 100644 --- a/command/ca/revoke.go +++ b/command/ca/revoke.go @@ -396,7 +396,9 @@ func (f *revokeFlow) GenerateToken(ctx *cli.Context, subject *string) (string, e } if *subject == "" { - *subject, err = ui.Prompt("What is the Serial Number of the certificate you would like to revoke? (`step certificate inspect foo.cert`)", ui.WithValidateNotEmpty()) + *subject, err = ui.Prompt("What is the Serial Number of the certificate you would like to revoke? (`step certificate inspect foo.cert`)", + ui.WithField("certificate serial number", "serial"), + ui.WithValidateNotEmpty()) if err != nil { return "", err } diff --git a/command/certificate/create.go b/command/certificate/create.go index ed33493f1..82a30fb22 100644 --- a/command/certificate/create.go +++ b/command/certificate/create.go @@ -914,6 +914,7 @@ func savePrivateKey(ctx *cli.Context, filename string, priv interface{}, insecur } } else { pass, err = ui.PromptPassword("Please enter the password to encrypt the private key", + ui.WithField("private key password", "password-file"), ui.WithValidateNotEmpty()) if err != nil { return errors.Wrap(err, "error reading password") diff --git a/command/certificate/p12.go b/command/certificate/p12.go index 2f6f53b25..673509707 100644 --- a/command/certificate/p12.go +++ b/command/certificate/p12.go @@ -144,7 +144,8 @@ func p12Action(ctx *cli.Context) error { } if password == "" { - pass, err := ui.PromptPassword("Please enter a password to encrypt the .p12 file") + pass, err := ui.PromptPassword("Please enter a password to encrypt the .p12 file", + ui.WithField("PKCS#12 password", "password-file")) if err != nil { return errors.Wrap(err, "error reading password") } diff --git a/command/certificate/sign.go b/command/certificate/sign.go index 628a391cf..892fe27e6 100644 --- a/command/certificate/sign.go +++ b/command/certificate/sign.go @@ -259,7 +259,7 @@ func signAction(ctx *cli.Context) error { opts = append(opts, pemutil.WithPasswordPrompt( fmt.Sprintf("Please enter the password to decrypt %s", keyFile), func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("CA key password", "password-file")) })) } else { opts = append(opts, pemutil.WithPasswordFile(passFile)) diff --git a/command/context/remove.go b/command/context/remove.go index 9aa4547b3..cd6c17f0c 100644 --- a/command/context/remove.go +++ b/command/context/remove.go @@ -92,7 +92,8 @@ func removeAction(ctx *cli.Context) error { } ui.Println() - if ok, err := ui.PromptYesNo(fmt.Sprintf("Are you sure you want to delete the configuration for context %s (this cannot be undone!) [y/n]", name)); err != nil { + if ok, err := ui.PromptYesNo(fmt.Sprintf("Are you sure you want to delete the configuration for context %s (this cannot be undone!) [y/n]", name), + ui.WithField("deletion confirmation", "")); err != nil { return err } else if !ok { return errors.New("context not removed") diff --git a/command/crypto/change-pass.go b/command/crypto/change-pass.go index 9fe8ea140..a0f129f3b 100644 --- a/command/crypto/change-pass.go +++ b/command/crypto/change-pass.go @@ -129,7 +129,8 @@ func changePassAction(ctx *cli.Context) error { if encryptPassFile != "" { opts = append(opts, pemutil.WithPasswordFile(encryptPassFile)) } else { - pass, err := ui.PromptPassword(fmt.Sprintf("Please enter the password to encrypt %s", newKeyPath)) + pass, err := ui.PromptPassword(fmt.Sprintf("Please enter the password to encrypt %s", newKeyPath), + ui.WithField("new password", "password-file")) if err != nil { return errors.Wrap(err, "error reading password") } @@ -156,7 +157,7 @@ func changePassAction(ctx *cli.Context) error { if !noPass { opts = []jose.Option{ jose.WithPasswordPrompter("Please enter the password to encrypt the private JWK", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("current password", "password-file")) }), } if encryptPassFile != "" { diff --git a/command/crypto/jwe/decrypt.go b/command/crypto/jwe/decrypt.go index 8d11e91c4..c41249f80 100644 --- a/command/crypto/jwe/decrypt.go +++ b/command/crypto/jwe/decrypt.go @@ -123,6 +123,7 @@ func decryptAction(ctx *cli.Context) error { pbes2Key, err = ui.PromptPassword( "Please enter the password to decrypt the content encryption key", + ui.WithField("decryption password", "password-file"), ui.WithValue(password)) default: return errs.RequiredOrFlag(ctx, "key", "jwk") diff --git a/command/crypto/jwe/encrypt.go b/command/crypto/jwe/encrypt.go index a5e73beb2..a4dc0bed0 100644 --- a/command/crypto/jwe/encrypt.go +++ b/command/crypto/jwe/encrypt.go @@ -224,7 +224,8 @@ func encryptAction(ctx *cli.Context) error { case jwks != "": jwk, err = jose.ReadKeySet(jwks, options...) case isPBES2: - pbes2Key, err = ui.PromptPassword("Please enter the password to encrypt the content encryption key") + pbes2Key, err = ui.PromptPassword("Please enter the password to encrypt the content encryption key", + ui.WithField("encryption password", "password-file")) default: return errs.RequiredOrFlag(ctx, "key", "jwks") } diff --git a/command/crypto/jwk/create.go b/command/crypto/jwk/create.go index af4bb9925..7924c914b 100644 --- a/command/crypto/jwk/create.go +++ b/command/crypto/jwk/create.go @@ -533,7 +533,9 @@ func createAction(ctx *cli.Context) (err error) { // Generate JWE encryption key. if jose.SupportsPBKDF2 { var key []byte - key, err = ui.PromptPassword("Please enter the password to encrypt the private JWK", ui.WithValue(password)) + key, err = ui.PromptPassword("Please enter the password to encrypt the private JWK", + ui.WithField("JWK encryption password", "password-file"), + ui.WithValue(password)) if err != nil { return errors.Wrap(err, "error reading password") } diff --git a/command/crypto/jwk/keyset.go b/command/crypto/jwk/keyset.go index a0e4aa8a6..1eaa22441 100644 --- a/command/crypto/jwk/keyset.go +++ b/command/crypto/jwk/keyset.go @@ -133,7 +133,7 @@ func keysetAddAction(ctx *cli.Context) error { // Attempt to parse an encrypted file if b, err = jose.Decrypt(b, jose.WithPasswordPrompter("Please enter the password to decrypt JWK", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("password", "password-file")) })); err != nil { return err } diff --git a/command/crypto/jwk/public.go b/command/crypto/jwk/public.go index 349837ffd..f94bf16f6 100644 --- a/command/crypto/jwk/public.go +++ b/command/crypto/jwk/public.go @@ -35,7 +35,7 @@ func publicAction(*cli.Context) error { jwk := new(jose.JSONWebKey) // Attempt to decrypt if encrypted if b, err = jose.Decrypt(b, jose.WithPasswordPrompter("Please enter the password to decrypt your private JWK", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("password", "password-file")) })); err != nil { return err } diff --git a/command/crypto/jwk/thumbprint.go b/command/crypto/jwk/thumbprint.go index a91661ad8..b5e4b792f 100644 --- a/command/crypto/jwk/thumbprint.go +++ b/command/crypto/jwk/thumbprint.go @@ -38,7 +38,7 @@ func thumbprintAction(*cli.Context) error { jwk := new(jose.JSONWebKey) // Attempt to decrypt if encrypted if b, err = jose.Decrypt(b, jose.WithPasswordPrompter("Please enter the password to decrypt your private JWK", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("password", "password-file")) })); err != nil { return err } diff --git a/command/crypto/key/format.go b/command/crypto/key/format.go index eac57d36e..3e98d82b1 100644 --- a/command/crypto/key/format.go +++ b/command/crypto/key/format.go @@ -313,7 +313,7 @@ func parseJWK(ctx *cli.Context, b []byte) (interface{}, error) { if _, err := jose.ParseEncrypted(string(b)); err == nil { opts := []jose.Option{ jose.WithPasswordPrompter("Please enter the password to decrypt the key", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("decryption password", "password-file")) }), } if passFile := ctx.String("password-file"); passFile != "" { @@ -350,7 +350,7 @@ func convertToPEM(ctx *cli.Context, key interface{}) (b []byte, err error) { opts = append(opts, pemutil.WithPasswordFile(passFile)) } else { opts = append(opts, pemutil.WithPasswordPrompt("Please enter the password to encrypt the private key", func(s string) ([]byte, error) { - return ui.PromptPassword(s, ui.WithValidateNotEmpty()) + return ui.PromptPassword(s, ui.WithField("encryption password", "password-file"), ui.WithValidateNotEmpty()) })) } default: @@ -406,7 +406,7 @@ func convertToSSH(ctx *cli.Context, key interface{}) ([]byte, error) { opts = append(opts, pemutil.WithPasswordFile(passFile)) } else { opts = append(opts, pemutil.WithPasswordPrompt("Please enter the password to encrypt the private key", func(s string) ([]byte, error) { - return ui.PromptPassword(s, ui.WithValidateNotEmpty()) + return ui.PromptPassword(s, ui.WithField("encryption password", "password-file"), ui.WithValidateNotEmpty()) })) } } diff --git a/command/crypto/keypair.go b/command/crypto/keypair.go index 332259f11..8c8464182 100644 --- a/command/crypto/keypair.go +++ b/command/crypto/keypair.go @@ -187,7 +187,9 @@ func createAction(ctx *cli.Context) (err error) { } } else { var pass []byte - pass, err = ui.PromptPassword("Please enter the password to encrypt the private key", ui.WithValue(password), ui.WithValidateNotEmpty()) + pass, err = ui.PromptPassword("Please enter the password to encrypt the private key", + ui.WithField("private key password", "password-file"), + ui.WithValue(password), ui.WithValidateNotEmpty()) if err != nil { return errors.Wrap(err, "error reading password") } diff --git a/command/ssh/certificate.go b/command/ssh/certificate.go index ebccd787e..fd1643c98 100644 --- a/command/ssh/certificate.go +++ b/command/ssh/certificate.go @@ -479,7 +479,7 @@ func certificateAction(ctx *cli.Context) error { prompt = fmt.Sprintf("%s (must be at least %d characters)", prompt, minPasswordLength) } opts = append(opts, pemutil.WithPasswordPrompt(prompt, func(s string) ([]byte, error) { - return ui.PromptPassword(s, ui.WithValidateNotEmpty(), ui.WithMinLength(minPasswordLength)) + return ui.PromptPassword(s, ui.WithField("private key password", "password-file"), ui.WithValidateNotEmpty(), ui.WithMinLength(minPasswordLength)) })) } diff --git a/command/ssh/rekey.go b/command/ssh/rekey.go index a384f9a4d..8283419d0 100644 --- a/command/ssh/rekey.go +++ b/command/ssh/rekey.go @@ -165,7 +165,7 @@ func rekeyAction(ctx *cli.Context) error { opts = append(opts, pemutil.WithPasswordFile(passwordFile)) default: opts = append(opts, pemutil.WithPasswordPrompt("Please enter the password to encrypt the private key", func(s string) ([]byte, error) { - return ui.PromptPassword(s, ui.WithValidateNotEmpty()) + return ui.PromptPassword(s, ui.WithField("private key password", "password-file"), ui.WithValidateNotEmpty()) })) } _, err = pemutil.Serialize(priv, opts...) diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 99ae3638c..fc3122555 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -96,10 +96,10 @@ func newApp(stdout, stderr io.Writer) *cli.App { // Define default file writers and prompters for go.step.sm/crypto pemutil.WriteFile = fileutil.WriteFile pemutil.PromptPassword = func(msg string) ([]byte, error) { - return ui.PromptPassword(msg) + return ui.PromptPassword(msg, ui.WithField("password", "password-file")) } jose.PromptPassword = func(msg string) ([]byte, error) { - return ui.PromptPassword(msg) + return ui.PromptPassword(msg, ui.WithField("password", "password-file")) } // Override global framework components @@ -130,6 +130,22 @@ func newApp(stdout, stderr io.Writer) *cli.App { Usage: "path to the config file to use for CLI flags", }) + // Flag to disable interactive prompts + app.Flags = append(app.Flags, cli.BoolFlag{ + Name: "non-interactive", + Usage: "disable interactive prompts; commands will fail if required input is missing", + EnvVar: "STEP_NON_INTERACTIVE", + }) + + // Before hook to propagate --non-interactive flag to the environment + // so that ui.CanPrompt() can check it without access to the cli context. + app.Before = func(ctx *cli.Context) error { + if ctx.GlobalBool("non-interactive") { + os.Setenv("STEP_NON_INTERACTIVE", "1") + } + return nil + } + // Action runs on `step` or `step ` if the command is not enabled. app.Action = func(ctx *cli.Context) error { args := ctx.Args() diff --git a/utils/cautils/certificate_flow.go b/utils/cautils/certificate_flow.go index 77609eee2..67000bbae 100644 --- a/utils/cautils/certificate_flow.go +++ b/utils/cautils/certificate_flow.go @@ -195,7 +195,9 @@ func (f *CertificateFlow) GenerateToken(ctx *cli.Context, subject string, sans [ } if subject == "" { - subject, err = ui.Prompt("What DNS names or IP addresses would you like to use? (e.g. internal.smallstep.com)", ui.WithValidateNotEmpty()) + subject, err = ui.Prompt("What DNS names or IP addresses would you like to use? (e.g. internal.smallstep.com)", + ui.WithField("DNS names or IP addresses", "san"), + ui.WithValidateNotEmpty()) if err != nil { return "", err } diff --git a/utils/cautils/client.go b/utils/cautils/client.go index ab66c9f76..b0705450e 100644 --- a/utils/cautils/client.go +++ b/utils/cautils/client.go @@ -142,7 +142,9 @@ func NewAdminClient(ctx *cli.Context, opts ...ca.ClientOption) (*ca.AdminClient, } subject := ctx.String("admin-subject") if subject == "" { - subject, err = ui.Prompt("Please enter admin name/subject (e.g., name@example.com)", ui.WithValidateNotEmpty()) + subject, err = ui.Prompt("Please enter admin name/subject (e.g., name@example.com)", + ui.WithField("admin name", "admin-subject"), + ui.WithValidateNotEmpty()) if err != nil { return nil, err } diff --git a/utils/cautils/token_flow.go b/utils/cautils/token_flow.go index ebdfa564c..a9337b61a 100644 --- a/utils/cautils/token_flow.go +++ b/utils/cautils/token_flow.go @@ -132,7 +132,7 @@ func NewTokenFlow(ctx *cli.Context, tokType int, subject string, sans []string, if tokType == SSHUserSignType { q = "What user principal would you like to use? (e.g. alice)" } - subject, err = ui.Prompt(q, ui.WithValidateNotEmpty()) + subject, err = ui.Prompt(q, ui.WithField("subject", "subject"), ui.WithValidateNotEmpty()) if err != nil { return "", err } @@ -399,7 +399,9 @@ func provisionerPrompt(ctx *cli.Context, provisioners provisioner.List) (provisi return items[0].Provisioner, nil } - i, _, err := ui.Select("What provisioner key do you want to use?", items, ui.WithSelectTemplates(ui.NamedSelectTemplates("Provisioner"))) + i, _, err := ui.Select("What provisioner key do you want to use?", items, + ui.WithField("provisioner key", "provisioner"), + ui.WithSelectTemplates(ui.NamedSelectTemplates("Provisioner"))) if err != nil { return nil, err } diff --git a/utils/cautils/token_generator.go b/utils/cautils/token_generator.go index 7937cc658..e3f01491f 100644 --- a/utils/cautils/token_generator.go +++ b/utils/cautils/token_generator.go @@ -405,7 +405,7 @@ func loadJWK(ctx *cli.Context, p *provisioner.JWK, tokAttrs tokenAttrs) (jwk *jo opts = append(opts, jose.WithPasswordPrompter("Please enter the password to decrypt the provisioner key", func(s string) ([]byte, error) { - return ui.PromptPassword(s) + return ui.PromptPassword(s, ui.WithField("password", "password-file")) }), ) diff --git a/utils/read.go b/utils/read.go index ca4675deb..261548e75 100644 --- a/utils/read.go +++ b/utils/read.go @@ -80,7 +80,7 @@ func ReadInput(prompt string) ([]byte, error) { } if st.Size() == 0 && st.Mode()&os.ModeNamedPipe == 0 { - return ui.PromptPassword(prompt) + return ui.PromptPassword(prompt, ui.WithField("password", "password-file")) } return ReadAll(stdin)