From 4a33c7eb08e7d6f0fe43f2c527185177fb666d6e Mon Sep 17 00:00:00 2001 From: bright-poku Date: Fri, 30 Apr 2021 14:36:16 -0400 Subject: [PATCH 01/29] [CLI-148] initial commit: user show, create, list --- internal/auth/auth.go | 2 +- internal/auth0/user.go | 15 +++ internal/cli/users.go | 224 ++++++++++++++++++++++++++++++++++++++ internal/display/users.go | 133 ++++++++++++++++++++++ 4 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 internal/display/users.go diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 2d21afbb0..6a72795de 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -30,7 +30,7 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "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", "read:users", "update:users", + "read:client_keys", "read:logs", "read:users", "update:users", "create:users", } // RequiredScopes returns the scopes used for login. diff --git a/internal/auth0/user.go b/internal/auth0/user.go index 1a8017210..3beccbaee 100644 --- a/internal/auth0/user.go +++ b/internal/auth0/user.go @@ -11,4 +11,19 @@ type UserAPI interface { // Unblock a user that was blocked due to an excessive amount of incorrectly // provided credentials. Unblock(id string, opts ...management.RequestOption) error + + // Create a new user + Create(u *management.User, opts ...management.RequestOption) (err error) + + // Read user details for a given user + Read(id string, opts ...management.RequestOption) (u *management.User, err error) + + // Update user + Update(id string, u *management.User, opts ...management.RequestOption) (err error) + + // Delete a user + Delete(id string, opts ...management.RequestOption) (err error) + + // List all users + List(opts ...management.RequestOption) (ul *management.UserList, err error) } diff --git a/internal/cli/users.go b/internal/cli/users.go index 2d66a6156..05c4f786b 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -1,18 +1,63 @@ package cli import ( + "errors" "fmt" + "strings" "github.com/auth0/auth0-cli/internal/ansi" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" ) +const ( + connectionTypeUPA = "Username-Password-Authentication" +) var ( userID = Argument{ Name: "User ID", Help: "Id of the user.", } + userConnection = Flag{ + Name: "Connection", + LongForm: "connection", + ShortForm: "c", + Help: "Name of the connection this user should be created in.", + IsRequired: true, + AlwaysPrompt: false, + } + userEmail = Flag{ + Name: "Email", + LongForm: "email", + ShortForm: "e", + Help: "The user's email.", + IsRequired: true, + AlwaysPrompt: false, + } + userPassword = Flag{ + Name: "Password", + LongForm: "password", + ShortForm: "p", + Help: "Initial password for this user (mandatory for non-SMS connections)", + IsRequired: true, + AlwaysPrompt: false, + } + userUsername = Flag{ + Name: "Username", + LongForm: "username", + ShortForm: "u", + Help: "The user's username. Only valid if the connection requires a username.", + IsRequired: true, + AlwaysPrompt: false, + } + userName = Flag{ + Name: "Name", + LongForm: "name", + ShortForm: "n", + Help: "The user's full name.", + IsRequired: true, + AlwaysPrompt: false, + } ) func usersCmd(cli *cli) *cobra.Command { @@ -24,6 +69,9 @@ func usersCmd(cli *cli) *cobra.Command { cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(userBlocksCmd(cli)) cmd.AddCommand(deleteUserBlocksCmd(cli)) + cmd.AddCommand(createUserCmd(cli)) + cmd.AddCommand(listUserCmd(cli)) + cmd.AddCommand(showUserCmd(cli)) return cmd } @@ -122,3 +170,179 @@ auth0 users unblock return cmd } + +func createUserCmd(cli *cli) *cobra.Command { + var inputs struct { + Connection string + Email string + Password string + Username string + Name string + } + + cmd := &cobra.Command{ + Use: "create", + Args: cobra.NoArgs, + Short: "Create a new user", + Long: "Create a new user.", + Example: `auth0 users create +auth0 users create --name myapp +auth0 users create -n myapp --type [native|spa|regular|m2m] +auth0 users create -n myapp -t [native|spa|regular|m2m] -- description `, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + // Prompt for app name + if err := userName.Ask(cmd, &inputs.Name, nil); err != nil { + return err + } + + //if err := userUsername.Ask(cmd, &inputs.Username, nil); err != nil { + // return err + //} + + if err := userEmail.Ask(cmd, &inputs.Email, nil); err != nil { + return err + } + + if err := userPassword.Ask(cmd, &inputs.Password, nil); err != nil { + return err + } + + if err := userConnection.Ask(cmd, &inputs.Connection, nil); err != nil { + return err + } + + a := &management.User{ + + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + //Username: &inputs.Username, + Password: &inputs.Password, + } + + // Create app + if err := ansi.Waiting(func() error { + return cli.api.User.Create(a) + }); err != nil { + return fmt.Errorf("Unable to create user: %w", err) + } + + // Render Result + cli.renderer.UserCreate(a) + + return nil + }, + } + userName.RegisterString(cmd, &inputs.Name, "") + userConnection.RegisterString(cmd, &inputs.Connection, "") + userPassword.RegisterString(cmd, &inputs.Password, "") + userEmail.RegisterString(cmd, &inputs.Email, "") + //userUsername.RegisterString(cmd, &inputs.Username, "") + + return cmd +} + +func listUserCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, + Short: "List your users", + Long: `List your existing users. To create one try: +auth0 users create`, + Example: `auth0 users list +auth0 users ls`, + RunE: func(cmd *cobra.Command, args []string) error { + var list *management.UserList + + if err := ansi.Waiting(func() error { + var err error + list, err = cli.api.User.List(management.ExcludeFields(exludedFields...)) + return err + }); err != nil { + return fmt.Errorf("An unexpected error occurred: %w", err) + } + + cli.renderer.UserList(list.Users) + return nil + }, + } + + return cmd +} + +func showUserCmd(cli *cli) *cobra.Command { + var inputs struct { + ID string + } + + cmd := &cobra.Command{ + Use: "show", + Args: cobra.MaximumNArgs(1), + Short: "Show an existing user", + Long: "Show an existing user.", + Example: `auth0 users show +auth0 users show `, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) + if err != nil { + return err + } + } else { + inputs.ID = args[0] + } + + a := &management.User{ID: &inputs.ID} + + if err := ansi.Waiting(func() error { + var err error + a, err = cli.api.User.Read(inputs.ID) + return err + }); err != nil { + return fmt.Errorf("Unable to load users. The Id %v specified doesn't exist", inputs.ID) + } + + cli.renderer.UserShow(a) + return nil + }, + } + + return cmd +} + +func userTypeFor(v string) string { + switch strings.ToLower(v) { + case "upa", "Username-Password-Authentication": + return connectionTypeUPA + default: + return v + } +} + +func (c *cli) userPickerOptions() (pickerOptions, error) { + list, err := c.api.User.List() + if err != nil { + return nil, err + } + + + var opts pickerOptions + for _, r := range list.Users { + label := fmt.Sprintf("%s %s", r.GetName(), ansi.Faint("("+r.GetID()+")")) + + opts = append(opts, pickerOption{value: r.GetID(), label: label}) + } + + if len(opts) == 0 { + return nil, errors.New("There are currently no users.") + } + + return opts, nil +} \ No newline at end of file diff --git a/internal/display/users.go b/internal/display/users.go new file mode 100644 index 000000000..485f74d6e --- /dev/null +++ b/internal/display/users.go @@ -0,0 +1,133 @@ +package display + +import ( + "gopkg.in/auth0.v5" + "gopkg.in/auth0.v5/management" +) + +type userView struct { + UserID string + Connection string + Name string + Username string + Email string +} + +func (v *userView) AsTableHeader() []string { + return []string{ + "UserID", + "Connection", + "Name", + "Username", + "Email", + } +} + +func (v *userView) AsTableRow() []string { + return []string{ + v.UserID, + v.Connection, + v.Name, + v.Username, + v.Email, + } +} + +func (v *userView) KeyValues() [][]string { + return [][]string{ + []string{"USER ID", v.UserID}, + []string{"CONNECTION", v.Connection}, + []string{"NAME", v.Name}, + []string{"USERNAME", v.Username}, + []string{"EMAIL", v.Email}, + } +} + +type userListView struct { + UserID string + Name string + Username string + Email string + Connection string +} + +func (v *userListView) AsTableHeader() []string { + return []string{"User ID", "Name", "Username", "Email", "Connection"} +} + +func (v *userListView) AsTableRow() []string{ + return []string{ + v.UserID, + v.Name, + v.Username, + v.Email, + v.Connection, + } +} + +func (r *Renderer) UserList(users []*management.User) { + resource := "users" + + r.Heading(resource) + + if len(users) == 0 { + r.EmptyState(resource) + r.Infof("Use 'auth0 users create' to add one") + return + } + + var res []View + for _, c := range users { + res = append(res, &userListView{ + UserID: auth0.StringValue(c.ID), + Name: auth0.StringValue(c.Name), + Username: auth0.StringValue(c.Username), + Email: auth0.StringValue(c.Email), + Connection: auth0.StringValue(c.Connection), + }) + } + + r.Results(res) +} + +func (r *Renderer) UserShow(users *management.User) { + r.Heading("users") + + v := &userView{ + UserID: auth0.StringValue(users.ID), + Connection: auth0.StringValue(users.Connection), + Name: auth0.StringValue(users.Name), + Username: auth0.StringValue(users.Username), + Email: auth0.StringValue(users.Email), + } + + r.Result(v) +} + +func (r *Renderer) UserCreate(users *management.User) { + r.Heading("users created") + + v := &userView{ + UserID: auth0.StringValue(users.ID), + Connection: auth0.StringValue(users.Connection), + Name: auth0.StringValue(users.Name), + Username: auth0.StringValue(users.Username), + Email: auth0.StringValue(users.Email), + } + + r.Result(v) +} + +func (r *Renderer) UserUpdate(users *management.User) { + r.Heading("users created") + + v := &userView{ + UserID: auth0.StringValue(users.ID), + Connection: auth0.StringValue(users.Connection), + Name: auth0.StringValue(users.Name), + Username: auth0.StringValue(users.Username), + Email: auth0.StringValue(users.Email), + } + + r.Result(v) +} \ No newline at end of file From 1ea48f626fc499f181e33863bdb93e76801bf6c8 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 3 May 2021 08:15:50 -0400 Subject: [PATCH 02/29] [CLI-148] refactor: users command --- internal/auth/auth.go | 3 +- internal/auth0/auth0.go | 2 + internal/auth0/connection.go | 28 ++ internal/auth0/user.go | 10 +- internal/cli/users.go | 524 ++++++++++++++++++++++++----------- internal/display/users.go | 38 ++- 6 files changed, 434 insertions(+), 171 deletions(-) create mode 100644 internal/auth0/connection.go diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 6a72795de..cd9bc0842 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -30,7 +30,8 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "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", "read:users", "update:users", "create:users", + "read:client_keys", "read:logs", "read:connections", "update:connections", + "read:users", "update:users", "create:users", "delete:users", } // RequiredScopes returns the scopes used for login. diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index 6afe2f232..ca03121ba 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -17,6 +17,7 @@ type API struct { ResourceServer ResourceServerAPI Rule RuleAPI User UserAPI + Connection ConnectionAPI } func NewAPI(m *management.Management) *API { @@ -30,6 +31,7 @@ func NewAPI(m *management.Management) *API { ResourceServer: m.ResourceServer, Rule: m.Rule, User: m.User, + Connection: m.Connection, } } diff --git a/internal/auth0/connection.go b/internal/auth0/connection.go new file mode 100644 index 000000000..67689da4f --- /dev/null +++ b/internal/auth0/connection.go @@ -0,0 +1,28 @@ +//go:generate mockgen -source=connection.go -destination=connection.go -package=auth0 + +package auth0 + +import "gopkg.in/auth0.v5/management" + +type ConnectionAPI interface { + + // Create a new connection. + Create(c *management.Connection, opts ...management.RequestOption) (err error) + + // Read retrieves a connection by its id. + Read(id string, opts ...management.RequestOption) (c *management.Connection, err error) + + // ReadByName retrieves a connection by its name. + ReadByName(id string, opts ...management.RequestOption) (c *management.Connection, err error) + + // Update a connection. + Update(id string, c *management.Connection, opts ...management.RequestOption) (err error) + + // Delete a connection. + Delete(id string, opts ...management.RequestOption) (err error) + + // List all connections. + List(opts ...management.RequestOption) (ul *management.ConnectionList, err error) + + +} \ No newline at end of file diff --git a/internal/auth0/user.go b/internal/auth0/user.go index 3beccbaee..7a82341a1 100644 --- a/internal/auth0/user.go +++ b/internal/auth0/user.go @@ -12,18 +12,18 @@ type UserAPI interface { // provided credentials. Unblock(id string, opts ...management.RequestOption) error - // Create a new user + // Create a new user. Create(u *management.User, opts ...management.RequestOption) (err error) - // Read user details for a given user + // Read user details for a given user. Read(id string, opts ...management.RequestOption) (u *management.User, err error) - // Update user + // Update user. Update(id string, u *management.User, opts ...management.RequestOption) (err error) - // Delete a user + // Delete a user. Delete(id string, opts ...management.RequestOption) (err error) - // List all users + // List all users. List(opts ...management.RequestOption) (ul *management.UserList, err error) } diff --git a/internal/cli/users.go b/internal/cli/users.go index 05c4f786b..588aee472 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -1,62 +1,62 @@ package cli import ( + "encoding/json" "errors" "fmt" - "strings" "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth0" + "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" ) -const ( - connectionTypeUPA = "Username-Password-Authentication" -) + var ( userID = Argument{ Name: "User ID", Help: "Id of the user.", } userConnection = Flag{ - Name: "Connection", - LongForm: "connection", - ShortForm: "c", - Help: "Name of the connection this user should be created in.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Connection", + LongForm: "connection", + ShortForm: "c", + Help: "Name of the connection this user should be created in.", + IsRequired: true, + AlwaysPrompt: false, } userEmail = Flag{ - Name: "Email", - LongForm: "email", - ShortForm: "e", - Help: "The user's email.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Email", + LongForm: "email", + ShortForm: "e", + Help: "The user's email.", + IsRequired: true, + AlwaysPrompt: false, } userPassword = Flag{ - Name: "Password", - LongForm: "password", - ShortForm: "p", - Help: "Initial password for this user (mandatory for non-SMS connections)", - IsRequired: true, - AlwaysPrompt: false, + Name: "Password", + LongForm: "password", + ShortForm: "p", + Help: "Initial password for this user (mandatory for non-SMS connections)", + IsRequired: true, + AlwaysPrompt: false, } userUsername = Flag{ - Name: "Username", - LongForm: "username", - ShortForm: "u", - Help: "The user's username. Only valid if the connection requires a username.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Username", + LongForm: "username", + ShortForm: "u", + Help: "The user's username. Only valid if the connection requires a username.", + IsRequired: false, + AlwaysPrompt: false, } userName = Flag{ - Name: "Name", - LongForm: "name", - ShortForm: "n", - Help: "The user's full name.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Name", + LongForm: "name", + ShortForm: "n", + Help: "The user's full name.", + IsRequired: true, + AlwaysPrompt: false, } ) @@ -67,63 +67,176 @@ func usersCmd(cli *cli) *cobra.Command { } cmd.SetUsageTemplate(resourceUsageTemplate()) - cmd.AddCommand(userBlocksCmd(cli)) - cmd.AddCommand(deleteUserBlocksCmd(cli)) + cmd.AddCommand(listUsersCmd(cli)) cmd.AddCommand(createUserCmd(cli)) - cmd.AddCommand(listUserCmd(cli)) cmd.AddCommand(showUserCmd(cli)) + cmd.AddCommand(deleteUserCmd(cli)) + cmd.AddCommand(updateUserCmd(cli)) + cmd.AddCommand(userBlocksCmd(cli)) + cmd.AddCommand(deleteUserBlocksCmd(cli)) + return cmd } -func userBlocksCmd(cli *cli) *cobra.Command { +func listUsersCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ - Use: "blocks", - Short: "Manage brute-force protection user blocks.", + Use: "list", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, + Short: "List your users.", + Long: `List your existing users. To create one try: +auth0 users create`, + Example: `auth0 users list +auth0 users ls`, + RunE: func(cmd *cobra.Command, args []string) error { + var list *management.UserList + + if err := ansi.Waiting(func() error { + var err error + list, err = cli.api.User.List() + return err + }); err != nil { + return fmt.Errorf("An unexpected error occurred: %w", err) + } + + cli.renderer.UserList(list.Users) + return nil + }, } - cmd.SetUsageTemplate(resourceUsageTemplate()) - cmd.AddCommand(listUserBlocksCmd(cli)) return cmd } -func listUserBlocksCmd(cli *cli) *cobra.Command { +func createUserCmd(cli *cli) *cobra.Command { var inputs struct { - userID string + Connection string + Email string + Password string + Username string + Name string } cmd := &cobra.Command{ - Use: "list", - Args: cobra.MaximumNArgs(1), - Short: "List brute-force protection blocks for a given user", - Long: `List brute-force protection blocks for a given user: + Use: "create", + Args: cobra.NoArgs, + Short: "Create a new user.", + Long: "Create a new user.", + Example: `auth0 users create +auth0 users create --name "John Doe" +auth0 users create -n "John Doe" --email john@example.com`, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + // Prompt for user's name + if err := userName.Ask(cmd, &inputs.Name, nil); err != nil { + return err + } -auth0 users blocks list -`, + // Select from the available connection types + // Users API currently support database connections + if err := userConnection.Select(cmd, &inputs.Connection, cli.connectionPickerOptions(), nil); err != nil { + return err + } + + // Prompt for user email + if err := userEmail.Ask(cmd, &inputs.Email, nil); err != nil { + return err + } + + // Prompt for user password + if err := userPassword.Ask(cmd, &inputs.Password, nil); err != nil { + return err + } + + // The getConnReqUsername returns the value for the requires_username field for the selected connection + // The result will be used to determine whether to prompt for username + conn := cli.getConnReqUsername(auth0.StringValue(&inputs.Connection)) + requireUsername := auth0.BoolValue(conn) + + var a *management.User + + // Prompt for username if the requireUsername is set to true + // Load values including the username's field into a fresh users instance + // Else block loads values without username for connections with requireUsername set to false + if requireUsername { + if err := userUsername.Ask(cmd, &inputs.Username, nil); err != nil { + return err + } + a = &management.User{ + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + Username: &inputs.Username, + Password: &inputs.Password, + } + } else { + a = &management.User{ + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + Password: &inputs.Password, + } + } + + + // Create app + if err := ansi.Waiting(func() error { + return cli.api.User.Create(a) + }); err != nil { + return fmt.Errorf("Unable to create user: %w", err) + } + + // Render Result + cli.renderer.UserCreate(a) + + return nil + }, + } + userName.RegisterString(cmd, &inputs.Name, "") + userConnection.RegisterString(cmd, &inputs.Connection, "") + userPassword.RegisterString(cmd, &inputs.Password, "") + userEmail.RegisterString(cmd, &inputs.Email, "") + userUsername.RegisterString(cmd, &inputs.Username, "") + + return cmd +} + +func showUserCmd(cli *cli) *cobra.Command { + var inputs struct { + ID string + } + + cmd := &cobra.Command{ + Use: "show", + Args: cobra.MaximumNArgs(1), + Short: "Show an existing user.", + Long: "Show an existing user.", + Example: `auth0 users show +auth0 users show `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if err := userID.Ask(cmd, &inputs.userID); err != nil { + err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) + if err != nil { return err } } else { - inputs.userID = args[0] + inputs.ID = args[0] } - var userBlocks []*management.UserBlock - - err := ansi.Waiting(func() error { + a := &management.User{ID: &inputs.ID} + if err := ansi.Waiting(func() error { var err error - userBlocks, err = cli.api.User.Blocks(inputs.userID) + a, err = cli.api.User.Read(inputs.ID) return err - }) - - if err != nil { - return fmt.Errorf("Unable to load user blocks %v, error: %w", inputs.userID, err) + }); err != nil { + return fmt.Errorf("Unable to load users. The Id %v specified doesn't exist", inputs.ID) } - cli.renderer.UserBlocksList(userBlocks) + cli.renderer.UserShow(a) return nil }, } @@ -131,142 +244,215 @@ auth0 users blocks list return cmd } -func deleteUserBlocksCmd(cli *cli) *cobra.Command { +func deleteUserCmd(cli *cli) *cobra.Command { var inputs struct { - userID string + ID string } cmd := &cobra.Command{ - Use: "unblock", + Use: "delete", Args: cobra.MaximumNArgs(1), - Short: "Delete brute-force protection blocks for a given user", - Long: `Delete brute-force protection blocks for a given user: - -auth0 users unblock -`, + Short: "Delete a User", + Long: "Delete a User.", + Example: `auth0 users delete +auth0 users delete `, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if err := userID.Ask(cmd, &inputs.userID); err != nil { + err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) + if err != nil { return err } } else { - inputs.userID = args[0] + inputs.ID = args[0] } - err := ansi.Spinner("Deleting blocks for user...", func() error { - return cli.api.User.Unblock(inputs.userID) - }) - - if err != nil { - return err + if !cli.force && canPrompt(cmd) { + if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { + return nil + } } - return nil + return ansi.Spinner("Deleting User", func() error { + _, err := cli.api.User.Read(inputs.ID) + + if err != nil { + return fmt.Errorf("Unable to delete User. The specified Id: %v doesn't exist", inputs.ID) + } + + return cli.api.User.Delete(inputs.ID) + }) }, } return cmd } -func createUserCmd(cli *cli) *cobra.Command { +func updateUserCmd(cli *cli) *cobra.Command { var inputs struct { - Connection string + ID string Email string Password string - Username string Name string + Connection string } cmd := &cobra.Command{ - Use: "create", - Args: cobra.NoArgs, - Short: "Create a new user", - Long: "Create a new user.", - Example: `auth0 users create -auth0 users create --name myapp -auth0 users create -n myapp --type [native|spa|regular|m2m] -auth0 users create -n myapp -t [native|spa|regular|m2m] -- description `, + Use: "update", + Args: cobra.MaximumNArgs(1), + Short: "Update a User", + Long: "Update a User.", + Example: `auth0 users update +auth0 users update +auth0 users update --name John Doe +auth0 users update -n John Doe --email john.doe@gmail.com`, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - // Prompt for app name - if err := userName.Ask(cmd, &inputs.Name, nil); err != nil { + var current *management.User + + if len(args) == 0 { + err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) + if err != nil { + return err + } + } else { + inputs.ID = args[0] + } + + if err := ansi.Waiting(func() error { + var err error + current, err = cli.api.User.Read(inputs.ID) return err + }); err != nil { + return fmt.Errorf("Unable to load User. The Id %v specified doesn't exist", inputs.ID) } - //if err := userUsername.Ask(cmd, &inputs.Username, nil); err != nil { - // return err - //} - if err := userEmail.Ask(cmd, &inputs.Email, nil); err != nil { + // using getUserConnection to get connection name from user Identities + // just using current.connection will return empty + conn := stringSliceToCommaSeparatedString(cli.getUserConnection(current)) + current.Connection = auth0.String(conn) + + if err := userConnection.AskU(cmd, &inputs.Connection, current.Connection); err != nil { return err } - if err := userPassword.Ask(cmd, &inputs.Password, nil); err != nil { + if err := userName.AskU(cmd, &inputs.Name, current.Name); err != nil { return err } - if err := userConnection.Ask(cmd, &inputs.Connection, nil); err != nil { + + if err := userEmail.AskU(cmd, &inputs.Email, current.Email); err != nil { return err } - a := &management.User{ + if err := userPassword.AskU(cmd, &inputs.Password, current.Password); err != nil { + return err + } - Connection: &inputs.Connection, - Email: &inputs.Email, - Name: &inputs.Name, - //Username: &inputs.Username, - Password: &inputs.Password, + // username cannot be updated for database connections + //if err := userUsername.AskU(cmd, &inputs.Username, current.Username); err != nil { + // return err + //} + + user := &management.User{} + + if len(inputs.Name) == 0 { + user.Name = current.Name + } else { + user.Name = &inputs.Name + } + + if len(inputs.Email) == 0 { + user.Email = current.Email + } else { + user.Email = &inputs.Email + } + + if len(inputs.Password) == 0 { + user.Password = current.Password + } else { + user.Password = &inputs.Password + } + + if len(inputs.Connection) == 0 { + user.Connection = current.Connection + } else { + user.Connection = &inputs.Connection } - // Create app if err := ansi.Waiting(func() error { - return cli.api.User.Create(a) + return cli.api.User.Update(current.GetID(), user) }); err != nil { - return fmt.Errorf("Unable to create user: %w", err) + return fmt.Errorf("An unexpected error occurred while trying to update an User with Id '%s': %w", inputs.ID, err) } - // Render Result - cli.renderer.UserCreate(a) - + cli.renderer.UserUpdate(user) return nil }, } - userName.RegisterString(cmd, &inputs.Name, "") - userConnection.RegisterString(cmd, &inputs.Connection, "") - userPassword.RegisterString(cmd, &inputs.Password, "") - userEmail.RegisterString(cmd, &inputs.Email, "") - //userUsername.RegisterString(cmd, &inputs.Username, "") + + userName.RegisterStringU(cmd, &inputs.Name, "") + userConnection.RegisterStringU(cmd, &inputs.Connection, "") + userPassword.RegisterStringU(cmd, &inputs.Password, "") + userEmail.RegisterStringU(cmd, &inputs.Email, "") return cmd } -func listUserCmd(cli *cli) *cobra.Command { +func userBlocksCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Args: cobra.NoArgs, - Short: "List your users", - Long: `List your existing users. To create one try: -auth0 users create`, - Example: `auth0 users list -auth0 users ls`, + Use: "blocks", + Short: "Manage brute-force protection user blocks.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(listUserBlocksCmd(cli)) + return cmd +} + +func listUserBlocksCmd(cli *cli) *cobra.Command { + var inputs struct { + userID string + } + + cmd := &cobra.Command{ + Use: "list", + Args: cobra.MaximumNArgs(1), + Short: "List brute-force protection blocks for a given user", + Long: `List brute-force protection blocks for a given user: + +auth0 users blocks list +`, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { - var list *management.UserList + if len(args) == 0 { + if err := userID.Ask(cmd, &inputs.userID); err != nil { + return err + } + } else { + inputs.userID = args[0] + } - if err := ansi.Waiting(func() error { + var userBlocks []*management.UserBlock + + err := ansi.Waiting(func() error { var err error - list, err = cli.api.User.List(management.ExcludeFields(exludedFields...)) + userBlocks, err = cli.api.User.Blocks(inputs.userID) return err - }); err != nil { - return fmt.Errorf("An unexpected error occurred: %w", err) + }) + + if err != nil { + return fmt.Errorf("Unable to load user blocks %v, error: %w", inputs.userID, err) } - cli.renderer.UserList(list.Users) + cli.renderer.UserBlocksList(userBlocks) return nil }, } @@ -274,42 +460,39 @@ auth0 users ls`, return cmd } -func showUserCmd(cli *cli) *cobra.Command { +func deleteUserBlocksCmd(cli *cli) *cobra.Command { var inputs struct { - ID string + userID string } cmd := &cobra.Command{ - Use: "show", + Use: "unblock", Args: cobra.MaximumNArgs(1), - Short: "Show an existing user", - Long: "Show an existing user.", - Example: `auth0 users show -auth0 users show `, + Short: "Delete brute-force protection blocks for a given user", + Long: `Delete brute-force protection blocks for a given user: + +auth0 users unblock +`, PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) - if err != nil { + if err := userID.Ask(cmd, &inputs.userID); err != nil { return err } } else { - inputs.ID = args[0] + inputs.userID = args[0] } - a := &management.User{ID: &inputs.ID} + err := ansi.Spinner("Deleting blocks for user...", func() error { + return cli.api.User.Unblock(inputs.userID) + }) - if err := ansi.Waiting(func() error { - var err error - a, err = cli.api.User.Read(inputs.ID) + if err != nil { return err - }); err != nil { - return fmt.Errorf("Unable to load users. The Id %v specified doesn't exist", inputs.ID) } - cli.renderer.UserShow(a) return nil }, } @@ -317,15 +500,6 @@ auth0 users show `, return cmd } -func userTypeFor(v string) string { - switch strings.ToLower(v) { - case "upa", "Username-Password-Authentication": - return connectionTypeUPA - default: - return v - } -} - func (c *cli) userPickerOptions() (pickerOptions, error) { list, err := c.api.User.List() if err != nil { @@ -345,4 +519,42 @@ func (c *cli) userPickerOptions() (pickerOptions, error) { } return opts, nil -} \ No newline at end of file +} + +func (c *cli) connectionPickerOptions() []string { + var list *management.ConnectionList + + var res []string + list, _ = c.api.Connection.List() + + for _, conn := range list.Connections { + res = append(res, conn.GetName()) + } + return res +} + +func (c *cli)getUserConnection(users *management.User) []string { + var res []string + for _, i := range users.Identities{ + res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection))) + + } + return res +} +// Since the option field is ignored with `json:"-"` in Connections +// This is a workaround to get the requires_username field nested inside Options field +type Options struct { + RequiresUsername bool `json:"requires_username"` +} + +func (c *cli) getConnReqUsername(s string) *bool { + var conn *management.Connection + conn, _ = c.api.Connection.ReadByName(s) + res := fmt.Sprintln(conn.Options) + + opts := Options{} + json.Unmarshal([]byte(res), &opts) + + return auth0.Bool(opts.RequiresUsername) +} + diff --git a/internal/display/users.go b/internal/display/users.go index 485f74d6e..a627c57db 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -1,45 +1,48 @@ package display import ( + "fmt" + "strings" + "gopkg.in/auth0.v5" "gopkg.in/auth0.v5/management" ) type userView struct { UserID string - Connection string Name string Username string Email string + Connection string } func (v *userView) AsTableHeader() []string { return []string{ "UserID", - "Connection", "Name", "Username", "Email", + "Connection", } } func (v *userView) AsTableRow() []string { return []string{ v.UserID, - v.Connection, v.Name, v.Username, v.Email, + v.Connection, } } func (v *userView) KeyValues() [][]string { return [][]string{ []string{"USER ID", v.UserID}, - []string{"CONNECTION", v.Connection}, []string{"NAME", v.Name}, []string{"USERNAME", v.Username}, []string{"EMAIL", v.Email}, + []string{"CONNECTION", v.Connection}, } } @@ -78,12 +81,13 @@ func (r *Renderer) UserList(users []*management.User) { var res []View for _, c := range users { + conn := getUserConnection(c) res = append(res, &userListView{ UserID: auth0.StringValue(c.ID), Name: auth0.StringValue(c.Name), Username: auth0.StringValue(c.Username), Email: auth0.StringValue(c.Email), - Connection: auth0.StringValue(c.Connection), + Connection: stringSliceToCommaSeparatedString(conn), }) } @@ -93,14 +97,15 @@ func (r *Renderer) UserList(users []*management.User) { func (r *Renderer) UserShow(users *management.User) { r.Heading("users") + conn := getUserConnection(users) + v := &userView{ UserID: auth0.StringValue(users.ID), - Connection: auth0.StringValue(users.Connection), Name: auth0.StringValue(users.Name), Username: auth0.StringValue(users.Username), Email: auth0.StringValue(users.Email), + Connection: stringSliceToCommaSeparatedString(conn), } - r.Result(v) } @@ -119,15 +124,30 @@ func (r *Renderer) UserCreate(users *management.User) { } func (r *Renderer) UserUpdate(users *management.User) { - r.Heading("users created") + r.Heading("users updated") + + conn := getUserConnection(users) v := &userView{ UserID: auth0.StringValue(users.ID), - Connection: auth0.StringValue(users.Connection), + Connection: stringSliceToCommaSeparatedString(conn), Name: auth0.StringValue(users.Name), Username: auth0.StringValue(users.Username), Email: auth0.StringValue(users.Email), } r.Result(v) +} + +func getUserConnection(users *management.User) []string { + var res []string + for _, i := range users.Identities{ + res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection))) + + } + return res +} + +func stringSliceToCommaSeparatedString(s []string) string { + return strings.Join(s, ", ") } \ No newline at end of file From b5138ff549bfd2c0d31af7b67171d213311763df Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 3 May 2021 08:26:35 -0400 Subject: [PATCH 03/29] [CLI-148] refactor users command: add error return value --- internal/cli/users.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 588aee472..9a10f8659 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -128,10 +128,6 @@ auth0 users create -n "John Doe" --email john@example.com`, prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - // Prompt for user's name - if err := userName.Ask(cmd, &inputs.Name, nil); err != nil { - return err - } // Select from the available connection types // Users API currently support database connections @@ -139,6 +135,11 @@ auth0 users create -n "John Doe" --email john@example.com`, return err } + // Prompt for user's name + if err := userName.Ask(cmd, &inputs.Name, nil); err != nil { + return err + } + // Prompt for user email if err := userEmail.Ask(cmd, &inputs.Email, nil); err != nil { return err @@ -553,7 +554,9 @@ func (c *cli) getConnReqUsername(s string) *bool { res := fmt.Sprintln(conn.Options) opts := Options{} - json.Unmarshal([]byte(res), &opts) + if err := json.Unmarshal([]byte(res), &opts); err != nil { + panic(err) + } return auth0.Bool(opts.RequiresUsername) } From 0520af18a6f06f537e79276750429cd5ae5fc923 Mon Sep 17 00:00:00 2001 From: bright-poku <75628344+bright-poku@users.noreply.github.com> Date: Mon, 3 May 2021 11:18:40 -0400 Subject: [PATCH 04/29] Update internal/cli/users.go Co-authored-by: Rita Zerrizuela --- internal/cli/users.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 028f88760..0021de074 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -24,7 +24,6 @@ var ( ShortForm: "c", Help: "Name of the connection this user should be created in.", IsRequired: true, - AlwaysPrompt: false, } userEmail = Flag{ Name: "Email", @@ -550,4 +549,3 @@ func (c *cli) getConnReqUsername(s string) *bool { return auth0.Bool(opts.RequiresUsername) } - From 06d8b0af919253972d2a7cc5da68e7501d65659b Mon Sep 17 00:00:00 2001 From: bright-poku Date: Tue, 4 May 2021 08:39:05 -0400 Subject: [PATCH 05/29] [CLI-148] refactor: add suggestions from review --- internal/auth0/connection.go | 4 +- internal/auth0/user.go | 3 + internal/cli/users.go | 281 +++++++++++++++++------------------ internal/display/users.go | 86 ++++------- internal/prompt/prompt.go | 13 ++ 5 files changed, 177 insertions(+), 210 deletions(-) diff --git a/internal/auth0/connection.go b/internal/auth0/connection.go index 67689da4f..9c9a32878 100644 --- a/internal/auth0/connection.go +++ b/internal/auth0/connection.go @@ -23,6 +23,4 @@ type ConnectionAPI interface { // List all connections. List(opts ...management.RequestOption) (ul *management.ConnectionList, err error) - - -} \ No newline at end of file +} diff --git a/internal/auth0/user.go b/internal/auth0/user.go index 7a82341a1..5e7123a3a 100644 --- a/internal/auth0/user.go +++ b/internal/auth0/user.go @@ -26,4 +26,7 @@ type UserAPI interface { // List all users. List(opts ...management.RequestOption) (ul *management.UserList, err error) + + // Search for users + Search(opts ...management.RequestOption) (us *management.UserList, err error) } diff --git a/internal/cli/users.go b/internal/cli/users.go index 9a10f8659..0f09cf8b5 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -2,9 +2,7 @@ package cli import ( "encoding/json" - "errors" "fmt" - "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/auth0-cli/internal/prompt" @@ -12,51 +10,57 @@ import ( "gopkg.in/auth0.v5/management" ) - var ( userID = Argument{ Name: "User ID", Help: "Id of the user.", } userConnection = Flag{ - Name: "Connection", - LongForm: "connection", - ShortForm: "c", - Help: "Name of the connection this user should be created in.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Connection", + LongForm: "connection", + ShortForm: "c", + Help: "Name of the connection this user should be created in.", + IsRequired: true, } userEmail = Flag{ - Name: "Email", - LongForm: "email", - ShortForm: "e", - Help: "The user's email.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Email", + LongForm: "email", + ShortForm: "e", + Help: "The user's email.", + IsRequired: true, } userPassword = Flag{ - Name: "Password", - LongForm: "password", - ShortForm: "p", - Help: "Initial password for this user (mandatory for non-SMS connections)", - IsRequired: true, - AlwaysPrompt: false, + Name: "Password", + LongForm: "password", + ShortForm: "p", + Help: "Initial password for this user (mandatory for non-SMS connections).", + IsRequired: true, } userUsername = Flag{ - Name: "Username", - LongForm: "username", - ShortForm: "u", - Help: "The user's username. Only valid if the connection requires a username.", - IsRequired: false, - AlwaysPrompt: false, + Name: "Username", + LongForm: "username", + ShortForm: "u", + Help: "The user's username. Only valid if the connection requires a username.", } userName = Flag{ - Name: "Name", - LongForm: "name", - ShortForm: "n", - Help: "The user's full name.", - IsRequired: true, - AlwaysPrompt: false, + Name: "Name", + LongForm: "name", + ShortForm: "n", + Help: "The user's full name.", + IsRequired: true, + AlwaysPrompt: true, + } + userQuery = Flag{ + Name: "Query", + LongForm: "query", + ShortForm: "q", + Help: "Query in Lucene query string syntax. See https://auth0.com/docs/users/user-search/user-search-query-syntax for more details.", + } + userSort = Flag{ + Name: "Sort", + LongForm: "sort", + ShortForm: "s", + Help: "Field to sort by. Use 'field:order' where 'order' is '1' for ascending and '-1' for descending. e.g. 'created_at:1'.", } ) @@ -67,7 +71,7 @@ func usersCmd(cli *cli) *cobra.Command { } cmd.SetUsageTemplate(resourceUsageTemplate()) - cmd.AddCommand(listUsersCmd(cli)) + cmd.AddCommand(searchUsersCmd(cli)) cmd.AddCommand(createUserCmd(cli)) cmd.AddCommand(showUserCmd(cli)) cmd.AddCommand(deleteUserCmd(cli)) @@ -78,57 +82,82 @@ func usersCmd(cli *cli) *cobra.Command { return cmd } -func listUsersCmd(cli *cli) *cobra.Command { +func searchUsersCmd(cli *cli) *cobra.Command { + var inputs struct { + query string + sort string + } + cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Args: cobra.NoArgs, - Short: "List your users.", - Long: `List your existing users. To create one try: + Use: "search", + Args: cobra.NoArgs, + Short: "search for users", + Long: `search for users. To create one try: auth0 users create`, - Example: `auth0 users list -auth0 users ls`, + Example: `auth0 users search +auth0 users search --query id +auth0 users search -q name --sort "name:1" +auth0 users search -q name -s "name:1"`, RunE: func(cmd *cobra.Command, args []string) error { - var list *management.UserList + + if err := userQuery.Ask(cmd, &inputs.query, nil); err != nil { + return err + } + + if err := userSort.Ask(cmd, &inputs.sort, nil); err != nil { + return err + } + + search := &management.UserList{} + + var queryParams []management.RequestOption + + if len(inputs.sort) == 0 { + queryParams = append(queryParams, management.Query(auth0.StringValue(&inputs.query))) + } else { + queryParams = append(queryParams, + management.Query(auth0.StringValue(&inputs.query)), + management.Parameter("sort", auth0.StringValue(&inputs.sort)), + ) + } if err := ansi.Waiting(func() error { var err error - list, err = cli.api.User.List() + search, err = cli.api.User.Search(queryParams...) return err }); err != nil { return fmt.Errorf("An unexpected error occurred: %w", err) } - cli.renderer.UserList(list.Users) + cli.renderer.UserSearch(search.Users) return nil }, } + userQuery.RegisterString(cmd, &inputs.query, "") + userSort.RegisterString(cmd, &inputs.sort, "") return cmd } func createUserCmd(cli *cli) *cobra.Command { var inputs struct { - Connection string - Email string - Password string - Username string - Name string + Connection string + Email string + Password string + Username string + Name string } cmd := &cobra.Command{ Use: "create", Args: cobra.NoArgs, - Short: "Create a new user.", - Long: "Create a new user.", + Short: "Create a new user", + Long: "Create a new user", Example: `auth0 users create auth0 users create --name "John Doe" -auth0 users create -n "John Doe" --email john@example.com`, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, +auth0 users create -n "John Doe" --email john@example.com +auth0 users create -n "John Doe" --e john@example.com --connection "Username-Password-Authentication"`, RunE: func(cmd *cobra.Command, args []string) error { - // Select from the available connection types // Users API currently support database connections if err := userConnection.Select(cmd, &inputs.Connection, cli.connectionPickerOptions(), nil); err != nil { @@ -145,10 +174,8 @@ auth0 users create -n "John Doe" --email john@example.com`, return err } - // Prompt for user password - if err := userPassword.Ask(cmd, &inputs.Password, nil); err != nil { - return err - } + ////Prompt for user password + inputs.Password = prompt.Password(inputs.Password) // The getConnReqUsername returns the value for the requires_username field for the selected connection // The result will be used to determine whether to prompt for username @@ -165,22 +192,21 @@ auth0 users create -n "John Doe" --email john@example.com`, return err } a = &management.User{ - Connection: &inputs.Connection, - Email: &inputs.Email, - Name: &inputs.Name, - Username: &inputs.Username, - Password: &inputs.Password, + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + Username: &inputs.Username, + Password: &inputs.Password, } } else { a = &management.User{ - Connection: &inputs.Connection, - Email: &inputs.Email, - Name: &inputs.Name, - Password: &inputs.Password, + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + Password: &inputs.Password, } } - // Create app if err := ansi.Waiting(func() error { return cli.api.User.Create(a) @@ -211,17 +237,13 @@ func showUserCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "show", Args: cobra.MaximumNArgs(1), - Short: "Show an existing user.", - Long: "Show an existing user.", + Short: "Show an existing user", + Long: "Show an existing user", Example: `auth0 users show auth0 users show `, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) - if err != nil { + if err := userID.Ask(cmd, &inputs.ID); err != nil { return err } } else { @@ -229,12 +251,13 @@ auth0 users show `, } a := &management.User{ID: &inputs.ID} + if err := ansi.Waiting(func() error { var err error a, err = cli.api.User.Read(inputs.ID) return err }); err != nil { - return fmt.Errorf("Unable to load users. The Id %v specified doesn't exist", inputs.ID) + return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } cli.renderer.UserShow(a) @@ -253,17 +276,13 @@ func deleteUserCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "delete", Args: cobra.MaximumNArgs(1), - Short: "Delete a User", - Long: "Delete a User.", + Short: "Delete a user", + Long: "Delete a user", Example: `auth0 users delete -auth0 users delete `, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, +auth0 users delete `, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) - if err != nil { + if err := userID.Ask(cmd, &inputs.ID); err != nil { return err } } else { @@ -276,11 +295,11 @@ auth0 users delete `, } } - return ansi.Spinner("Deleting User", func() error { + return ansi.Spinner("Deleting user", func() error { _, err := cli.api.User.Read(inputs.ID) if err != nil { - return fmt.Errorf("Unable to delete User. The specified Id: %v doesn't exist", inputs.ID) + return fmt.Errorf("Unable to delete user. The specified Id: %v doesn't exist", inputs.ID) } return cli.api.User.Delete(inputs.ID) @@ -293,52 +312,47 @@ auth0 users delete `, func updateUserCmd(cli *cli) *cobra.Command { var inputs struct { - ID string - Email string - Password string - Name string - Connection string + ID string + Email string + Password string + Name string + Connection string } cmd := &cobra.Command{ Use: "update", Args: cobra.MaximumNArgs(1), - Short: "Update a User", - Long: "Update a User.", + Short: "Update a user", + Long: "Update a user", Example: `auth0 users update auth0 users update auth0 users update --name John Doe auth0 users update -n John Doe --email john.doe@gmail.com`, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, RunE: func(cmd *cobra.Command, args []string) error { - var current *management.User - if len(args) == 0 { - err := userID.Pick(cmd, &inputs.ID, cli.userPickerOptions) - if err != nil { + if err := userID.Ask(cmd, &inputs.ID); err != nil { return err } } else { inputs.ID = args[0] } + var current *management.User + if err := ansi.Waiting(func() error { var err error current, err = cli.api.User.Read(inputs.ID) return err }); err != nil { - return fmt.Errorf("Unable to load User. The Id %v specified doesn't exist", inputs.ID) + return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } - // using getUserConnection to get connection name from user Identities // just using current.connection will return empty conn := stringSliceToCommaSeparatedString(cli.getUserConnection(current)) current.Connection = auth0.String(conn) - if err := userConnection.AskU(cmd, &inputs.Connection, current.Connection); err != nil { + if err := userConnection.AskU(cmd, &inputs.Connection, current.Connection); err != nil { return err } @@ -346,7 +360,6 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, return err } - if err := userEmail.AskU(cmd, &inputs.Email, current.Email); err != nil { return err } @@ -389,14 +402,13 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, if err := ansi.Waiting(func() error { return cli.api.User.Update(current.GetID(), user) }); err != nil { - return fmt.Errorf("An unexpected error occurred while trying to update an User with Id '%s': %w", inputs.ID, err) + return fmt.Errorf("An unexpected error occurred while trying to update an user with Id '%s': %w", inputs.ID, err) } cli.renderer.UserUpdate(user) return nil }, } - userName.RegisterStringU(cmd, &inputs.Name, "") userConnection.RegisterStringU(cmd, &inputs.Connection, "") userPassword.RegisterStringU(cmd, &inputs.Password, "") @@ -429,9 +441,6 @@ func listUserBlocksCmd(cli *cli) *cobra.Command { auth0 users blocks list `, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := userID.Ask(cmd, &inputs.userID); err != nil { @@ -474,9 +483,6 @@ func deleteUserBlocksCmd(cli *cli) *cobra.Command { auth0 users unblock `, - PreRun: func(cmd *cobra.Command, args []string) { - prepareInteractivity(cmd) - }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := userID.Ask(cmd, &inputs.userID); err != nil { @@ -501,32 +507,13 @@ auth0 users unblock return cmd } -func (c *cli) userPickerOptions() (pickerOptions, error) { - list, err := c.api.User.List() - if err != nil { - return nil, err - } - - - var opts pickerOptions - for _, r := range list.Users { - label := fmt.Sprintf("%s %s", r.GetName(), ansi.Faint("("+r.GetID()+")")) - - opts = append(opts, pickerOption{value: r.GetID(), label: label}) - } - - if len(opts) == 0 { - return nil, errors.New("There are currently no users.") - } - - return opts, nil -} - func (c *cli) connectionPickerOptions() []string { - var list *management.ConnectionList - var res []string - list, _ = c.api.Connection.List() + + list, err := c.api.Connection.List() + if err != nil { + fmt.Println(err) + } for _, conn := range list.Connections { res = append(res, conn.GetName()) @@ -534,30 +521,32 @@ func (c *cli) connectionPickerOptions() []string { return res } -func (c *cli)getUserConnection(users *management.User) []string { +func (c *cli) getUserConnection(users *management.User) []string { var res []string - for _, i := range users.Identities{ + for _, i := range users.Identities { res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection))) } return res } + // Since the option field is ignored with `json:"-"` in Connections // This is a workaround to get the requires_username field nested inside Options field type Options struct { - RequiresUsername bool `json:"requires_username"` + RequiresUsername bool `json:"requires_username"` } func (c *cli) getConnReqUsername(s string) *bool { - var conn *management.Connection - conn, _ = c.api.Connection.ReadByName(s) + conn, err := c.api.Connection.ReadByName(s) + if err != nil { + fmt.Println(err) + } res := fmt.Sprintln(conn.Options) opts := Options{} if err := json.Unmarshal([]byte(res), &opts); err != nil { - panic(err) + fmt.Println(err) } return auth0.Bool(opts.RequiresUsername) } - diff --git a/internal/display/users.go b/internal/display/users.go index a627c57db..8ee1facfb 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -4,23 +4,20 @@ import ( "fmt" "strings" + "github.com/auth0/auth0-cli/internal/ansi" "gopkg.in/auth0.v5" "gopkg.in/auth0.v5/management" ) type userView struct { - UserID string - Name string - Username string - Email string - Connection string + UserID string + Email string + Connection string } func (v *userView) AsTableHeader() []string { return []string{ "UserID", - "Name", - "Username", "Email", "Connection", } @@ -28,9 +25,7 @@ func (v *userView) AsTableHeader() []string { func (v *userView) AsTableRow() []string { return []string{ - v.UserID, - v.Name, - v.Username, + ansi.Faint(v.UserID), v.Email, v.Connection, } @@ -38,38 +33,14 @@ func (v *userView) AsTableRow() []string { func (v *userView) KeyValues() [][]string { return [][]string{ - []string{"USER ID", v.UserID}, - []string{"NAME", v.Name}, - []string{"USERNAME", v.Username}, + []string{"USERID", ansi.Faint(v.UserID)}, []string{"EMAIL", v.Email}, []string{"CONNECTION", v.Connection}, } } -type userListView struct { - UserID string - Name string - Username string - Email string - Connection string -} - -func (v *userListView) AsTableHeader() []string { - return []string{"User ID", "Name", "Username", "Email", "Connection"} -} - -func (v *userListView) AsTableRow() []string{ - return []string{ - v.UserID, - v.Name, - v.Username, - v.Email, - v.Connection, - } -} - -func (r *Renderer) UserList(users []*management.User) { - resource := "users" +func (r *Renderer) UserSearch(users []*management.User) { + resource := "user" r.Heading(resource) @@ -82,11 +53,9 @@ func (r *Renderer) UserList(users []*management.User) { var res []View for _, c := range users { conn := getUserConnection(c) - res = append(res, &userListView{ - UserID: auth0.StringValue(c.ID), - Name: auth0.StringValue(c.Name), - Username: auth0.StringValue(c.Username), - Email: auth0.StringValue(c.Email), + res = append(res, &userView{ + UserID: ansi.Faint(auth0.StringValue(c.ID)), + Email: auth0.StringValue(c.Email), Connection: stringSliceToCommaSeparatedString(conn), }) } @@ -94,46 +63,41 @@ func (r *Renderer) UserList(users []*management.User) { r.Results(res) } -func (r *Renderer) UserShow(users *management.User) { - r.Heading("users") +func (r *Renderer) UserShow(users *management.User) { + r.Heading("user") conn := getUserConnection(users) v := &userView{ - UserID: auth0.StringValue(users.ID), - Name: auth0.StringValue(users.Name), - Username: auth0.StringValue(users.Username), + UserID: ansi.Faint(auth0.StringValue(users.ID)), Email: auth0.StringValue(users.Email), Connection: stringSliceToCommaSeparatedString(conn), } + r.Result(v) } -func (r *Renderer) UserCreate(users *management.User) { - r.Heading("users created") +func (r *Renderer) UserCreate(users *management.User) { + r.Heading("user created") v := &userView{ - UserID: auth0.StringValue(users.ID), - Connection: auth0.StringValue(users.Connection), - Name: auth0.StringValue(users.Name), - Username: auth0.StringValue(users.Username), + UserID: ansi.Faint(auth0.StringValue(users.ID)), Email: auth0.StringValue(users.Email), + Connection: auth0.StringValue(users.Connection), } r.Result(v) } -func (r *Renderer) UserUpdate(users *management.User) { - r.Heading("users updated") +func (r *Renderer) UserUpdate(users *management.User) { + r.Heading("user updated") conn := getUserConnection(users) v := &userView{ - UserID: auth0.StringValue(users.ID), - Connection: stringSliceToCommaSeparatedString(conn), - Name: auth0.StringValue(users.Name), - Username: auth0.StringValue(users.Username), + UserID: auth0.StringValue(users.ID), Email: auth0.StringValue(users.Email), + Connection: stringSliceToCommaSeparatedString(conn), } r.Result(v) @@ -141,7 +105,7 @@ func (r *Renderer) UserUpdate(users *management.User) { func getUserConnection(users *management.User) []string { var res []string - for _, i := range users.Identities{ + for _, i := range users.Identities { res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection))) } @@ -150,4 +114,4 @@ func getUserConnection(users *management.User) []string { func stringSliceToCommaSeparatedString(s []string) string { return strings.Join(s, ", ") -} \ No newline at end of file +} diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 586a8e49c..0020e7494 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -1,6 +1,7 @@ package prompt import ( + "fmt" "os" "github.com/AlecAivazis/survey/v2" @@ -92,3 +93,15 @@ func SelectInput(name string, message string, help string, options []string, def return input } + +func Password(inputs string) string { + prompt := &survey.Password{ + Message: "Password:", + } + + if err := askOne(prompt, &inputs); err != nil { + fmt.Println(err) + } + + return inputs +} From 1fb585e17fe66526b0ff332959a7dba385de2158 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Tue, 4 May 2021 08:57:39 -0400 Subject: [PATCH 06/29] [CLI-148] refactor: run gofmt on users files --- internal/auth0/auth0.go | 4 ++-- internal/cli/users.go | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index 9c687b6bb..25d3e1967 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -20,7 +20,7 @@ type API struct { Rule RuleAPI Tenant TenantAPI User UserAPI - Connection ConnectionAPI + Connection ConnectionAPI } func NewAPI(m *management.Management) *API { @@ -37,7 +37,7 @@ func NewAPI(m *management.Management) *API { Rule: m.Rule, Tenant: m.Tenant, User: m.User, - Connection: m.Connection, + Connection: m.Connection, } } diff --git a/internal/cli/users.go b/internal/cli/users.go index 7d7e2a7cb..191e3578a 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -206,7 +206,6 @@ auth0 users create -n "John Doe" --e john@example.com --connection "Username-Pas Password: &inputs.Password, } } - // Create app if err := ansi.Waiting(func() error { return cli.api.User.Create(a) @@ -346,7 +345,6 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, }); err != nil { return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } - // using getUserConnection to get connection name from user Identities // just using current.connection will return empty conn := stringSliceToCommaSeparatedString(cli.getUserConnection(current)) From ae549e1acd3f318e9f42eb7e27013647a41baf14 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Tue, 4 May 2021 09:02:38 -0400 Subject: [PATCH 07/29] [CLI-148] refactor: cleanup whitespaces --- internal/auth0/connection.go | 2 +- internal/auth0/user.go | 2 +- internal/display/users.go | 2 +- internal/prompt/prompt.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/auth0/connection.go b/internal/auth0/connection.go index 9c9a32878..c14b75f57 100644 --- a/internal/auth0/connection.go +++ b/internal/auth0/connection.go @@ -23,4 +23,4 @@ type ConnectionAPI interface { // List all connections. List(opts ...management.RequestOption) (ul *management.ConnectionList, err error) -} +} \ No newline at end of file diff --git a/internal/auth0/user.go b/internal/auth0/user.go index 5e7123a3a..c7754d20e 100644 --- a/internal/auth0/user.go +++ b/internal/auth0/user.go @@ -29,4 +29,4 @@ type UserAPI interface { // Search for users Search(opts ...management.RequestOption) (us *management.UserList, err error) -} +} \ No newline at end of file diff --git a/internal/display/users.go b/internal/display/users.go index 8ee1facfb..9f1963020 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -114,4 +114,4 @@ func getUserConnection(users *management.User) []string { func stringSliceToCommaSeparatedString(s []string) string { return strings.Join(s, ", ") -} +} \ No newline at end of file diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 0020e7494..b14cf0aa4 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -104,4 +104,4 @@ func Password(inputs string) string { } return inputs -} +} \ No newline at end of file From 07c73aa4fac17d886f88c96ab30610103557463a Mon Sep 17 00:00:00 2001 From: bright-poku Date: Tue, 4 May 2021 14:40:32 -0400 Subject: [PATCH 08/29] [CLI-148] refactored getconnrequusername function --- internal/cli/users.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 191e3578a..656a5c383 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -524,12 +524,7 @@ func (c *cli) getUserConnection(users *management.User) []string { return res } -// Since the option field is ignored with `json:"-"` in Connections // This is a workaround to get the requires_username field nested inside Options field -type Options struct { - RequiresUsername bool `json:"requires_username"` -} - func (c *cli) getConnReqUsername(s string) *bool { conn, err := c.api.Connection.ReadByName(s) if err != nil { @@ -537,10 +532,10 @@ func (c *cli) getConnReqUsername(s string) *bool { } res := fmt.Sprintln(conn.Options) - opts := Options{} + opts := &management.ConnectionOptions{} if err := json.Unmarshal([]byte(res), &opts); err != nil { fmt.Println(err) } - return auth0.Bool(opts.RequiresUsername) + return opts.RequiresUsername } From c807b80114a2022a991688d84d28763d72d5092a Mon Sep 17 00:00:00 2001 From: bright-poku Date: Tue, 4 May 2021 22:46:22 -0400 Subject: [PATCH 09/29] [CLI-148] refactor: run gofmt on project user files --- internal/auth0/connection.go | 2 +- internal/auth0/user.go | 2 +- internal/cli/users.go | 3 ++- internal/display/users.go | 2 +- internal/prompt/prompt.go | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/auth0/connection.go b/internal/auth0/connection.go index c14b75f57..9c9a32878 100644 --- a/internal/auth0/connection.go +++ b/internal/auth0/connection.go @@ -23,4 +23,4 @@ type ConnectionAPI interface { // List all connections. List(opts ...management.RequestOption) (ul *management.ConnectionList, err error) -} \ No newline at end of file +} diff --git a/internal/auth0/user.go b/internal/auth0/user.go index c7754d20e..5e7123a3a 100644 --- a/internal/auth0/user.go +++ b/internal/auth0/user.go @@ -29,4 +29,4 @@ type UserAPI interface { // Search for users Search(opts ...management.RequestOption) (us *management.UserList, err error) -} \ No newline at end of file +} diff --git a/internal/cli/users.go b/internal/cli/users.go index 656a5c383..da6caeaa8 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -508,10 +508,10 @@ func (c *cli) connectionPickerOptions() []string { if err != nil { fmt.Println(err) } - for _, conn := range list.Connections { res = append(res, conn.GetName()) } + return res } @@ -521,6 +521,7 @@ func (c *cli) getUserConnection(users *management.User) []string { res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection))) } + return res } diff --git a/internal/display/users.go b/internal/display/users.go index 9f1963020..8ee1facfb 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -114,4 +114,4 @@ func getUserConnection(users *management.User) []string { func stringSliceToCommaSeparatedString(s []string) string { return strings.Join(s, ", ") -} \ No newline at end of file +} diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index b14cf0aa4..0020e7494 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -104,4 +104,4 @@ func Password(inputs string) string { } return inputs -} \ No newline at end of file +} From 89e8a4a89fc1c42afe997140348d4e7f673be67a Mon Sep 17 00:00:00 2001 From: bright-poku Date: Fri, 7 May 2021 11:25:24 -0400 Subject: [PATCH 10/29] [CLI-148] refactor: added code review suggestions --- internal/cli/users.go | 71 ++++++++++++++++++++------------------- internal/display/users.go | 71 ++++++++++++++++++++++++++++----------- internal/prompt/prompt.go | 4 +-- 3 files changed, 89 insertions(+), 57 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index da6caeaa8..5f31fed05 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -51,10 +51,11 @@ var ( AlwaysPrompt: true, } userQuery = Flag{ - Name: "Query", - LongForm: "query", - ShortForm: "q", - Help: "Query in Lucene query string syntax. See https://auth0.com/docs/users/user-search/user-search-query-syntax for more details.", + Name: "Query", + LongForm: "query", + ShortForm: "q", + Help: "Query in Lucene query string syntax. See https://auth0.com/docs/users/user-search/user-search-query-syntax for more details.", + IsRequired: true, } userSort = Flag{ Name: "Sort", @@ -91,23 +92,18 @@ func searchUsersCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "search", Args: cobra.NoArgs, - Short: "search for users", - Long: `search for users. To create one try: + Short: "Search for users", + Long: `Search for users. To create one try: auth0 users create`, Example: `auth0 users search auth0 users search --query id auth0 users search -q name --sort "name:1" auth0 users search -q name -s "name:1"`, RunE: func(cmd *cobra.Command, args []string) error { - if err := userQuery.Ask(cmd, &inputs.query, nil); err != nil { return err } - if err := userSort.Ask(cmd, &inputs.sort, nil); err != nil { - return err - } - search := &management.UserList{} var queryParams []management.RequestOption @@ -133,6 +129,7 @@ auth0 users search -q name -s "name:1"`, return nil }, } + userQuery.RegisterString(cmd, &inputs.query, "") userSort.RegisterString(cmd, &inputs.sort, "") @@ -152,7 +149,7 @@ func createUserCmd(cli *cli) *cobra.Command { Use: "create", Args: cobra.NoArgs, Short: "Create a new user", - Long: "Create a new user", + Long: "Create a new user.", Example: `auth0 users create auth0 users create --name "John Doe" auth0 users create -n "John Doe" --email john@example.com @@ -175,36 +172,31 @@ auth0 users create -n "John Doe" --e john@example.com --connection "Username-Pas } ////Prompt for user password - inputs.Password = prompt.Password(inputs.Password) + var err error + if inputs.Password, err = prompt.Password(inputs.Password); err != nil { + return err + } // The getConnReqUsername returns the value for the requires_username field for the selected connection // The result will be used to determine whether to prompt for username conn := cli.getConnReqUsername(auth0.StringValue(&inputs.Connection)) requireUsername := auth0.BoolValue(conn) - var a *management.User - // Prompt for username if the requireUsername is set to true // Load values including the username's field into a fresh users instance // Else block loads values without username for connections with requireUsername set to false + a := &management.User{ + Connection: &inputs.Connection, + Email: &inputs.Email, + Name: &inputs.Name, + Password: &inputs.Password, + } + if requireUsername { if err := userUsername.Ask(cmd, &inputs.Username, nil); err != nil { return err } - a = &management.User{ - Connection: &inputs.Connection, - Email: &inputs.Email, - Name: &inputs.Name, - Username: &inputs.Username, - Password: &inputs.Password, - } - } else { - a = &management.User{ - Connection: &inputs.Connection, - Email: &inputs.Email, - Name: &inputs.Name, - Password: &inputs.Password, - } + a.Username = &inputs.Username } // Create app if err := ansi.Waiting(func() error { @@ -214,7 +206,7 @@ auth0 users create -n "John Doe" --e john@example.com --connection "Username-Pas } // Render Result - cli.renderer.UserCreate(a) + cli.renderer.UserCreate(a, requireUsername) return nil }, @@ -237,7 +229,7 @@ func showUserCmd(cli *cli) *cobra.Command { Use: "show", Args: cobra.MaximumNArgs(1), Short: "Show an existing user", - Long: "Show an existing user", + Long: "Show an existing user.", Example: `auth0 users show auth0 users show `, RunE: func(cmd *cobra.Command, args []string) error { @@ -259,7 +251,12 @@ auth0 users show `, return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } - cli.renderer.UserShow(a) + conn := stringSliceToCommaSeparatedString(cli.getUserConnection(a)) + a.Connection = auth0.String(conn) + con := cli.getConnReqUsername(auth0.StringValue(a.Connection)) + requireUsername := auth0.BoolValue(con) + + cli.renderer.UserShow(a, requireUsername) return nil }, } @@ -276,7 +273,7 @@ func deleteUserCmd(cli *cli) *cobra.Command { Use: "delete", Args: cobra.MaximumNArgs(1), Short: "Delete a user", - Long: "Delete a user", + Long: "Delete a user.", Example: `auth0 users delete auth0 users delete `, RunE: func(cmd *cobra.Command, args []string) error { @@ -322,7 +319,7 @@ func updateUserCmd(cli *cli) *cobra.Command { Use: "update", Args: cobra.MaximumNArgs(1), Short: "Update a user", - Long: "Update a user", + Long: "Update a user.", Example: `auth0 users update auth0 users update auth0 users update --name John Doe @@ -403,10 +400,14 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, return fmt.Errorf("An unexpected error occurred while trying to update an user with Id '%s': %w", inputs.ID, err) } - cli.renderer.UserUpdate(user) + con := cli.getConnReqUsername(auth0.StringValue(&inputs.Connection)) + requireUsername := auth0.BoolValue(con) + + cli.renderer.UserUpdate(user, requireUsername) return nil }, } + userName.RegisterStringU(cmd, &inputs.Name, "") userConnection.RegisterStringU(cmd, &inputs.Connection, "") userPassword.RegisterStringU(cmd, &inputs.Password, "") diff --git a/internal/display/users.go b/internal/display/users.go index 8ee1facfb..ffd69b46e 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -10,12 +10,22 @@ import ( ) type userView struct { - UserID string - Email string - Connection string + UserID string + Email string + Connection string + Username string + RequireUsername bool } func (v *userView) AsTableHeader() []string { + if v.RequireUsername { + return []string{ + "UserID", + "Email", + "Connection", + "Username", + } + } return []string{ "UserID", "Email", @@ -24,6 +34,14 @@ func (v *userView) AsTableHeader() []string { } func (v *userView) AsTableRow() []string { + if v.RequireUsername { + return []string{ + ansi.Faint(v.UserID), + v.Email, + v.Connection, + v.Username, + } + } return []string{ ansi.Faint(v.UserID), v.Email, @@ -32,6 +50,14 @@ func (v *userView) AsTableRow() []string { } func (v *userView) KeyValues() [][]string { + if v.RequireUsername { + return [][]string{ + []string{"USERID", ansi.Faint(v.UserID)}, + []string{"EMAIL", v.Email}, + []string{"CONNECTION", v.Connection}, + []string{"USERNAME", v.Username}, + } + } return [][]string{ []string{"USERID", ansi.Faint(v.UserID)}, []string{"EMAIL", v.Email}, @@ -54,50 +80,55 @@ func (r *Renderer) UserSearch(users []*management.User) { for _, c := range users { conn := getUserConnection(c) res = append(res, &userView{ - UserID: ansi.Faint(auth0.StringValue(c.ID)), - Email: auth0.StringValue(c.Email), - Connection: stringSliceToCommaSeparatedString(conn), + UserID: ansi.Faint(auth0.StringValue(c.ID)), + Email: auth0.StringValue(c.Email), + Connection: stringSliceToCommaSeparatedString(conn), + Username: auth0.StringValue(c.Username), }) } r.Results(res) } -func (r *Renderer) UserShow(users *management.User) { +func (r *Renderer) UserShow(users *management.User, requireUsername bool) { r.Heading("user") conn := getUserConnection(users) - v := &userView{ - UserID: ansi.Faint(auth0.StringValue(users.ID)), - Email: auth0.StringValue(users.Email), - Connection: stringSliceToCommaSeparatedString(conn), + RequireUsername: requireUsername, + UserID: ansi.Faint(auth0.StringValue(users.ID)), + Email: auth0.StringValue(users.Email), + Connection: stringSliceToCommaSeparatedString(conn), + Username: auth0.StringValue(users.Username), } r.Result(v) } -func (r *Renderer) UserCreate(users *management.User) { +func (r *Renderer) UserCreate(users *management.User, requireUsername bool) { r.Heading("user created") v := &userView{ - UserID: ansi.Faint(auth0.StringValue(users.ID)), - Email: auth0.StringValue(users.Email), - Connection: auth0.StringValue(users.Connection), + RequireUsername: requireUsername, + UserID: ansi.Faint(auth0.StringValue(users.ID)), + Email: auth0.StringValue(users.Email), + Connection: auth0.StringValue(users.Connection), + Username: auth0.StringValue(users.Username), } r.Result(v) } -func (r *Renderer) UserUpdate(users *management.User) { +func (r *Renderer) UserUpdate(users *management.User, requireUsername bool) { r.Heading("user updated") conn := getUserConnection(users) - v := &userView{ - UserID: auth0.StringValue(users.ID), - Email: auth0.StringValue(users.Email), - Connection: stringSliceToCommaSeparatedString(conn), + RequireUsername: requireUsername, + UserID: auth0.StringValue(users.ID), + Email: auth0.StringValue(users.Email), + Connection: stringSliceToCommaSeparatedString(conn), + Username: auth0.StringValue(users.Username), } r.Result(v) diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 0020e7494..eba5c64e9 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -94,7 +94,7 @@ func SelectInput(name string, message string, help string, options []string, def return input } -func Password(inputs string) string { +func Password(inputs string) (string, error) { prompt := &survey.Password{ Message: "Password:", } @@ -103,5 +103,5 @@ func Password(inputs string) string { fmt.Println(err) } - return inputs + return inputs, nil } From d583efa33882350c209b927a554c69336a7f2a0a Mon Sep 17 00:00:00 2001 From: bright-poku Date: Fri, 7 May 2021 13:36:53 -0400 Subject: [PATCH 11/29] [CLI-148] refactor: add users open command --- internal/cli/users.go | 38 ++++++++++++++++++++++++++++++++++++++ internal/display/users.go | 12 ++++++------ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 5f31fed05..63bb5363d 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -8,6 +8,7 @@ import ( "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" + "net/url" ) var ( @@ -77,6 +78,7 @@ func usersCmd(cli *cli) *cobra.Command { cmd.AddCommand(showUserCmd(cli)) cmd.AddCommand(deleteUserCmd(cli)) cmd.AddCommand(updateUserCmd(cli)) + cmd.AddCommand(openUserCmd(cli)) cmd.AddCommand(userBlocksCmd(cli)) cmd.AddCommand(deleteUserBlocksCmd(cli)) @@ -416,6 +418,35 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, return cmd } +func openUserCmd(cli *cli) *cobra.Command { + var inputs struct { + ID string + } + + cmd := &cobra.Command{ + Use: "open", + Args: cobra.MaximumNArgs(1), + Short: "Open user details page in Auth0 Manage", + Long: "Open user details page in Auth0 Manage.", + Example: `auth0 users open +auth0 users open "auth0|xxxxxxxxxx"`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := userID.Ask(cmd, &inputs.ID); err != nil { + return err + } + } else { + inputs.ID = args[0] + } + + openManageURL(cli, cli.config.DefaultTenant, formatUserDetailsPath(url.PathEscape(inputs.ID))) + return nil + }, + } + + return cmd +} + func userBlocksCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "blocks", @@ -502,6 +533,13 @@ func deleteUserBlocksCmd(cli *cli) *cobra.Command { return cmd } +func formatUserDetailsPath(id string) string { + if len(id) == 0 { + return "" + } + return fmt.Sprintf("users/%s", id) +} + func (c *cli) connectionPickerOptions() []string { var res []string diff --git a/internal/display/users.go b/internal/display/users.go index ffd69b46e..ddce4fc1f 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -80,10 +80,10 @@ func (r *Renderer) UserSearch(users []*management.User) { for _, c := range users { conn := getUserConnection(c) res = append(res, &userView{ - UserID: ansi.Faint(auth0.StringValue(c.ID)), - Email: auth0.StringValue(c.Email), - Connection: stringSliceToCommaSeparatedString(conn), - Username: auth0.StringValue(c.Username), + UserID: ansi.Faint(auth0.StringValue(c.ID)), + Email: auth0.StringValue(c.Email), + Connection: stringSliceToCommaSeparatedString(conn), + Username: auth0.StringValue(c.Username), }) } @@ -113,7 +113,7 @@ func (r *Renderer) UserCreate(users *management.User, requireUsername bool) { UserID: ansi.Faint(auth0.StringValue(users.ID)), Email: auth0.StringValue(users.Email), Connection: auth0.StringValue(users.Connection), - Username: auth0.StringValue(users.Username), + Username: auth0.StringValue(users.Username), } r.Result(v) @@ -128,7 +128,7 @@ func (r *Renderer) UserUpdate(users *management.User, requireUsername bool) { UserID: auth0.StringValue(users.ID), Email: auth0.StringValue(users.Email), Connection: stringSliceToCommaSeparatedString(conn), - Username: auth0.StringValue(users.Username), + Username: auth0.StringValue(users.Username), } r.Result(v) From 8679b8d9fc1e3fbbf82a6c80eb841905a7729ce9 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Fri, 7 May 2021 13:45:11 -0400 Subject: [PATCH 12/29] [CLI-148] refactor: gofmt to cleanup --- internal/cli/users.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 63bb5363d..4c5b91ba2 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -253,10 +253,13 @@ auth0 users show `, return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } + // get current connection conn := stringSliceToCommaSeparatedString(cli.getUserConnection(a)) a.Connection = auth0.String(conn) - con := cli.getConnReqUsername(auth0.StringValue(a.Connection)) - requireUsername := auth0.BoolValue(con) + + // parse the connection name to get the requireUsername status + u := cli.getConnReqUsername(auth0.StringValue(a.Connection)) + requireUsername := auth0.BoolValue(u) cli.renderer.UserShow(a, requireUsername) return nil From 97d67f55be262c3e98ec0ea7fb9ec39a34e59fb7 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Fri, 7 May 2021 16:08:11 -0300 Subject: [PATCH 13/29] Apply suggestions from code review --- internal/cli/users.go | 2 +- internal/display/users.go | 20 ++------------------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 4c5b91ba2..819578cfc 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -328,7 +328,7 @@ func updateUserCmd(cli *cli) *cobra.Command { Example: `auth0 users update auth0 users update auth0 users update --name John Doe -auth0 users update -n John Doe --email john.doe@gmail.com`, +auth0 users update -n John Doe --email john.doe@example.com`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := userID.Ask(cmd, &inputs.ID); err != nil { diff --git a/internal/display/users.go b/internal/display/users.go index ddce4fc1f..327bce940 100644 --- a/internal/display/users.go +++ b/internal/display/users.go @@ -18,14 +18,6 @@ type userView struct { } func (v *userView) AsTableHeader() []string { - if v.RequireUsername { - return []string{ - "UserID", - "Email", - "Connection", - "Username", - } - } return []string{ "UserID", "Email", @@ -34,14 +26,6 @@ func (v *userView) AsTableHeader() []string { } func (v *userView) AsTableRow() []string { - if v.RequireUsername { - return []string{ - ansi.Faint(v.UserID), - v.Email, - v.Connection, - v.Username, - } - } return []string{ ansi.Faint(v.UserID), v.Email, @@ -52,14 +36,14 @@ func (v *userView) AsTableRow() []string { func (v *userView) KeyValues() [][]string { if v.RequireUsername { return [][]string{ - []string{"USERID", ansi.Faint(v.UserID)}, + []string{"ID", ansi.Faint(v.UserID)}, []string{"EMAIL", v.Email}, []string{"CONNECTION", v.Connection}, []string{"USERNAME", v.Username}, } } return [][]string{ - []string{"USERID", ansi.Faint(v.UserID)}, + []string{"ID", ansi.Faint(v.UserID)}, []string{"EMAIL", v.Email}, []string{"CONNECTION", v.Connection}, } From ef74b987aa14142e3cbd5dd83b83643a7ac92384 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 07:43:08 -0400 Subject: [PATCH 14/29] [CLI-148] refactor: password input --- internal/cli/flags.go | 19 ++++++++++++++++++- internal/cli/input.go | 11 +++++++++++ internal/cli/users.go | 5 ++--- internal/prompt/prompt.go | 14 +++++++------- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/internal/cli/flags.go b/internal/cli/flags.go index cef467ebd..ccbf14f0e 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -2,7 +2,6 @@ package cli import ( "fmt" - "github.com/AlecAivazis/survey/v2" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" @@ -135,6 +134,14 @@ func (f *Flag) EditorPromptU(cmd *cobra.Command, value *string, initialValue, fi return nil } +func (f *Flag) AskPassword(cmd *cobra.Command, value *string, defaultValue *string) error { + return askPasswordFlag(cmd, f, value, defaultValue, false) +} + +func (f *Flag) AskPasswordU(cmd *cobra.Command, value *string, defaultValue *string) error { + return askPasswordFlag(cmd, f, value, defaultValue, true) +} + func (f *Flag) RegisterString(cmd *cobra.Command, value *string, defaultValue string) { registerString(cmd, f, value, defaultValue, false) } @@ -205,6 +212,16 @@ func selectFlag(cmd *cobra.Command, f *Flag, value interface{}, options []string return nil } +func askPasswordFlag(cmd *cobra.Command, f *Flag, value *string, defaultValue *string, isUpdate bool) error { + if shouldAsk(cmd, f, isUpdate) { + if err := askPassword(cmd, f, value, defaultValue, isUpdate); err != nil { + return err + } + } + + return nil +} + func registerString(cmd *cobra.Command, f *Flag, value *string, defaultValue string, isUpdate bool) { cmd.Flags().StringVarP(value, f.LongForm, f.ShortForm, defaultValue, f.Help) diff --git a/internal/cli/input.go b/internal/cli/input.go index 2a1f66c01..e79bab79a 100644 --- a/internal/cli/input.go +++ b/internal/cli/input.go @@ -36,6 +36,17 @@ func askBool(cmd *cobra.Command, i commandInput, value *bool, defaultValue *bool return nil } +func askPassword(cmd *cobra.Command, i commandInput, value interface{}, defaultValue *string, isUpdate bool) error { + isRequired := !isUpdate && i.GetIsRequired() + input := prompt.Password("", i.GetLabel(), i.GetHelp(), auth0.StringValue(defaultValue), isRequired) + + if err := prompt.AskOne(input, value); err != nil { + return handleInputError(err) + } + + return nil +} + func _select(cmd *cobra.Command, i commandInput, value interface{}, options []string, defaultValue *string, isUpdate bool) error { isRequired := !isUpdate && i.GetIsRequired() diff --git a/internal/cli/users.go b/internal/cli/users.go index 4c5b91ba2..45ce66630 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -174,8 +174,7 @@ auth0 users create -n "John Doe" --e john@example.com --connection "Username-Pas } ////Prompt for user password - var err error - if inputs.Password, err = prompt.Password(inputs.Password); err != nil { + if err := userPassword.AskPassword(cmd, &inputs.Password, nil); err != nil { return err } @@ -364,7 +363,7 @@ auth0 users update -n John Doe --email john.doe@gmail.com`, return err } - if err := userPassword.AskU(cmd, &inputs.Password, current.Password); err != nil { + if err := userPassword.AskPasswordU(cmd, &inputs.Password, current.Password); err != nil { return err } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index eba5c64e9..1c0f976e2 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -1,7 +1,6 @@ package prompt import ( - "fmt" "os" "github.com/AlecAivazis/survey/v2" @@ -94,14 +93,15 @@ func SelectInput(name string, message string, help string, options []string, def return input } -func Password(inputs string) (string, error) { - prompt := &survey.Password{ - Message: "Password:", +func Password(name string, message string, help string, defaultValue string, required bool) *survey.Question { + input := &survey.Question{ + Name: name, + Prompt: &survey.Password{Message: message}, } - if err := askOne(prompt, &inputs); err != nil { - fmt.Println(err) + if required { + input.Validate = survey.Required } - return inputs, nil + return input } From 7c21f4d5494527255146517d38b4cfe8b0dd6804 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 09:07:42 -0400 Subject: [PATCH 15/29] [CLI-148] refactor: cleanup spaces --- internal/cli/input.go | 2 +- internal/prompt/prompt.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cli/input.go b/internal/cli/input.go index e79bab79a..aa249baa8 100644 --- a/internal/cli/input.go +++ b/internal/cli/input.go @@ -38,7 +38,7 @@ func askBool(cmd *cobra.Command, i commandInput, value *bool, defaultValue *bool func askPassword(cmd *cobra.Command, i commandInput, value interface{}, defaultValue *string, isUpdate bool) error { isRequired := !isUpdate && i.GetIsRequired() - input := prompt.Password("", i.GetLabel(), i.GetHelp(), auth0.StringValue(defaultValue), isRequired) + input := prompt.Password("", i.GetLabel(), auth0.StringValue(defaultValue), isRequired) if err := prompt.AskOne(input, value); err != nil { return handleInputError(err) diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 1c0f976e2..486d67f98 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -93,7 +93,7 @@ func SelectInput(name string, message string, help string, options []string, def return input } -func Password(name string, message string, help string, defaultValue string, required bool) *survey.Question { +func Password(name string, message string, defaultValue string, required bool) *survey.Question { input := &survey.Question{ Name: name, Prompt: &survey.Password{Message: message}, From f7c25a86de79ad2994f27417155a1cd1f934697e Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 09:20:52 -0400 Subject: [PATCH 16/29] [CLI-148] refactor: sort imports --- internal/cli/flags.go | 1 + internal/cli/users.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/cli/flags.go b/internal/cli/flags.go index ccbf14f0e..755eedcab 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "github.com/AlecAivazis/survey/v2" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" diff --git a/internal/cli/users.go b/internal/cli/users.go index 0a1192688..991a575e4 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -3,12 +3,13 @@ package cli import ( "encoding/json" "fmt" + "net/url" + "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" - "net/url" ) var ( From d74e2d845ed6693ccc6233e4e30f7964bfddde1f Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 11:52:01 -0400 Subject: [PATCH 17/29] [CLI-148] refactor: sync with master --- internal/cli/users.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 991a575e4..d78bfba72 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -253,7 +253,7 @@ auth0 users show `, return fmt.Errorf("Unable to load user. The Id %v specified doesn't exist", inputs.ID) } - // get current connection + // get the current connection conn := stringSliceToCommaSeparatedString(cli.getUserConnection(a)) a.Connection = auth0.String(conn) From 4e8007c97e51e8537d2f0831ac286e875a1b2324 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 13:47:12 -0400 Subject: [PATCH 18/29] [CLI-148] refactor: updated set of scopes --- pkg/auth0-cli-config-generator/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/auth0-cli-config-generator/main.go b/pkg/auth0-cli-config-generator/main.go index 955837529..eff4dd9d6 100644 --- a/pkg/auth0-cli-config-generator/main.go +++ b/pkg/auth0-cli-config-generator/main.go @@ -33,9 +33,10 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", - "read:users", "update:users", + "read:users", "update:users", "create:users", "delete:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", + "read:connections", "update:connections", } func (p params) validate() error { From 4aac3b54fd9818fae7cf658a51eed1ceb6a4b166 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 13:49:12 -0400 Subject: [PATCH 19/29] [CLI-148] refactor: updated order of scopes --- pkg/auth0-cli-config-generator/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/auth0-cli-config-generator/main.go b/pkg/auth0-cli-config-generator/main.go index eff4dd9d6..c1d34ebab 100644 --- a/pkg/auth0-cli-config-generator/main.go +++ b/pkg/auth0-cli-config-generator/main.go @@ -33,7 +33,7 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", - "read:users", "update:users", "create:users", "delete:users", + "create:users", "delete:users", "read:users", "update:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", "read:connections", "update:connections", From f44f4448a31eae05aa2b38500b27f9939e0e9085 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 14:27:20 -0400 Subject: [PATCH 20/29] [CLI-148] refactor: updated scopes in auth file --- internal/auth/auth.go | 2 +- pkg/auth0-cli-config-generator/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 5d0c64666..3052bc386 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -31,7 +31,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", "read:connections", "update:connections", - "read:users", "update:users", "create:users", "delete:users", + "create:users", "delete:users", "read:users", "update:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", } diff --git a/pkg/auth0-cli-config-generator/main.go b/pkg/auth0-cli-config-generator/main.go index c1d34ebab..5b871aa53 100644 --- a/pkg/auth0-cli-config-generator/main.go +++ b/pkg/auth0-cli-config-generator/main.go @@ -33,7 +33,7 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", - "create:users", "delete:users", "read:users", "update:users", + "create:users", "delete:users", "read:users", "update:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", "read:connections", "update:connections", From aa381aab9331f2a2abdb9f35b6ae0d914e78f828 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Mon, 10 May 2021 21:32:56 -0400 Subject: [PATCH 21/29] [CLi-148] refactor: clean up comments --- internal/cli/users.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index d78bfba72..fd2f04f46 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -186,7 +186,6 @@ auth0 users create -n "John Doe" --e john@example.com --connection "Username-Pas // Prompt for username if the requireUsername is set to true // Load values including the username's field into a fresh users instance - // Else block loads values without username for connections with requireUsername set to false a := &management.User{ Connection: &inputs.Connection, Email: &inputs.Email, From e2e649ee6fb28643724311b10847b552276c6423 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Wed, 12 May 2021 16:41:55 -0400 Subject: [PATCH 22/29] [CLi-148] refactor: updated scopes to match main branch --- pkg/auth0-cli-config-generator/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/auth0-cli-config-generator/main.go b/pkg/auth0-cli-config-generator/main.go index 5b871aa53..955837529 100644 --- a/pkg/auth0-cli-config-generator/main.go +++ b/pkg/auth0-cli-config-generator/main.go @@ -33,10 +33,9 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", - "create:users", "delete:users", "read:users", "update:users", + "read:users", "update:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", - "read:connections", "update:connections", } func (p params) validate() error { From cd1e2b233ad359f110ca6efe641f399812d2077a Mon Sep 17 00:00:00 2001 From: bright-poku Date: Wed, 12 May 2021 17:10:57 -0400 Subject: [PATCH 23/29] [CLi-148] refactor: cleanup --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 73452f9b1..56dc910db 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ tags # misc .vscode .DS_Store +.idea \ No newline at end of file From 0555fcbf820dd3033c8d341400e995693e23211f Mon Sep 17 00:00:00 2001 From: bright-poku Date: Wed, 12 May 2021 19:05:37 -0400 Subject: [PATCH 24/29] workflow test --- .github/workflows/go.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7f83dfa02..9d8ba3957 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,15 +23,15 @@ jobs: - name: ci run: PATH=$(go env GOPATH)/bin:$PATH make ci - - name: integration - # skip running this action if the PR is coming from a fork: - if: github.event.pull_request.head.repo.full_name == github.repository - shell: bash - run: PATH=$(go env GOPATH)/bin:$PATH make integration - env: - AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} - AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} - AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} - AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} - AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} - AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} +# - name: integration +# # skip running this action if the PR is coming from a fork: +# if: github.event.pull_request.head.repo.full_name == github.repository +# shell: bash +# run: PATH=$(go env GOPATH)/bin:$PATH make integration +# env: +# AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} +# AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} +# AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} +# AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} +# AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} +# AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} From a2c707ce74051e0f96fba603792cca5f3e641845 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Wed, 12 May 2021 19:23:22 -0400 Subject: [PATCH 25/29] workflow add back integration --- .github/workflows/go.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9d8ba3957..7f83dfa02 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,15 +23,15 @@ jobs: - name: ci run: PATH=$(go env GOPATH)/bin:$PATH make ci -# - name: integration -# # skip running this action if the PR is coming from a fork: -# if: github.event.pull_request.head.repo.full_name == github.repository -# shell: bash -# run: PATH=$(go env GOPATH)/bin:$PATH make integration -# env: -# AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} -# AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} -# AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} -# AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} -# AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} -# AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} + - name: integration + # skip running this action if the PR is coming from a fork: + if: github.event.pull_request.head.repo.full_name == github.repository + shell: bash + run: PATH=$(go env GOPATH)/bin:$PATH make integration + env: + AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} + AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} + AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} + AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} + AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} + AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} From 3a8db6ea3c537304cd8ae6607472102c5e57ae71 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Wed, 12 May 2021 23:05:01 -0400 Subject: [PATCH 26/29] workflow comment out integration --- .github/workflows/go.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7f83dfa02..9d8ba3957 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,15 +23,15 @@ jobs: - name: ci run: PATH=$(go env GOPATH)/bin:$PATH make ci - - name: integration - # skip running this action if the PR is coming from a fork: - if: github.event.pull_request.head.repo.full_name == github.repository - shell: bash - run: PATH=$(go env GOPATH)/bin:$PATH make integration - env: - AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} - AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} - AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} - AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} - AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} - AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} +# - name: integration +# # skip running this action if the PR is coming from a fork: +# if: github.event.pull_request.head.repo.full_name == github.repository +# shell: bash +# run: PATH=$(go env GOPATH)/bin:$PATH make integration +# env: +# AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} +# AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} +# AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} +# AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} +# AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} +# AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} From 811f879c4e419a9da33f8c1fe93093a783773f01 Mon Sep 17 00:00:00 2001 From: bright-poku Date: Thu, 13 May 2021 15:55:10 -0400 Subject: [PATCH 27/29] [CLI-148] updated scopes in config generator --- .github/workflows/go.yml | 24 ++++++++++++------------ pkg/auth0-cli-config-generator/main.go | 3 ++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9d8ba3957..7f83dfa02 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,15 +23,15 @@ jobs: - name: ci run: PATH=$(go env GOPATH)/bin:$PATH make ci -# - name: integration -# # skip running this action if the PR is coming from a fork: -# if: github.event.pull_request.head.repo.full_name == github.repository -# shell: bash -# run: PATH=$(go env GOPATH)/bin:$PATH make integration -# env: -# AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} -# AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} -# AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} -# AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} -# AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} -# AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} + - name: integration + # skip running this action if the PR is coming from a fork: + if: github.event.pull_request.head.repo.full_name == github.repository + shell: bash + run: PATH=$(go env GOPATH)/bin:$PATH make integration + env: + AUTH0_CLI_CLIENT_NAME: ${{ secrets.AUTH0_CLI_CLIENT_NAME }} + AUTH0_CLI_CLIENT_DOMAIN: ${{ secrets.AUTH0_CLI_CLIENT_DOMAIN }} + AUTH0_CLI_CLIENT_ID: ${{ secrets.AUTH0_CLI_CLIENT_ID }} + AUTH0_CLI_CLIENT_SECRET: ${{ secrets.AUTH0_CLI_CLIENT_SECRET }} + AUTH0_CLI_REUSE_CONFIG: ${{ secrets.AUTH0_CLI_REUSE_CONFIG }} + AUTH0_CLI_OVERWRITE: ${{ secrets.AUTH0_CLI_OVERWRITE }} diff --git a/pkg/auth0-cli-config-generator/main.go b/pkg/auth0-cli-config-generator/main.go index 955837529..6d8bef902 100644 --- a/pkg/auth0-cli-config-generator/main.go +++ b/pkg/auth0-cli-config-generator/main.go @@ -33,7 +33,8 @@ var requiredScopes = []string{ "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "create:rules", "delete:rules", "read:rules", "update:rules", - "read:users", "update:users", + "read:client_keys", "read:logs", "read:connections", "update:connections", + "create:users", "delete:users", "read:users", "update:users", "read:branding", "update:branding", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", } From f59c51a1af362a1b96867537cd5d660e789179b5 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Thu, 13 May 2021 22:39:46 -0300 Subject: [PATCH 28/29] Update internal/cli/users.go --- internal/cli/users.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index fd2f04f46..b43905ed6 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -351,10 +351,6 @@ auth0 users update -n John Doe --email john.doe@example.com`, conn := stringSliceToCommaSeparatedString(cli.getUserConnection(current)) current.Connection = auth0.String(conn) - if err := userConnection.AskU(cmd, &inputs.Connection, current.Connection); err != nil { - return err - } - if err := userName.AskU(cmd, &inputs.Name, current.Name); err != nil { return err } From 377386cb6543ca2ce70fb17cdd95f3658c1b104f Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Thu, 13 May 2021 22:51:49 -0300 Subject: [PATCH 29/29] Fix 400 error --- internal/cli/users.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index b43905ed6..281184e62 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -400,7 +400,7 @@ auth0 users update -n John Doe --email john.doe@example.com`, return fmt.Errorf("An unexpected error occurred while trying to update an user with Id '%s': %w", inputs.ID, err) } - con := cli.getConnReqUsername(auth0.StringValue(&inputs.Connection)) + con := cli.getConnReqUsername(auth0.StringValue(user.Connection)) requireUsername := auth0.BoolValue(con) cli.renderer.UserUpdate(user, requireUsername)