Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2ed0eaf

Browse files
authored
feat: add minimum password entropy requirements (#6090)
* feat: add minimum password entropy requirements * Fix all the tests * Fix E2E tests
1 parent fe725f7 commit 2ed0eaf

13 files changed

+122
-63
lines changed

cli/login.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"github.com/coder/coder/cli/cliflag"
2121
"github.com/coder/coder/cli/cliui"
22+
"github.com/coder/coder/coderd/userpassword"
2223
"github.com/coder/coder/codersdk"
2324
)
2425

@@ -152,16 +153,19 @@ func login() *cobra.Command {
152153

153154
for !matching {
154155
password, err = cliui.Prompt(cmd, cliui.PromptOptions{
155-
Text: "Enter a " + cliui.Styles.Field.Render("password") + ":",
156-
Secret: true,
157-
Validate: cliui.ValidateNotEmpty,
156+
Text: "Enter a " + cliui.Styles.Field.Render("password") + ":",
157+
Secret: true,
158+
Validate: func(s string) error {
159+
return userpassword.Validate(s)
160+
},
158161
})
159162
if err != nil {
160163
return xerrors.Errorf("specify password prompt: %w", err)
161164
}
162165
confirm, err := cliui.Prompt(cmd, cliui.PromptOptions{
163-
Text: "Confirm " + cliui.Styles.Field.Render("password") + ":",
164-
Secret: true,
166+
Text: "Confirm " + cliui.Styles.Field.Render("password") + ":",
167+
Secret: true,
168+
Validate: cliui.ValidateNotEmpty,
165169
})
166170
if err != nil {
167171
return xerrors.Errorf("confirm password prompt: %w", err)

cli/login_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ func TestLogin(t *testing.T) {
5454
"first user?", "yes",
5555
"username", "testuser",
5656
"email", "[email protected]",
57-
"password", "password",
58-
"password", "password", // Confirm.
57+
"password", "SomeSecurePassword!",
58+
"password", "SomeSecurePassword!", // Confirm.
5959
"trial", "yes",
6060
}
6161
for i := 0; i < len(matches); i += 2 {
@@ -89,8 +89,8 @@ func TestLogin(t *testing.T) {
8989
"first user?", "yes",
9090
"username", "testuser",
9191
"email", "[email protected]",
92-
"password", "password",
93-
"password", "password", // Confirm.
92+
"password", "SomeSecurePassword!",
93+
"password", "SomeSecurePassword!", // Confirm.
9494
"trial", "yes",
9595
}
9696
for i := 0; i < len(matches); i += 2 {
@@ -107,7 +107,7 @@ func TestLogin(t *testing.T) {
107107
t.Parallel()
108108
client := coderdtest.New(t, nil)
109109
doneChan := make(chan struct{})
110-
root, _ := clitest.New(t, "login", client.URL.String(), "--first-user-username", "testuser", "--first-user-email", "[email protected]", "--first-user-password", "password", "--first-user-trial")
110+
root, _ := clitest.New(t, "login", client.URL.String(), "--first-user-username", "testuser", "--first-user-email", "[email protected]", "--first-user-password", "SomeSecurePassword!", "--first-user-trial")
111111
pty := ptytest.New(t)
112112
root.SetIn(pty.Input())
113113
root.SetOut(pty.Output())
@@ -143,8 +143,8 @@ func TestLogin(t *testing.T) {
143143
"first user?", "yes",
144144
"username", "testuser",
145145
"email", "[email protected]",
146-
"password", "mypass",
147-
"password", "wrongpass", // Confirm.
146+
"password", "MyFirstSecurePassword!",
147+
"password", "MyNonMatchingSecurePassword!", // Confirm.
148148
}
149149
for i := 0; i < len(matches); i += 2 {
150150
match := matches[i]
@@ -157,9 +157,9 @@ func TestLogin(t *testing.T) {
157157
pty.ExpectMatch("Passwords do not match")
158158
pty.ExpectMatch("Enter a " + cliui.Styles.Field.Render("password"))
159159

160-
pty.WriteLine("pass")
160+
pty.WriteLine("SomeSecurePassword!")
161161
pty.ExpectMatch("Confirm")
162-
pty.WriteLine("pass")
162+
pty.WriteLine("SomeSecurePassword!")
163163
pty.ExpectMatch("trial")
164164
pty.WriteLine("yes")
165165
pty.ExpectMatch("Welcome to Coder")

cli/resetpassword.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ func resetPassword() *cobra.Command {
5050
}
5151

5252
password, err := cliui.Prompt(cmd, cliui.PromptOptions{
53-
Text: "Enter new " + cliui.Styles.Field.Render("password") + ":",
54-
Secret: true,
55-
Validate: cliui.ValidateNotEmpty,
53+
Text: "Enter new " + cliui.Styles.Field.Render("password") + ":",
54+
Secret: true,
55+
Validate: func(s string) error {
56+
return userpassword.Validate(s)
57+
},
5658
})
5759
if err != nil {
5860
return xerrors.Errorf("password prompt: %w", err)

cli/resetpassword_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ func TestResetPassword(t *testing.T) {
2828

2929
const email = "[email protected]"
3030
const username = "example"
31-
const oldPassword = "password"
32-
const newPassword = "password2"
31+
const oldPassword = "MyOldPassword!"
32+
const newPassword = "MyNewPassword!"
3333

3434
// start postgres and coder server processes
3535

coderd/coderdtest/coderdtest.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
428428
var FirstUserParams = codersdk.CreateFirstUserRequest{
429429
430430
Username: "testuser",
431-
Password: "testpass",
431+
Password: "SomeSecurePassword!",
432432
}
433433

434434
// CreateFirstUser creates a user with preset credentials and authenticates
@@ -455,7 +455,7 @@ func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationI
455455
req := codersdk.CreateUserRequest{
456456
Email: namesgenerator.GetRandomName(10) + "@coder.com",
457457
Username: randomUsername(),
458-
Password: "testpass",
458+
Password: "SomeSecurePassword!",
459459
OrganizationID: organizationID,
460460
}
461461

coderd/userpassword/userpassword.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strconv"
1111
"strings"
1212

13+
passwordvalidator "github.com/wagslane/go-password-validator"
1314
"golang.org/x/crypto/pbkdf2"
1415
"golang.org/x/exp/slices"
1516
"golang.org/x/xerrors"
@@ -125,15 +126,14 @@ func hashWithSaltAndIter(password string, salt []byte, iter int) string {
125126
// Validate checks that the plain text password meets the minimum password requirements.
126127
// It returns properly formatted errors for detailed form validation on the client.
127128
func Validate(password string) error {
128-
const (
129-
minLength = 8
130-
maxLength = 64
131-
)
132-
if len(password) < minLength {
133-
return xerrors.Errorf("Password must be at least %d characters.", minLength)
129+
// Ensure passwords are secure enough!
130+
// See: https://github.com/wagslane/go-password-validator#what-entropy-value-should-i-use
131+
err := passwordvalidator.Validate(password, 52)
132+
if err != nil {
133+
return err
134134
}
135-
if len(password) > maxLength {
136-
return xerrors.Errorf("Password must be no more than %d characters.", maxLength)
135+
if len(password) > 64 {
136+
return xerrors.Errorf("password must be no more than %d characters", 64)
137137
}
138138
return nil
139139
}

coderd/users.go

+24
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
105105
}
106106
}
107107

108+
err = userpassword.Validate(createUser.Password)
109+
if err != nil {
110+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
111+
Message: "Password not strong enough!",
112+
Validations: []codersdk.ValidationError{{
113+
Field: "password",
114+
Detail: err.Error(),
115+
}},
116+
})
117+
return
118+
}
119+
108120
user, organizationID, err := api.CreateUser(ctx, api.Database, CreateUserRequest{
109121
CreateUserRequest: codersdk.CreateUserRequest{
110122
Email: createUser.Email,
@@ -316,6 +328,18 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
316328
return
317329
}
318330

331+
err = userpassword.Validate(req.Password)
332+
if err != nil {
333+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
334+
Message: "Password not strong enough!",
335+
Validations: []codersdk.ValidationError{{
336+
Field: "password",
337+
Detail: err.Error(),
338+
}},
339+
})
340+
return
341+
}
342+
319343
user, _, err := api.CreateUser(ctx, api.Database, CreateUserRequest{
320344
CreateUserRequest: req,
321345
LoginType: database.LoginTypePassword,

0 commit comments

Comments
 (0)