diff --git a/cli/login.go b/cli/login.go index 49729a605cadc..7ef42a6e4ef92 100644 --- a/cli/login.go +++ b/cli/login.go @@ -117,6 +117,19 @@ func login() *cobra.Command { if err != nil { return xerrors.Errorf("specify password prompt: %w", err) } + _, err = cliui.Prompt(cmd, cliui.PromptOptions{ + Text: "Confirm " + cliui.Styles.Field.Render("password") + ":", + Secret: true, + Validate: func(s string) error { + if s != password { + return xerrors.Errorf("Passwords do not match") + } + return nil + }, + }) + if err != nil { + return xerrors.Errorf("confirm password prompt: %w", err) + } _, err = client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{ Email: email, diff --git a/cli/login_test.go b/cli/login_test.go index 32e9fcae55e52..a9cd31b6b7d46 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -43,6 +43,7 @@ func TestLogin(t *testing.T) { "username", "testuser", "email", "user@coder.com", "password", "password", + "password", "password", // Confirm. } for i := 0; i < len(matches); i += 2 { match := matches[i] @@ -54,6 +55,44 @@ func TestLogin(t *testing.T) { <-doneChan }) + t.Run("InitialUserTTYConfirmPasswordFailAndReprompt", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + client := coderdtest.New(t, nil) + // The --force-tty flag is required on Windows, because the `isatty` library does not + // accurately detect Windows ptys when they are not attached to a process: + // https://github.com/mattn/go-isatty/issues/59 + doneChan := make(chan struct{}) + root, _ := clitest.New(t, "login", "--force-tty", client.URL.String()) + pty := ptytest.New(t) + root.SetIn(pty.Input()) + root.SetOut(pty.Output()) + go func() { + defer close(doneChan) + err := root.ExecuteContext(ctx) + require.ErrorIs(t, err, context.Canceled) + }() + + matches := []string{ + "first user?", "yes", + "username", "testuser", + "email", "user@coder.com", + "password", "mypass", + "password", "wrongpass", // Confirm. + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + pty.WriteLine(value) + } + pty.ExpectMatch("Passwords do not match") + pty.ExpectMatch("password") // Re-prompt password. + cancel() + <-doneChan + }) + t.Run("ExistingUserValidTokenTTY", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: