From 0f93aa79a6e4605bd2daa182c362fe4f86a047f3 Mon Sep 17 00:00:00 2001 From: Morgan <23509639+morganelle@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:20:17 -0700 Subject: [PATCH 1/3] Grants string (#270) * remove auth0.String from apiGrantsFor slice items * remove test file --- internal/cli/apps.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/cli/apps.go b/internal/cli/apps.go index 810eb7683..43763647c 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -745,25 +745,25 @@ func apiGrantsFor(s []string) []interface{} { for i, v := range s { switch strings.ToLower(v) { case "authorization-code", "code": - res[i] = auth0.String("authorization_code") + res[i] = "authorization_code" case "implicit": - res[i] = auth0.String("implicit") + res[i] = "implicit" case "refresh-token": - res[i] = auth0.String("refresh_token") + res[i] = "refresh_token" case "client-credentials", "credentials": - res[i] = auth0.String("client_credentials") + res[i] = "client_credentials" case "password": - res[i] = auth0.String("password") + res[i] = "password" case "password-realm": - res[i] = auth0.String("http://auth0.com/oauth/grant-type/password-realm") + res[i] = "http://auth0.com/oauth/grant-type/password-realm" case "mfa-oob": - res[i] = auth0.String("http://auth0.com/oauth/grant-type/mfa-oob") + res[i] = "http://auth0.com/oauth/grant-type/mfa-oob" case "mfa-otp": - res[i] = auth0.String("http://auth0.com/oauth/grant-type/mfa-otp") + res[i] = "http://auth0.com/oauth/grant-type/mfa-otp" case "mfa-recovery-code": - res[i] = auth0.String("http://auth0.com/oauth/grant-type/mfa-recovery-code") + res[i] = "http://auth0.com/oauth/grant-type/mfa-recovery-code" case "device-code": - res[i] = auth0.String("urn:ietf:params:oauth:grant-type:device_code") + res[i] = "urn:ietf:params:oauth:grant-type:device_code" default: } } From 8f52e8f68a5914e909ef0e6f1c2aff265bc207cf Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Fri, 30 Apr 2021 11:23:22 -0300 Subject: [PATCH 2/3] Add support for new API properties [CLI-139] (#265) --- internal/cli/apis.go | 92 +++++++++++++++++++++++++++++++-------- internal/cli/flags.go | 16 ++++--- internal/cli/input.go | 9 ++-- internal/cli/rules.go | 8 +++- internal/display/apis.go | 29 +++++++----- internal/display/rules.go | 3 +- internal/prompt/prompt.go | 53 +++++++++++----------- 7 files changed, 144 insertions(+), 66 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index 92b5fc249..b5200b7eb 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/url" + "strconv" "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/prompt" @@ -35,9 +36,23 @@ var ( Name: "Scopes", LongForm: "scopes", ShortForm: "s", - Help: "Comma-separated list of scopes.", + Help: "Comma-separated list of scopes (permissions).", IsRequired: true, } + apiTokenLifetime = Flag{ + Name: "Token Lifetime", + LongForm: "token-lifetime", + ShortForm: "l", + Help: "The amount of time in seconds that the token will be valid after being issued. Default value is 86400 seconds (1 day).", + AlwaysPrompt: true, + } + apiOfflineAccess = Flag{ + Name: "Allow Offline Access", + LongForm: "offline-access", + ShortForm: "o", + Help: "Whether Refresh Tokens can be issued for this API (true) or not (false).", + AlwaysPrompt: true, + } ) func apisCmd(cli *cli) *cobra.Command { @@ -147,9 +162,11 @@ auth0 apis show `, func createApiCmd(cli *cli) *cobra.Command { var inputs struct { - Name string - Identifier string - Scopes []string + Name string + Identifier string + Scopes []string + TokenLifetime int + AllowOfflineAccess bool } cmd := &cobra.Command{ @@ -158,8 +175,10 @@ func createApiCmd(cli *cli) *cobra.Command { Short: "Create a new API", Long: "Create a new API.", Example: `auth0 apis create -auth0 apis create --name myapi -auth0 apis create -n myapi --identifier http://my-api`, +auth0 apis create --name myapi +auth0 apis create -n myapi --identifier http://my-api +auth0 apis create -n myapi --token-expiration 6100 +auth0 apis create -n myapi -e 6100 --offline-access=true`, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -176,9 +195,19 @@ auth0 apis create -n myapi --identifier http://my-api`, return err } + if err := apiTokenLifetime.Ask(cmd, &inputs.TokenLifetime, auth0.String("86400")); err != nil { + return err + } + + if err :=apiOfflineAccess.AskBool(cmd, &inputs.AllowOfflineAccess, nil); err != nil { + return err + } + api := &management.ResourceServer{ - Name: &inputs.Name, - Identifier: &inputs.Identifier, + Name: &inputs.Name, + Identifier: &inputs.Identifier, + AllowOfflineAccess: &inputs.AllowOfflineAccess, + TokenLifetime: &inputs.TokenLifetime, } if len(inputs.Scopes) > 0 { @@ -199,15 +228,19 @@ auth0 apis create -n myapi --identifier http://my-api`, apiName.RegisterString(cmd, &inputs.Name, "") apiIdentifier.RegisterString(cmd, &inputs.Identifier, "") apiScopes.RegisterStringSlice(cmd, &inputs.Scopes, nil) + apiOfflineAccess.RegisterBool(cmd, &inputs.AllowOfflineAccess, false) + apiTokenLifetime.RegisterInt(cmd, &inputs.TokenLifetime, 0) return cmd } func updateApiCmd(cli *cli) *cobra.Command { var inputs struct { - ID string - Name string - Scopes []string + ID string + Name string + Scopes []string + TokenLifetime int + AllowOfflineAccess bool } cmd := &cobra.Command{ @@ -217,7 +250,9 @@ func updateApiCmd(cli *cli) *cobra.Command { Long: "Update an API.", Example: `auth0 apis update auth0 apis update -auth0 apis update --name myapi`, +auth0 apis update --name myapi +auth0 apis update -n myapi --token-expiration 6100 +auth0 apis update -n myapi -e 6100 --offline-access=true`, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -249,7 +284,22 @@ auth0 apis update --name myapi`, return err } - api := &management.ResourceServer{} + currentTokenLifetime := strconv.Itoa(auth0.IntValue(current.TokenLifetime)) + if err := apiTokenLifetime.AskU(cmd, &inputs.TokenLifetime, ¤tTokenLifetime); err != nil { + return err + } + + if !cmd.Flags().Changed(apiOfflineAccess.LongForm) { + inputs.AllowOfflineAccess = auth0.BoolValue(current.AllowOfflineAccess) + } + + if err := apiOfflineAccess.AskBoolU(cmd, &inputs.AllowOfflineAccess, current.AllowOfflineAccess); err != nil { + return err + } + + api := &management.ResourceServer{ + AllowOfflineAccess: &inputs.AllowOfflineAccess, + } if len(inputs.Name) == 0 { api.Name = current.Name @@ -263,6 +313,12 @@ auth0 apis update --name myapi`, api.Scopes = apiScopesFor(inputs.Scopes) } + if inputs.TokenLifetime == 0 { + api.TokenLifetime = current.TokenLifetime + } else { + api.TokenLifetime = &inputs.TokenLifetime + } + if err := ansi.Waiting(func() error { return cli.api.ResourceServer.Update(current.GetID(), api) }); err != nil { @@ -276,6 +332,8 @@ auth0 apis update --name myapi`, apiName.RegisterStringU(cmd, &inputs.Name, "") apiScopes.RegisterStringSliceU(cmd, &inputs.Scopes, nil) + apiOfflineAccess.RegisterBoolU(cmd, &inputs.AllowOfflineAccess, false) + apiTokenLifetime.RegisterIntU(cmd, &inputs.TokenLifetime, 0) return cmd } @@ -332,10 +390,10 @@ func openApiCmd(cli *cli) *cobra.Command { } cmd := &cobra.Command{ - Use: "open", - Args: cobra.MaximumNArgs(1), - Short: "Open API settings page in Auth0 Manage", - Long: "Open API settings page in Auth0 Manage.", + Use: "open", + Args: cobra.MaximumNArgs(1), + Short: "Open API settings page in Auth0 Manage", + Long: "Open API settings page in Auth0 Manage.", Example: `auth0 apis open auth0 apis open `, PreRun: func(cmd *cobra.Command, args []string) { diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 15b0a8115..00d66f9b3 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -49,12 +49,12 @@ func (f *Flag) AskManyU(cmd *cobra.Command, value interface{}, defaultValue *str return askManyFlag(cmd, f, value, defaultValue, true) } -func (f *Flag) AskBool(cmd *cobra.Command, value *bool, defaultValue *bool) { - askBoolFlag(cmd, f, value, defaultValue, false) +func (f *Flag) AskBool(cmd *cobra.Command, value *bool, defaultValue *bool) error { + return askBoolFlag(cmd, f, value, defaultValue, false) } -func (f *Flag) AskBoolU(cmd *cobra.Command, value *bool, defaultValue *bool) { - askBoolFlag(cmd, f, value, defaultValue, true) +func (f *Flag) AskBoolU(cmd *cobra.Command, value *bool, defaultValue *bool) error { + return askBoolFlag(cmd, f, value, defaultValue, true) } func (f *Flag) Select(cmd *cobra.Command, value interface{}, options []string, defaultValue *string) error { @@ -167,10 +167,14 @@ func askManyFlag(cmd *cobra.Command, f *Flag, value interface{}, defaultValue *s return nil } -func askBoolFlag(cmd *cobra.Command, f *Flag, value *bool, defaultValue *bool, isUpdate bool) { +func askBoolFlag(cmd *cobra.Command, f *Flag, value *bool, defaultValue *bool, isUpdate bool) error { if shouldAsk(cmd, f, isUpdate) { - askBool(cmd, f, value, defaultValue) + if err := askBool(cmd, f, value, defaultValue); err != nil { + return err + } } + + return nil } func selectFlag(cmd *cobra.Command, f *Flag, value interface{}, options []string, defaultValue *string, isUpdate bool) error { diff --git a/internal/cli/input.go b/internal/cli/input.go index 20e358d2e..2a1f66c01 100644 --- a/internal/cli/input.go +++ b/internal/cli/input.go @@ -28,9 +28,12 @@ func ask(cmd *cobra.Command, i commandInput, value interface{}, defaultValue *st return nil } -func askBool(cmd *cobra.Command, i commandInput, value *bool, defaultValue *bool) { - result := prompt.ConfirmDefault(i.GetLabel(), auth0.BoolValue(defaultValue)) - *value = result +func askBool(cmd *cobra.Command, i commandInput, value *bool, defaultValue *bool) error { + if err := prompt.AskBool(i.GetLabel(), value, auth0.BoolValue(defaultValue)); err != nil { + return handleInputError(err) + } + + return nil } func _select(cmd *cobra.Command, i commandInput, value interface{}, options []string, defaultValue *string, isUpdate bool) error { diff --git a/internal/cli/rules.go b/internal/cli/rules.go index 4fd0998c2..cce1575e7 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -313,7 +313,13 @@ auth0 rules update -n "My Updated Rule" --enabled=false`, return err } - ruleEnabled.AskBoolU(cmd, &inputs.Enabled, rule.Enabled) + if !cmd.Flags().Changed(ruleEnabled.LongForm) { + inputs.Enabled = auth0.BoolValue(rule.Enabled) + } + + if err := ruleEnabled.AskBoolU(cmd, &inputs.Enabled, rule.Enabled); err != nil { + return err + } // TODO(cyx): we can re-think this once we have // `--stdin` based commands. For now we don't have diff --git a/internal/display/apis.go b/internal/display/apis.go index 2e27fa5e3..8606d9cc5 100644 --- a/internal/display/apis.go +++ b/internal/display/apis.go @@ -3,6 +3,7 @@ package display import ( "fmt" "os" + "strconv" "strings" "github.com/auth0/auth0-cli/internal/ansi" @@ -12,20 +13,22 @@ import ( ) type apiView struct { - ID string - Name string - Identifier string - Scopes string + ID string + Name string + Identifier string + Scopes string + TokenLifetime int + OfflineAccess bool raw interface{} } func (v *apiView) AsTableHeader() []string { - return []string{"ID", "Name", "Identifier", "Scopes"} + return []string{} } func (v *apiView) AsTableRow() []string { - return []string{ansi.Faint(v.ID), v.Name, v.Identifier, fmt.Sprint(v.Scopes)} + return []string{} } func (v *apiView) KeyValues() [][]string { @@ -34,6 +37,8 @@ func (v *apiView) KeyValues() [][]string { {"NAME", v.Name}, {"IDENTIFIER", v.Identifier}, {"SCOPES", v.Scopes}, + {"TOKEN LIFETIME", strconv.Itoa(v.TokenLifetime)}, + {"ALLOW OFFLINE ACCESS", strconv.FormatBool(v.OfflineAccess)}, } } @@ -107,10 +112,12 @@ func (r *Renderer) ApiUpdate(api *management.ResourceServer) { func makeApiView(api *management.ResourceServer) (*apiView, bool) { scopes, scopesTruncated := getScopes(api.Scopes) view := &apiView{ - ID: auth0.StringValue(api.ID), - Name: auth0.StringValue(api.Name), - Identifier: auth0.StringValue(api.Identifier), - Scopes: auth0.StringValue(scopes), + ID: auth0.StringValue(api.ID), + Name: auth0.StringValue(api.Name), + Identifier: auth0.StringValue(api.Identifier), + Scopes: auth0.StringValue(scopes), + TokenLifetime: auth0.IntValue(api.TokenLifetime), + OfflineAccess: auth0.BoolValue(api.AllowOfflineAccess), raw: api, } @@ -172,7 +179,7 @@ func makeScopeView(scope *management.ResourceServerScope) *scopeView { func getScopes(scopes []*management.ResourceServerScope) (*string, bool) { ellipsis := "..." separator := " " - padding := 16 // the longest apiView key plus two spaces before and after in the label column + padding := 22 // the longest apiView key plus two spaces before and after in the label column terminalWidth, _, err := term.GetSize(int(os.Stdin.Fd())) if err != nil { terminalWidth = 80 diff --git a/internal/display/rules.go b/internal/display/rules.go index 8b0ea5582..013fa37bb 100644 --- a/internal/display/rules.go +++ b/internal/display/rules.go @@ -30,8 +30,9 @@ func (v *ruleView) AsTableRow() []string { func (v *ruleView) KeyValues() [][]string { return [][]string{ []string{"NAME", v.Name}, - []string{"ID", v.ID}, + []string{"ID", ansi.Faint(v.ID)}, []string{"ENABLED", strconv.FormatBool(v.Enabled)}, + []string{"ORDER", strconv.Itoa(v.Order)}, []string{"SCRIPT", v.Script}, } } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 70850c293..586a8e49c 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -24,6 +24,32 @@ func askOne(prompt survey.Prompt, response interface{}) error { return survey.AskOne(prompt, response, stdErrWriter, Icons) } +func AskBool(message string, value *bool, defaultValue bool) error { + prompt := &survey.Confirm{ + Message: message, + Default: defaultValue, + } + + if err := askOne(prompt, value); err != nil { + return err + } + + return nil +} + +func Confirm(message string) bool { + result := false + prompt := &survey.Confirm{ + Message: message, + } + + if err := askOne(prompt, &result); err != nil { + return false + } + + return result +} + func TextInput(name string, message string, help string, defaultValue string, required bool) *survey.Question { input := &survey.Question{ Name: name, @@ -66,30 +92,3 @@ func SelectInput(name string, message string, help string, options []string, def return input } - -func Confirm(message string) bool { - result := false - prompt := &survey.Confirm{ - Message: message, - } - - if err := askOne(prompt, &result); err != nil { - return false - } - - return result -} - -func ConfirmDefault(message string, defaultValue bool) bool { - result := false - prompt := &survey.Confirm{ - Message: message, - Default: defaultValue, - } - - if err := askOne(prompt, &result); err != nil { - return defaultValue - } - - return result -} From 457d3ff07f6c32b7bbe94919bc98fd9d6d7748f1 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Fri, 30 Apr 2021 12:07:42 -0300 Subject: [PATCH 3/3] Fix flag issues on `apps create` and `apps update` [CLI-146] (#269) * Fix flag value retention on apps create and update * Use the new IsSet method --- internal/cli/apis.go | 2 +- internal/cli/apps.go | 16 ++++++++-------- internal/cli/flags.go | 4 ++++ internal/cli/rules.go | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index b5200b7eb..07b7bb49e 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -289,7 +289,7 @@ auth0 apis update -n myapi -e 6100 --offline-access=true`, return err } - if !cmd.Flags().Changed(apiOfflineAccess.LongForm) { + if !apiOfflineAccess.IsSet(cmd) { inputs.AllowOfflineAccess = auth0.BoolValue(current.AllowOfflineAccess) } diff --git a/internal/cli/apps.go b/internal/cli/apps.go index 43763647c..5cdc9a81e 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -355,7 +355,7 @@ auth0 apps create -n myapp -t [native|spa|regular|m2m] -- description -n myapp --type [native|spa|regular|m2m]`, appIsSPA := apiTypeFor(inputs.Type) == appTypeSPA // Prompt for callback URLs if app is not m2m - if !appIsM2M { + if !appIsM2M && !appCallbacks.IsSet(cmd) { var defaultValue string if !appIsNative { @@ -536,7 +536,7 @@ auth0 apps update -n myapp --type [native|spa|regular|m2m]`, } // Prompt for logout URLs if app is not m2m - if !appIsM2M { + if !appIsM2M && !appLogoutURLs.IsSet(cmd) { var defaultValue string if !appIsNative { @@ -553,7 +553,7 @@ auth0 apps update -n myapp --type [native|spa|regular|m2m]`, } // Prompt for allowed origins URLs if app is SPA - if appIsSPA { + if appIsSPA && !appOrigins.IsSet(cmd) { defaultValue := appDefaultURL if len(current.AllowedOrigins) > 0 { @@ -566,7 +566,7 @@ auth0 apps update -n myapp --type [native|spa|regular|m2m]`, } // Prompt for allowed web origins URLs if app is SPA - if appIsSPA { + if appIsSPA && !appWebOrigins.IsSet(cmd) { defaultValue := appDefaultURL if len(current.WebOrigins) > 0 { diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 00d66f9b3..6bb1b9a59 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -33,6 +33,10 @@ func (f Flag) GetIsRequired() bool { return f.IsRequired } +func (f *Flag) IsSet(cmd *cobra.Command) bool { + return cmd.Flags().Changed(f.LongForm) +} + func (f *Flag) Ask(cmd *cobra.Command, value interface{}, defaultValue *string) error { return askFlag(cmd, f, value, defaultValue, false) } diff --git a/internal/cli/rules.go b/internal/cli/rules.go index cce1575e7..68000acea 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -313,7 +313,7 @@ auth0 rules update -n "My Updated Rule" --enabled=false`, return err } - if !cmd.Flags().Changed(ruleEnabled.LongForm) { + if !ruleEnabled.IsSet(cmd) { inputs.Enabled = auth0.BoolValue(rule.Enabled) }