diff --git a/cmd/config/config.go b/cmd/config/config.go index d63b1c3a..0d6a7ddc 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -1,6 +1,7 @@ package config import ( + "github.com/pingidentity/pingcli/internal/configuration/options" "github.com/spf13/cobra" ) @@ -32,5 +33,7 @@ func NewConfigCommand() *cobra.Command { NewConfigViewProfileCommand(), ) + cmd.PersistentFlags().AddFlag(options.ConfigUnmaskSecretValueOption.Flag) + return cmd } diff --git a/cmd/config/get.go b/cmd/config/get.go index 65d37b38..0f9b3def 100644 --- a/cmd/config/get.go +++ b/cmd/config/get.go @@ -16,7 +16,10 @@ const ( pingcli config get --profile myProfile noColor Read the worker ID used to authenticate to the PingOne service management API. - pingcli config get service.pingone.authentication.worker.environmentID` + pingcli config get service.pingone.authentication.worker.environmentID + + Read the unmasked value for the request access token. + pingcli config get --unmask-values request.accessToken` ) func NewConfigGetCommand() *cobra.Command { diff --git a/cmd/config/get_test.go b/cmd/config/get_test.go index 62f43ca1..f7427d30 100644 --- a/cmd/config/get_test.go +++ b/cmd/config/get_test.go @@ -93,3 +93,14 @@ func Example_getUnmaskedValue() { // Configuration values for profile 'default' and key 'noColor': // noColor=true } + +// https://pkg.go.dev/testing#hdr-Examples +func Example_get_UnmaskValuesFlag() { + t := testing.T{} + _ = testutils_cobra.ExecutePingcli(&t, "config", "get", "--unmask-values", options.RequestAccessTokenOption.ViperKey) + + // Output: + // Configuration values for profile 'default' and key 'request.accessToken': + // request.accessToken= + // request.accessTokenExpiry=0 +} diff --git a/cmd/config/set.go b/cmd/config/set.go index d89f9473..2512476d 100644 --- a/cmd/config/set.go +++ b/cmd/config/set.go @@ -13,7 +13,10 @@ const ( pingcli config set color=true Set the PingOne tenant region code setting to 'AP' for the profile named 'myProfile'. - pingcli config set --profile myProfile service.pingone.regionCode=AP` + pingcli config set --profile myProfile service.pingone.regionCode=AP + + Set the PingFederate basic authentication password with unmasked output + pingcli config set --profile myProfile --unmask-values service.pingfederate.authentication.basicAuth.password=1234` ) func NewConfigSetCommand() *cobra.Command { diff --git a/cmd/config/set_test.go b/cmd/config/set_test.go index b60d98b0..ce4365bb 100644 --- a/cmd/config/set_test.go +++ b/cmd/config/set_test.go @@ -94,6 +94,16 @@ func Example_setMaskedValue() { // service.pingfederate.authentication.basicAuth.password=******** } +// https://pkg.go.dev/testing#hdr-Examples +func Example_set_UnmaskedValuesFlag() { + t := testing.T{} + _ = testutils_cobra.ExecutePingcli(&t, "config", "set", "--unmask-values", fmt.Sprintf("%s=%s", options.PingFederateBasicAuthPasswordOption.ViperKey, "1234")) + + // Output: + // SUCCESS: Configuration set successfully: + // service.pingfederate.authentication.basicAuth.password=1234 +} + // https://pkg.go.dev/testing#hdr-Examples func Example_setUnmaskedValue() { t := testing.T{} diff --git a/cmd/config/view_profile.go b/cmd/config/view_profile.go index 6b52b7ac..f93a5d26 100644 --- a/cmd/config/view_profile.go +++ b/cmd/config/view_profile.go @@ -13,7 +13,10 @@ const ( pingcli config view-profile View configuration for a specific profile - pingcli config view-profile myprofile` + pingcli config view-profile myprofile + + View configuration for a specific profile with unmasked values + pingcli config --unmask-values view-profile myprofile` ) func NewConfigViewProfileCommand() *cobra.Command { diff --git a/cmd/config/view_profile_test.go b/cmd/config/view_profile_test.go index 4abaf9b0..93b2c9ed 100644 --- a/cmd/config/view_profile_test.go +++ b/cmd/config/view_profile_test.go @@ -19,6 +19,11 @@ func TestConfigViewProfileCmd_Execute_WithProfileFlag(t *testing.T) { testutils.CheckExpectedError(t, err, nil) } +func TestConfigViewProfileCmd_Execute_UnmaskValuesFlag(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "config", "view-profile", "--unmask-values") + testutils.CheckExpectedError(t, err, nil) +} + // Test Config Set Command fails with invalid flag func TestConfigViewProfileCmd_Execute_InvalidFlag(t *testing.T) { expectedErrorPattern := `^unknown flag: --invalid$` diff --git a/internal/commands/config/get_internal.go b/internal/commands/config/get_internal.go index 0399dc33..928b6845 100644 --- a/internal/commands/config/get_internal.go +++ b/internal/commands/config/get_internal.go @@ -32,7 +32,12 @@ func RunInternalConfigGet(viperKey string) (err error) { return fmt.Errorf("failed to get configuration: %v", err) } - if opt.Sensitive { + unmaskOptionVal, err := profiles.GetOptionValue(options.ConfigUnmaskSecretValueOption) + if err != nil { + unmaskOptionVal = "false" + } + + if opt.Sensitive && strings.EqualFold(unmaskOptionVal, "false") { msgStr += fmt.Sprintf("%s=%s\n", opt.ViperKey, profiles.MaskValue(vVal)) } else { msgStr += fmt.Sprintf("%s=%s\n", opt.ViperKey, vVal) diff --git a/internal/commands/config/set_internal.go b/internal/commands/config/set_internal.go index 081011da..17ce9460 100644 --- a/internal/commands/config/set_internal.go +++ b/internal/commands/config/set_internal.go @@ -52,7 +52,12 @@ func RunInternalConfigSet(kvPair string) (err error) { return fmt.Errorf("failed to set configuration: %v", err) } - if opt.Sensitive { + unmaskOptionVal, err := profiles.GetOptionValue(options.ConfigUnmaskSecretValueOption) + if err != nil { + unmaskOptionVal = "false" + } + + if opt.Sensitive && strings.EqualFold(unmaskOptionVal, "false") { msgStr += fmt.Sprintf("%s=%s", vKey, profiles.MaskValue(vVal)) } else { msgStr += fmt.Sprintf("%s=%s", vKey, vVal) diff --git a/internal/commands/config/unset_internal.go b/internal/commands/config/unset_internal.go index 6e10e1e2..304a4612 100644 --- a/internal/commands/config/unset_internal.go +++ b/internal/commands/config/unset_internal.go @@ -2,6 +2,7 @@ package config_internal import ( "fmt" + "strings" "github.com/pingidentity/pingcli/internal/configuration" "github.com/pingidentity/pingcli/internal/configuration/options" @@ -39,10 +40,15 @@ func RunInternalConfigUnset(viperKey string) (err error) { vVal, _, err := profiles.ViperValueFromOption(opt) if err != nil { - return fmt.Errorf("failed to set configuration: %v", err) + return fmt.Errorf("failed to unset configuration: %v", err) + } + + unmaskOptionVal, err := profiles.GetOptionValue(options.ConfigUnmaskSecretValueOption) + if err != nil { + unmaskOptionVal = "false" } - if opt.Sensitive { + if opt.Sensitive && strings.EqualFold(unmaskOptionVal, "false") { msgStr += fmt.Sprintf("%s=%s", viperKey, profiles.MaskValue(vVal)) } else { msgStr += fmt.Sprintf("%s=%s", viperKey, vVal) diff --git a/internal/commands/config/view_profile_internal.go b/internal/commands/config/view_profile_internal.go index 476482ea..2cf6f33c 100644 --- a/internal/commands/config/view_profile_internal.go +++ b/internal/commands/config/view_profile_internal.go @@ -2,6 +2,7 @@ package config_internal import ( "fmt" + "strings" "github.com/pingidentity/pingcli/internal/configuration/options" "github.com/pingidentity/pingcli/internal/output" @@ -37,7 +38,12 @@ func RunInternalConfigViewProfile(args []string) (err error) { return fmt.Errorf("failed to view profile: %v", err) } - if opt.Sensitive { + unmaskOptionVal, err := profiles.GetOptionValue(options.ConfigUnmaskSecretValueOption) + if err != nil { + unmaskOptionVal = "false" + } + + if opt.Sensitive && strings.EqualFold(unmaskOptionVal, "false") { msgStr += fmt.Sprintf("%s=%s\n", opt.ViperKey, profiles.MaskValue(vVal)) } else { msgStr += fmt.Sprintf("%s=%s\n", opt.ViperKey, vVal) diff --git a/internal/configuration/options/options.go b/internal/configuration/options/options.go index 64005ece..52e0781d 100644 --- a/internal/configuration/options/options.go +++ b/internal/configuration/options/options.go @@ -78,6 +78,7 @@ func Options() []Option { ConfigAddProfileSetActiveOption, ConfigDeleteAutoAcceptOption, ConfigListKeysYamlOption, + ConfigUnmaskSecretValueOption, RequestDataOption, RequestDataRawOption, @@ -131,6 +132,8 @@ var ( ConfigListKeysYamlOption Option ConfigDeleteAutoAcceptOption Option + + ConfigUnmaskSecretValueOption Option ) // 'pingcli platform export' command options diff --git a/internal/configuration/root/root.go b/internal/configuration/root/root.go index 0d7dc0d6..3929004b 100644 --- a/internal/configuration/root/root.go +++ b/internal/configuration/root/root.go @@ -17,6 +17,7 @@ func InitRootOptions() { initColorOption() initConfigOption() initOutputFormatOption() + initUnmaskSecretValuesOption() } func initActiveProfileOption() { @@ -129,6 +130,29 @@ func initOutputFormatOption() { } } +func initUnmaskSecretValuesOption() { + cobraParamName := "unmask-values" + cobraValue := new(customtypes.Bool) + defaultValue := customtypes.Bool(false) + + options.ConfigUnmaskSecretValueOption = options.Option{ + CobraParamName: cobraParamName, + CobraParamValue: cobraValue, + DefaultValue: &defaultValue, + EnvVar: "", // No EnvVar + Flag: &pflag.Flag{ + Name: cobraParamName, + Shorthand: "U", + Usage: "Unmask secret values. (default false)", + Value: cobraValue, + NoOptDefVal: "true", // Make this flag a boolean flag + }, + Sensitive: false, + Type: options.ENUM_BOOL, + ViperKey: "", // No ViperKey + } +} + func getDefaultConfigFilepath() (defaultConfigFilepath *customtypes.String) { l := logger.Get()