From 72caa5b44419eb77fda2474505b5ba4993418a49 Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 27 Nov 2023 10:27:13 +0100 Subject: [PATCH] DXCDT-588: Prevent user create or import to continue if connection is disabled (#921) --- internal/cli/users.go | 53 +++++++++++++++++++------- internal/cli/users_test.go | 78 ++++++++++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 26 deletions(-) diff --git a/internal/cli/users.go b/internal/cli/users.go index 84b906ba0..1e2fad6b6 100644 --- a/internal/cli/users.go +++ b/internal/cli/users.go @@ -229,8 +229,7 @@ func createUserCmd(cli *cli) *cobra.Command { auth0 users create --name "John Doe" --email john@example.com --connection-name "Username-Password-Authentication" --username "example" auth0 users create -n "John Doe" -e john@example.com -c "Username-Password-Authentication" -u "example" --json`, RunE: func(cmd *cobra.Command, args []string) error { - // Users API currently only supports database connections. - options, err := cli.dbConnectionPickerOptions(cmd.Context()) + options, err := cli.databaseAndPasswordlessConnectionOptions(cmd.Context()) if err != nil { return err } @@ -239,17 +238,26 @@ func createUserCmd(cli *cli) *cobra.Command { return err } - // Prompt for user's name + connection, err := cli.api.Connection.ReadByName(cmd.Context(), inputs.ConnectionName) + if err != nil { + return fmt.Errorf("failed to find connection with name %q: %w", inputs.ConnectionName, err) + } + + if len(connection.GetEnabledClients()) == 0 { + return fmt.Errorf( + "failed to continue due to the connection with name %q being disabled, enable an application on this connection and try again", + inputs.ConnectionName, + ) + } + 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 } - // Prompt for user password if err := userPassword.AskPassword(cmd, &inputs.Password); err != nil { return err } @@ -562,7 +570,7 @@ The file size limit for a bulk import is 500KB. You will need to start multiple auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert=false --email-results=false`, RunE: func(cmd *cobra.Command, args []string) error { // Users API currently only supports database connections. - dbConnectionOptions, err := cli.dbConnectionPickerOptions(cmd.Context()) + dbConnectionOptions, err := cli.databaseAndPasswordlessConnectionOptions(cmd.Context()) if err != nil { return err } @@ -576,6 +584,13 @@ The file size limit for a bulk import is 500KB. You will need to start multiple return fmt.Errorf("failed to find connection with name %q: %w", inputs.ConnectionName, err) } + if len(connection.GetEnabledClients()) == 0 { + return fmt.Errorf( + "failed to continue due to the connection with name %q being disabled, enable an application on this connection and try again", + inputs.ConnectionName, + ) + } + inputs.ConnectionID = connection.GetID() pipedUsersBody := iostream.PipedInput() @@ -657,22 +672,32 @@ func formatUserDetailsPath(id string) string { return fmt.Sprintf("users/%s", id) } -func (c *cli) dbConnectionPickerOptions(ctx context.Context) ([]string, error) { - list, err := c.api.Connection.List(ctx, management.Parameter("strategy", management.ConnectionStrategyAuth0)) +func (c *cli) databaseAndPasswordlessConnectionOptions(ctx context.Context) ([]string, error) { + connectionList, err := c.api.Connection.List( + ctx, + management.Parameter("strategy[0]", management.ConnectionStrategyAuth0), + management.Parameter("strategy[1]", management.ConnectionStrategyEmail), + management.Parameter("strategy[2]", management.ConnectionStrategySMS), + management.PerPage(100), + ) if err != nil { return nil, err } - var res []string - for _, conn := range list.Connections { - res = append(res, conn.GetName()) + var connectionNames []string + for _, connection := range connectionList.Connections { + if len(connection.GetEnabledClients()) == 0 { + continue + } + + connectionNames = append(connectionNames, connection.GetName()) } - if len(res) == 0 { - return nil, errors.New("There are currently no database connections.") + if len(connectionNames) == 0 { + return nil, errors.New("there are currently no active database or passwordless connections to choose from") } - return res, nil + return connectionNames, nil } func (c *cli) getUserConnection(users *management.User) []string { diff --git a/internal/cli/users_test.go b/internal/cli/users_test.go index cde330f6f..c66f21709 100644 --- a/internal/cli/users_test.go +++ b/internal/cli/users_test.go @@ -26,12 +26,57 @@ func TestConnectionsPickerOptions(t *testing.T) { name: "happy path", connections: []*management.Connection{ { - Name: auth0.String("some-name-1"), - Strategy: auth0.String("auth0"), + Name: auth0.String("some-name-1"), + Strategy: auth0.String("auth0"), + EnabledClients: &[]string{"1"}, }, { - Name: auth0.String("some-name-2"), - Strategy: auth0.String("auth0"), + Name: auth0.String("some-name-2"), + Strategy: auth0.String("auth0"), + EnabledClients: &[]string{"1"}, + }, + { + Name: auth0.String("some-name-3"), + Strategy: auth0.String("sms"), + EnabledClients: &[]string{"1"}, + }, + { + Name: auth0.String("some-name-4"), + Strategy: auth0.String("email"), + EnabledClients: &[]string{"1"}, + }, + }, + assertOutput: func(t testing.TB, options []string) { + assert.Len(t, options, 4) + assert.Equal(t, "some-name-1", options[0]) + assert.Equal(t, "some-name-2", options[1]) + assert.Equal(t, "some-name-3", options[2]) + assert.Equal(t, "some-name-4", options[3]) + }, + assertError: func(t testing.TB, err error) { + t.Fail() + }, + }, + { + name: "happy path: returning only active connections", + connections: []*management.Connection{ + { + Name: auth0.String("some-name-1"), + Strategy: auth0.String("auth0"), + EnabledClients: &[]string{"1"}, + }, + { + Name: auth0.String("some-name-2"), + Strategy: auth0.String("auth0"), + EnabledClients: &[]string{"1"}, + }, + { + Name: auth0.String("some-name-3"), + Strategy: auth0.String("sms"), + }, + { + Name: auth0.String("some-name-4"), + Strategy: auth0.String("email"), }, }, assertOutput: func(t testing.TB, options []string) { @@ -50,7 +95,7 @@ func TestConnectionsPickerOptions(t *testing.T) { t.Fail() }, assertError: func(t testing.TB, err error) { - assert.ErrorContains(t, err, "There are currently no database connections.") + assert.ErrorContains(t, err, "there are currently no active database or passwordless connections to choose from") }, }, { @@ -67,26 +112,35 @@ func TestConnectionsPickerOptions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + ctx := context.TODO() + ctrl := gomock.NewController(t) defer ctrl.Finish() connectionAPI := mock.NewMockConnectionAPI(ctrl) connectionAPI.EXPECT(). - List(gomock.Any(), gomock.Any()). - Return(&management.ConnectionList{ - Connections: test.connections}, test.apiError) + List(ctx, gomock.Any()). + Return( + &management.ConnectionList{ + Connections: test.connections, + }, + test.apiError, + ) cli := &cli{ - api: &auth0.API{Connection: connectionAPI}, + api: &auth0.API{ + Connection: connectionAPI, + }, } - options, err := cli.dbConnectionPickerOptions(context.Background()) + options, err := cli.databaseAndPasswordlessConnectionOptions(ctx) if err != nil { test.assertError(t, err) - } else { - test.assertOutput(t, options) + return } + + test.assertOutput(t, options) }) } }