From 71a58f597975d22f4a3016424cfaa316cb766999 Mon Sep 17 00:00:00 2001 From: Rene Cunningham Date: Wed, 27 Jan 2021 10:32:02 +1100 Subject: [PATCH] A0CLI-47: add initial role support. --- internal/auth/auth.go | 1 + internal/auth0/auth0.go | 2 + internal/auth0/role.go | 22 +++ internal/cli/roles.go | 272 ++++++++++++++++++++++++++++++++++++++ internal/cli/root.go | 1 + internal/display/roles.go | 62 +++++++++ 6 files changed, 360 insertions(+) create mode 100644 internal/auth0/role.go create mode 100644 internal/cli/roles.go create mode 100644 internal/display/roles.go diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 0504dd881..414b58598 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -44,6 +44,7 @@ var requiredScopes = []string{ "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", "read:client_keys", "read:logs", + "create:roles", "delete:roles", "read:roles", "update:roles", } type Authenticator struct { diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index 6d18af900..d21d45a57 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -16,6 +16,7 @@ type API struct { Log LogAPI Rule RuleAPI ResourceServer ResourceServerAPI + Role RoleAPI } func NewAPI(m *management.Management) *API { @@ -28,6 +29,7 @@ func NewAPI(m *management.Management) *API { Log: m.Log, ResourceServer: m.ResourceServer, Rule: m.Rule, + Role: m.Role, } } diff --git a/internal/auth0/role.go b/internal/auth0/role.go new file mode 100644 index 000000000..5f6f4b1a8 --- /dev/null +++ b/internal/auth0/role.go @@ -0,0 +1,22 @@ +//go:generate mockgen -source=role.go -destination=role_mock.go -package=auth0 + +package auth0 + +import "gopkg.in/auth0.v5/management" + +type RoleAPI interface { + // Create a new role. + Create(r *management.Role, opts ...management.RequestOption) (err error) + + // Retrieve a role. + Read(id string, opts ...management.RequestOption) (r *management.Role, err error) + + // List all roles that can be assigned to users or groups. + List(opts ...management.RequestOption) (r *management.RoleList, err error) + + // Update a role. + Update(id string, r *management.Role, opts ...management.RequestOption) (err error) + + // Delete a role. + Delete(id string, opts ...management.RequestOption) (err error) +} diff --git a/internal/cli/roles.go b/internal/cli/roles.go new file mode 100644 index 000000000..de3cebd47 --- /dev/null +++ b/internal/cli/roles.go @@ -0,0 +1,272 @@ +package cli + +import ( + "github.com/AlecAivazis/survey/v2" + "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth0" + "github.com/spf13/cobra" + "gopkg.in/auth0.v5/management" +) + +func rolesCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "roles", + Short: "manage resources for roles.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(rolesListCmd(cli)) + cmd.AddCommand(rolesGetCmd(cli)) + cmd.AddCommand(rolesDeleteCmd(cli)) + cmd.AddCommand(rolesUpdateCmd(cli)) + cmd.AddCommand(rolesCreateCmd(cli)) + + return cmd +} + +func rolesListCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List all roles", + Long: `$ auth0 roles list +Retrieve filtered list of roles that can be assigned to users or groups + +`, + RunE: func(cmd *cobra.Command, args []string) error { + var list *management.RoleList + err := ansi.Spinner("Getting roles", func() error { + var err error + list, err = cli.api.Role.List() + return err + }) + + if err != nil { + return err + } + + cli.renderer.RoleList(list.Roles) + return nil + }, + } + + return cmd +} + +func rolesGetCmd(cli *cli) *cobra.Command { + var flags struct { + RoleID string + } + cmd := &cobra.Command{ + Use: "get", + Short: "Get a role", + Long: `$ auth0 roles get --role-id myRoleID +Get a role + +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("role-id") { + qs := []*survey.Question{ + { + Name: "RoleID", + Prompt: &survey.Input{ + Message: "RoleID:", + Help: "ID of the role to get.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + var role *management.Role + err := ansi.Spinner("Getting role", func() error { + var err error + role, err = cli.api.Role.Read(flags.RoleID) + return err + }) + + if err != nil { + return err + } + + cli.renderer.RoleGet(role) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.RoleID, "role-id", "i", "", "ID of the role to get.") + + return cmd +} + +func rolesDeleteCmd(cli *cli) *cobra.Command { + var flags struct { + RoleID string + } + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a role", + Long: `$ auth0 roles delete --role-id myRoleID +Delete a role. + +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("role-id") { + qs := []*survey.Question{ + { + Name: "RoleID", + Prompt: &survey.Input{ + Message: "RoleID:", + Help: "ID of the role to delete.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + return ansi.Spinner("Deleting role", func() error { + return cli.api.Role.Delete(flags.RoleID) + }) + }, + } + + cmd.Flags().StringVarP(&flags.RoleID, "role-id", "i", "", "ID of the role to delete.") + + return cmd +} + +func rolesUpdateCmd(cli *cli) *cobra.Command { + var flags struct { + RoleID string + Name string + Description string + } + cmd := &cobra.Command{ + Use: "update", + Short: "Update a role", + Long: `$ auth0 roles update --role-id myRoleID --name myName --description myDescription +Update a role. + +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("role-id") { + qs := []*survey.Question{ + { + Name: "RoleID", + Prompt: &survey.Input{ + Message: "RoleID:", + Help: "ID of the role to update.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + role := &management.Role{} + + if cmd.Flags().Changed("name") { + role.Name = auth0.String(flags.Name) + } + + if cmd.Flags().Changed("description") { + role.Description = auth0.String(flags.Description) + } + + err := ansi.Spinner("Updating role", func() error { + return cli.api.Role.Update(flags.RoleID, role) + }) + if err != nil { + return err + } + + cli.renderer.RoleUpdate(role) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.RoleID, "role-id", "i", "", "ID of the role to update.") + cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of this role.") + cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of this role.") + + return cmd +} + +func rolesCreateCmd(cli *cli) *cobra.Command { + var flags struct { + Name string + Description string + } + cmd := &cobra.Command{ + Use: "create", + Short: "Create a role", + Long: `$ auth0 roles create --name myName --description myDescription +Create a new role. + +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("name") { + qs := []*survey.Question{ + { + Name: "Name", + Prompt: &survey.Input{ + Message: "Name:", + Help: "Name of the role.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + if !cmd.Flags().Changed("description") { + qs := []*survey.Question{ + { + Name: "Description", + Prompt: &survey.Input{ + Message: "Description:", + Help: "Description of the role.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + role := &management.Role{ + Name: auth0.String(flags.Name), + Description: auth0.String(flags.Description), + } + + err := ansi.Spinner("Creating role", func() error { + return cli.api.Role.Create(role) + }) + if err != nil { + return err + } + + cli.renderer.RoleCreate(role) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the role.") + cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of the role.") + + return cmd +} diff --git a/internal/cli/root.go b/internal/cli/root.go index 5d30cff5d..ff5366e6f 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -59,6 +59,7 @@ func Execute() { rootCmd.AddCommand(connectionsCmd(cli)) rootCmd.AddCommand(tryLoginCmd(cli)) rootCmd.AddCommand(completionCmd(cli)) + rootCmd.AddCommand(rolesCmd(cli)) // TODO(cyx): backport this later on using latest auth0/v5. // rootCmd.AddCommand(actionsCmd(cli)) diff --git a/internal/display/roles.go b/internal/display/roles.go new file mode 100644 index 000000000..8252bde1b --- /dev/null +++ b/internal/display/roles.go @@ -0,0 +1,62 @@ +package display + +import ( + "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth0" + "gopkg.in/auth0.v5/management" +) + +type roleView struct { + Name string + ID string + Description string +} + +func (v *roleView) AsTableHeader() []string { + return []string{"Name", "Role ID", "Description"} +} + +func (v *roleView) AsTableRow() []string { + return []string{v.Name, v.ID, v.Description} +} + +func (r *Renderer) RoleList(roles []*management.Role) { + r.Heading(ansi.Bold(r.Tenant), "roles\n") + var res []View + for _, r := range roles { + res = append(res, &roleView{ + Name: auth0.StringValue(r.Name), + ID: auth0.StringValue(r.ID), + Description: auth0.StringValue(r.Description), + }) + } + + r.Results(res) +} + +func (r *Renderer) RoleGet(role *management.Role) { + r.Heading(ansi.Bold(r.Tenant), "role\n") + r.Results([]View{&roleView{ + Name: auth0.StringValue(role.Name), + ID: auth0.StringValue(role.ID), + Description: auth0.StringValue(role.Description), + }}) +} + +func (r *Renderer) RoleUpdate(role *management.Role) { + r.Heading(ansi.Bold(r.Tenant), "role\n") + r.Results([]View{&roleView{ + Name: auth0.StringValue(role.Name), + ID: auth0.StringValue(role.ID), + Description: auth0.StringValue(role.Description), + }}) +} + +func (r *Renderer) RoleCreate(role *management.Role) { + r.Heading(ansi.Bold(r.Tenant), "role\n") + r.Results([]View{&roleView{ + Name: auth0.StringValue(role.Name), + ID: auth0.StringValue(role.ID), + Description: auth0.StringValue(role.Description), + }}) +}