Skip to content

Commit 3dbcddc

Browse files
authored
fix: Confirm password in cli create first user step (#1220)
Fixes #1182
1 parent 914a2f4 commit 3dbcddc

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

cli/login.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ func login() *cobra.Command {
117117
if err != nil {
118118
return xerrors.Errorf("specify password prompt: %w", err)
119119
}
120+
_, err = cliui.Prompt(cmd, cliui.PromptOptions{
121+
Text: "Confirm " + cliui.Styles.Field.Render("password") + ":",
122+
Secret: true,
123+
Validate: func(s string) error {
124+
if s != password {
125+
return xerrors.Errorf("Passwords do not match")
126+
}
127+
return nil
128+
},
129+
})
130+
if err != nil {
131+
return xerrors.Errorf("confirm password prompt: %w", err)
132+
}
120133

121134
_, err = client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{
122135
Email: email,

cli/login_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func TestLogin(t *testing.T) {
4343
"username", "testuser",
4444
"email", "user@coder.com",
4545
"password", "password",
46+
"password", "password", // Confirm.
4647
}
4748
for i := 0; i < len(matches); i += 2 {
4849
match := matches[i]
@@ -54,6 +55,44 @@ func TestLogin(t *testing.T) {
5455
<-doneChan
5556
})
5657

58+
t.Run("InitialUserTTYConfirmPasswordFailAndReprompt", func(t *testing.T) {
59+
t.Parallel()
60+
ctx, cancel := context.WithCancel(context.Background())
61+
defer cancel()
62+
client := coderdtest.New(t, nil)
63+
// The --force-tty flag is required on Windows, because the `isatty` library does not
64+
// accurately detect Windows ptys when they are not attached to a process:
65+
// https://github.com/mattn/go-isatty/issues/59
66+
doneChan := make(chan struct{})
67+
root, _ := clitest.New(t, "login", "--force-tty", client.URL.String())
68+
pty := ptytest.New(t)
69+
root.SetIn(pty.Input())
70+
root.SetOut(pty.Output())
71+
go func() {
72+
defer close(doneChan)
73+
err := root.ExecuteContext(ctx)
74+
require.ErrorIs(t, err, context.Canceled)
75+
}()
76+
77+
matches := []string{
78+
"first user?", "yes",
79+
"username", "testuser",
80+
"email", "user@coder.com",
81+
"password", "mypass",
82+
"password", "wrongpass", // Confirm.
83+
}
84+
for i := 0; i < len(matches); i += 2 {
85+
match := matches[i]
86+
value := matches[i+1]
87+
pty.ExpectMatch(match)
88+
pty.WriteLine(value)
89+
}
90+
pty.ExpectMatch("Passwords do not match")
91+
pty.ExpectMatch("password") // Re-prompt password.
92+
cancel()
93+
<-doneChan
94+
})
95+
5796
t.Run("ExistingUserValidTokenTTY", func(t *testing.T) {
5897
t.Parallel()
5998
client := coderdtest.New(t, nil)

0 commit comments

Comments
 (0)