From 928c1de34f93502a969461397d9580743bffe01b Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Sun, 18 Apr 2021 19:08:16 -0300 Subject: [PATCH 1/5] Add `tenants open` and `apis open` --- internal/cli/apis.go | 55 +++++++++++++++++++++++++++ internal/cli/apps.go | 34 +++-------------- internal/cli/tenants.go | 72 ++++++++++++++++++++++++++++++++++++ internal/cli/utils_shared.go | 42 +++++++++++++++++++++ 4 files changed, 174 insertions(+), 29 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index eafbff2b2..788e32e2c 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -8,6 +8,7 @@ import ( "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" + "gopkg.in/auth0.v5" "gopkg.in/auth0.v5/management" ) @@ -53,6 +54,7 @@ func apisCmd(cli *cli) *cobra.Command { cmd.AddCommand(showApiCmd(cli)) cmd.AddCommand(updateApiCmd(cli)) cmd.AddCommand(deleteApiCmd(cli)) + cmd.AddCommand(openApiCmd(cli)) cmd.AddCommand(scopesCmd(cli)) return cmd @@ -324,6 +326,52 @@ auth0 apis delete `, return cmd } +func openApiCmd(cli *cli) *cobra.Command { + var inputs struct { + ID string + } + + 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.", + Example: "auth0 apis open ", + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + err := apiID.Pick(cmd, &inputs.ID, cli.apiPickerOptions) + if err != nil { + return err + } + } else { + inputs.ID = args[0] + } + + // Heuristics to determine if this a valid ID, or an audience value + if _, err := url.ParseRequestURI(inputs.ID); err == nil || len(inputs.ID) != 24 { + if err := ansi.Waiting(func() error { + api, err := cli.api.ResourceServer.Read(url.PathEscape(inputs.ID)) + if err != nil { + return err + } + inputs.ID = auth0.StringValue(api.ID) + return nil + }); err != nil { + return fmt.Errorf("An unexpected error occurred while trying to get the API Id for '%s': %w", inputs.ID, err) + } + } + + openManageURL(cli, cli.config.DefaultTenant, formatApiSettingsPath(inputs.ID)) + return nil + }, + } + + return cmd +} + func listScopesCmd(cli *cli) *cobra.Command { var inputs struct { ID string @@ -368,6 +416,13 @@ auth0 apis scopes ls `, return cmd } +func formatApiSettingsPath(id string) string { + if len(id) == 0 { + return "" + } + return fmt.Sprintf("apis/%s/settings", id) +} + func apiScopesFor(scopes []string) []*management.ResourceServerScope { models := []*management.ResourceServerScope{} diff --git a/internal/cli/apps.go b/internal/cli/apps.go index 3501541ad..6d702bb51 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -7,7 +7,6 @@ import ( "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/auth0" - "github.com/auth0/auth0-cli/internal/open" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" @@ -22,7 +21,6 @@ const ( appTypeRegularWeb = "regular_web" appTypeNonInteractive = "non_interactive" appDefaultURL = "http://localhost:3000" - manageDomain = "https://manage.auth0.com" ) var ( @@ -669,7 +667,7 @@ func openAppCmd(cli *cli) *cobra.Command { Args: cobra.MaximumNArgs(1), Short: "Open application settings page in Auth0 Manage", Long: "Open application settings page in Auth0 Manage.", - Example: `auth0 apps open `, + Example: "auth0 apps open ", PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -683,14 +681,7 @@ func openAppCmd(cli *cli) *cobra.Command { inputs.ID = args[0] } - manageUrl := formatManageURL(cli.config, inputs.ID) - if manageUrl == "" { - cli.renderer.Warnf("Unable to format the correct URL, please ensure you have run auth0 login and try again.") - return nil - } - if err := open.URL(manageUrl); err != nil { - cli.renderer.Warnf("Couldn't open the URL, please do it manually: %s.", manageUrl) - } + openManageURL(cli, cli.config.DefaultTenant, formatAppSettingsPath(inputs.ID)) return nil }, } @@ -698,26 +689,11 @@ func openAppCmd(cli *cli) *cobra.Command { return cmd } -func formatManageURL(cfg config, id string) string { - if cfg.DefaultTenant == "" || id == "" { - return "" - } - // ex: dev-tti06f6y.us.auth0.com - s := strings.Split(cfg.DefaultTenant, ".") - if len(s) < 4 { +func formatAppSettingsPath(id string) string { + if len(id) == 0 { return "" } - region := s[len(s)-3] - tenant := cfg.Tenants[cfg.DefaultTenant].Name - if tenant == "" { - return "" - } - return fmt.Sprintf("%s/dashboard/%s/%s/applications/%s/settings", - manageDomain, - region, - tenant, - id, - ) + return fmt.Sprintf("applications/%s/settings", id) } func apiTypeFor(v string) string { diff --git a/internal/cli/tenants.go b/internal/cli/tenants.go index 8105b0ee8..f1ec21b87 100644 --- a/internal/cli/tenants.go +++ b/internal/cli/tenants.go @@ -7,6 +7,13 @@ import ( "github.com/spf13/cobra" ) +var ( + tenantDomain = Argument{ + Name: "Tenant", + Help: "Tenant to select", + } +) + func tenantsCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "tenants", @@ -17,6 +24,7 @@ func tenantsCmd(cli *cli) *cobra.Command { cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(useTenantCmd(cli)) cmd.AddCommand(listTenantCmd(cli)) + cmd.AddCommand(openTenantCmd(cli)) return cmd } @@ -93,3 +101,67 @@ func useTenantCmd(cli *cli) *cobra.Command { return cmd } + +func openTenantCmd(cli *cli) *cobra.Command { + var inputs struct { + Domain string + } + + cmd := &cobra.Command{ + Use: "open", + Args: cobra.MaximumNArgs(1), + Short: "Open tenant settings page in Auth0 Manage", + Long: "Open tenant settings page in Auth0 Manage.", + Example: "auth0 tenants open ", + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + err := tenantDomain.Pick(cmd, &inputs.Domain, cli.tenantPickerOptions) + if err != nil { + return err + } + } else { + inputs.Domain = args[0] + + if _, ok := cli.config.Tenants[inputs.Domain]; !ok { + return fmt.Errorf("Unable to find tenant %s; run 'auth0 login' to configure a new tenant", inputs.Domain) + } + } + + openManageURL(cli, inputs.Domain, "tenant/general") + return nil + }, + } + + return cmd +} + +func (c *cli) tenantPickerOptions() (pickerOptions, error) { + tens, err := c.listTenants() + if err != nil { + return nil, fmt.Errorf("Unable to load tenants due to an unexpected error: %w", err) + } + + var ( + priorityOpts, opts pickerOptions + ) + + for _, t := range tens { + opt := pickerOption{value: t.Domain, label: t.Domain} + + // check if this is currently the default tenant. + if t.Domain == c.config.DefaultTenant { + priorityOpts = append(priorityOpts, opt) + } else { + opts = append(opts, opt) + } + } + + if len(opts)+len(priorityOpts) == 0 { + return nil, errNoApps + } + + return append(priorityOpts, opts...), nil +} diff --git a/internal/cli/utils_shared.go b/internal/cli/utils_shared.go index d6b48169f..1e9a8cb51 100644 --- a/internal/cli/utils_shared.go +++ b/internal/cli/utils_shared.go @@ -3,6 +3,7 @@ package cli import ( "crypto/rand" "fmt" + "strings" "encoding/base64" "encoding/json" @@ -24,6 +25,7 @@ const ( cliLoginTestingCallbackURL string = "http://localhost:8484" cliLoginTestingInitiateLoginURI string = "https://cli.auth0.com" cliLoginTestingStateSize int = 64 + manageURL string = "https://manage.auth0.com" ) var ( @@ -272,3 +274,43 @@ func containsStr(s []interface{}, u string) bool { } return false } + +func openManageURL(cli *cli, tenant string, path string) { + manageTenantURL := formatManageTenantURL(tenant, cli.config) + if len(manageTenantURL) == 0 || len(path) == 0 { + cli.renderer.Warnf("Unable to format the correct URL, please ensure you have run 'auth0 login' and try again.") + return + } + if err := open.URL(fmt.Sprintf("%s%s", manageTenantURL, path)); err != nil { + cli.renderer.Warnf("Couldn't open the URL, please do it manually: %s.", manageTenantURL) + } +} + +func formatManageTenantURL(tenant string, cfg config) string { + if len(tenant) == 0 { + return "" + } + // ex: dev-tti06f6y.us.auth0.com + s := strings.Split(tenant, ".") + + if len(s) < 3 { + return "" + } + + var region string + if len(s) == 3 { // It's a PUS1 tenant, ex: dev-tti06f6y.auth0.com + region = "us" + } else { + region = s[len(s)-3] + } + + tenantName := cfg.Tenants[tenant].Name + if len(tenantName) == 0 { + return "" + } + return fmt.Sprintf("%s/dashboard/%s/%s/", + manageURL, + region, + tenantName, + ) +} From 107573b56ac6c0778a0de14775e18eb3dc32948c Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Mon, 19 Apr 2021 19:01:17 -0300 Subject: [PATCH 2/5] Update internal/cli/tenants.go Co-authored-by: Jorge L. Fatta --- internal/cli/tenants.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/cli/tenants.go b/internal/cli/tenants.go index f1ec21b87..037beb7d1 100644 --- a/internal/cli/tenants.go +++ b/internal/cli/tenants.go @@ -144,9 +144,7 @@ func (c *cli) tenantPickerOptions() (pickerOptions, error) { return nil, fmt.Errorf("Unable to load tenants due to an unexpected error: %w", err) } - var ( - priorityOpts, opts pickerOptions - ) + var priorityOpts, opts pickerOptions for _, t := range tens { opt := pickerOption{value: t.Domain, label: t.Domain} From 62c64d535f533809886487d159ef00c33b04881b Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Mon, 19 Apr 2021 19:30:18 -0300 Subject: [PATCH 3/5] Address review feedback --- internal/cli/apis.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index 788e32e2c..92b5fc249 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -113,7 +113,7 @@ func showApiCmd(cli *cli) *cobra.Command { Short: "Show an API", Long: "Show an API.", Example: `auth0 apis show -auth0 apis show `, +auth0 apis show `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -216,8 +216,8 @@ func updateApiCmd(cli *cli) *cobra.Command { Short: "Update an API", Long: "Update an API.", Example: `auth0 apis update -auth0 apis update -auth0 apis update --name myapi`, +auth0 apis update +auth0 apis update --name myapi`, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -291,7 +291,7 @@ func deleteApiCmd(cli *cli) *cobra.Command { Short: "Delete an API", Long: "Delete an API.", Example: `auth0 apis delete -auth0 apis delete `, +auth0 apis delete `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -336,7 +336,8 @@ func openApiCmd(cli *cli) *cobra.Command { Args: cobra.MaximumNArgs(1), Short: "Open API settings page in Auth0 Manage", Long: "Open API settings page in Auth0 Manage.", - Example: "auth0 apis open ", + Example: `auth0 apis open +auth0 apis open `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -351,6 +352,11 @@ func openApiCmd(cli *cli) *cobra.Command { } // Heuristics to determine if this a valid ID, or an audience value + // Audiences are usually URLs, but not necessarily. Whereas IDs have a length of 24 + // So here if the value is not a URL, we then check if has the length of an ID + // If the length check fails, we know it's a non-URL audience value + // This will fail for non-URL audience values with the same length as the ID + // But it should cover the vast majority of users if _, err := url.ParseRequestURI(inputs.ID); err == nil || len(inputs.ID) != 24 { if err := ansi.Waiting(func() error { api, err := cli.api.ResourceServer.Read(url.PathEscape(inputs.ID)) @@ -384,7 +390,7 @@ func listScopesCmd(cli *cli) *cobra.Command { Short: "List the scopes of an API", Long: "List the scopes of an API.", Example: `auth0 apis scopes list -auth0 apis scopes ls `, +auth0 apis scopes ls `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, @@ -402,7 +408,7 @@ auth0 apis scopes ls `, if err := ansi.Waiting(func() error { var err error - api, err = cli.api.ResourceServer.Read(inputs.ID) + api, err = cli.api.ResourceServer.Read(url.PathEscape(inputs.ID)) return err }); err != nil { return fmt.Errorf("An unexpected error occurred while getting scopes for an API with Id '%s': %w", inputs.ID, err) From 99b46027febdde66983f38c1fe72c988ec96ec1a Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Mon, 19 Apr 2021 19:35:15 -0300 Subject: [PATCH 4/5] Remove excess whitespace --- internal/cli/tenants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/tenants.go b/internal/cli/tenants.go index 037beb7d1..a570de883 100644 --- a/internal/cli/tenants.go +++ b/internal/cli/tenants.go @@ -144,7 +144,7 @@ func (c *cli) tenantPickerOptions() (pickerOptions, error) { return nil, fmt.Errorf("Unable to load tenants due to an unexpected error: %w", err) } - var priorityOpts, opts pickerOptions + var priorityOpts, opts pickerOptions for _, t := range tens { opt := pickerOption{value: t.Domain, label: t.Domain} From 91375499ee6df06cfbb8bd909dcec7bd970a82f1 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Mon, 19 Apr 2021 19:37:44 -0300 Subject: [PATCH 5/5] Replace spaces with a tab --- internal/cli/tenants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/tenants.go b/internal/cli/tenants.go index b7877fa29..ad1f4cf1b 100644 --- a/internal/cli/tenants.go +++ b/internal/cli/tenants.go @@ -144,7 +144,7 @@ func (c *cli) tenantPickerOptions() (pickerOptions, error) { return nil, fmt.Errorf("Unable to load tenants due to an unexpected error: %w", err) } - var priorityOpts, opts pickerOptions + var priorityOpts, opts pickerOptions for _, t := range tens { opt := pickerOption{value: t.Domain, label: t.Domain}