File tree 11 files changed +71
-28
lines changed
UserSettingsPage/SecurityPage
11 files changed +71
-28
lines changed Original file line number Diff line number Diff line change @@ -1109,8 +1109,6 @@ func TestUpdateUserPassword(t *testing.T) {
1109
1109
require .Equal (t , database .AuditActionWrite , auditor .AuditLogs ()[numLogs - 1 ].Action )
1110
1110
})
1111
1111
1112
- // FIXME: Re-enable the tests once real logic changed
1113
- // Currently there's no check in code to validate that users have to put the old password
1114
1112
t .Run ("MemberCantUpdateOwnPasswordWithoutOldPassword" , func (t * testing.T ) {
1115
1113
t .Parallel ()
1116
1114
client := coderdtest .New (t , nil )
Original file line number Diff line number Diff line change @@ -1322,11 +1322,13 @@ class ApiMethods {
1322
1322
await this . axios . put ( `/api/v2/users/${ userId } /password` , updatePassword ) ;
1323
1323
} ;
1324
1324
1325
- validateUserPassword = async ( password : string ) : Promise < boolean > => {
1325
+ validateUserPassword = async (
1326
+ password : string ,
1327
+ ) : Promise < TypesGen . ValidateUserPasswordResponse > => {
1326
1328
const response = await this . axios . post ( "/api/v2/users/validate-password" , {
1327
1329
password,
1328
1330
} ) ;
1329
- return response . data . valid ;
1331
+ return response . data ;
1330
1332
} ;
1331
1333
1332
1334
getRoles = async ( ) : Promise < Array < TypesGen . AssignableRoles > > => {
Original file line number Diff line number Diff line change @@ -64,7 +64,7 @@ export interface CreateUserFormProps {
64
64
onSubmit : ( user : TypesGen . CreateUserRequestWithOrgs ) => void ;
65
65
onCancel : ( ) => void ;
66
66
onPasswordChange : ( password : string ) => void ;
67
- passwordIsValid : boolean ;
67
+ passwordValidator : TypesGen . ValidateUserPasswordResponse ;
68
68
error ?: unknown ;
69
69
isLoading : boolean ;
70
70
authMethods ?: TypesGen . AuthMethods ;
@@ -91,7 +91,7 @@ export const CreateUserForm: FC<
91
91
onSubmit,
92
92
onCancel,
93
93
onPasswordChange,
94
- passwordIsValid ,
94
+ passwordValidator ,
95
95
error,
96
96
isLoading,
97
97
authMethods,
@@ -206,15 +206,15 @@ export const CreateUserForm: FC<
206
206
( form . values . login_type !== "password" &&
207
207
"No password required for this login type" ) ||
208
208
( form . values . password !== "" &&
209
- ! passwordIsValid &&
210
- "password is not strong enough." ) ,
209
+ ! passwordValidator . valid &&
210
+ passwordValidator . details ) ,
211
211
} ) }
212
212
autoComplete = "current-password"
213
213
fullWidth
214
214
id = "password"
215
215
data-testid = "password-input"
216
216
disabled = { form . values . login_type !== "password" }
217
- error = { ! ! ( form . values . password !== "" && ! passwordIsValid ) }
217
+ error = { ! ! ( form . values . password !== "" && ! passwordValidator . valid ) }
218
218
label = { Language . passwordLabel }
219
219
type = "password"
220
220
/>
Original file line number Diff line number Diff line change @@ -20,12 +20,15 @@ export const CreateUserPage: FC = () => {
20
20
const authMethodsQuery = useQuery ( authMethods ( ) ) ;
21
21
const validatePasswordMutation = useMutation ( validatePassword ( ) ) ;
22
22
23
- const [ passwordIsValid , setPasswordIsValid ] = useState ( false ) ;
23
+ const [ passwordValidator , setPasswordValidator ] = useState ( {
24
+ valid : false ,
25
+ details : "" ,
26
+ } ) ;
24
27
25
28
const validateUserPassword = async ( password : string ) => {
26
29
validatePasswordMutation . mutate ( password , {
27
30
onSuccess : ( data ) => {
28
- setPasswordIsValid ( data ) ;
31
+ setPasswordValidator ( { valid : data . valid , details : data . details } ) ;
29
32
} ,
30
33
} ) ;
31
34
} ;
@@ -53,7 +56,7 @@ export const CreateUserPage: FC = () => {
53
56
navigate ( ".." , { relative : "path" } ) ;
54
57
} }
55
58
onPasswordChange = { debouncedValidateUserPassword }
56
- passwordIsValid = { passwordIsValid }
59
+ passwordValidator = { passwordValidator }
57
60
isLoading = { createUserMutation . isLoading }
58
61
/>
59
62
</ Margins >
Original file line number Diff line number Diff line change @@ -49,6 +49,32 @@ describe("Setup Page", () => {
49
49
) ;
50
50
} ) ;
51
51
52
+ it ( "renders the password validation error" , async ( ) => {
53
+ server . use (
54
+ http . post ( "/api/v2/users/validate-password" , ( ) => {
55
+ return HttpResponse . json ( {
56
+ valid : false ,
57
+ details : "Password is too short" ,
58
+ } ) ;
59
+ } ) ,
60
+ ) ;
61
+
62
+ renderWithRouter (
63
+ createMemoryRouter (
64
+ [
65
+ {
66
+ path : "/setup" ,
67
+ element : < SetupPage /> ,
68
+ } ,
69
+ ] ,
70
+ { initialEntries : [ "/setup" ] } ,
71
+ ) ,
72
+ ) ;
73
+ await waitForLoaderToBeRemoved ( ) ;
74
+ await fillForm ( { password : "short" } ) ;
75
+ await waitFor ( ( ) => screen . findByText ( "Password is too short" ) ) ;
76
+ } ) ;
77
+
52
78
it ( "redirects to the app when setup is successful" , async ( ) => {
53
79
let userHasBeenCreated = false ;
54
80
@@ -99,6 +125,7 @@ describe("Setup Page", () => {
99
125
await fillForm ( ) ;
100
126
await waitFor ( ( ) => screen . findByText ( "Templates" ) ) ;
101
127
} ) ;
128
+
102
129
it ( "calls sendBeacon with telemetry" , async ( ) => {
103
130
const sendBeacon = jest . fn ( ) ;
104
131
Object . defineProperty ( window . navigator , "sendBeacon" , {
Original file line number Diff line number Diff line change @@ -29,7 +29,10 @@ export const SetupPage: FC = () => {
29
29
const buildInfoQuery = useQuery ( buildInfo ( metadata [ "build-info" ] ) ) ;
30
30
const navigate = useNavigate ( ) ;
31
31
32
- const [ passwordIsValid , setPasswordIsValid ] = useState ( false ) ;
32
+ const [ passwordValidator , setPasswordValidator ] = useState ( {
33
+ valid : false ,
34
+ details : "" ,
35
+ } ) ;
33
36
34
37
useEffect ( ( ) => {
35
38
if ( ! buildInfoQuery . data ) {
@@ -43,7 +46,7 @@ export const SetupPage: FC = () => {
43
46
const validateUserPassword = async ( password : string ) => {
44
47
validatePasswordMutation . mutate ( password , {
45
48
onSuccess : ( data ) => {
46
- setPasswordIsValid ( data ) ;
49
+ setPasswordValidator ( { valid : data . valid , details : data . details } ) ;
47
50
} ,
48
51
} ) ;
49
52
} ;
@@ -74,7 +77,7 @@ export const SetupPage: FC = () => {
74
77
</ Helmet >
75
78
< SetupPageView
76
79
onPasswordChange = { debouncedValidateUserPassword }
77
- passwordIsValid = { passwordIsValid }
80
+ passwordValidator = { passwordValidator }
78
81
isLoading = { isSigningIn || createFirstUserMutation . isLoading }
79
82
error = { createFirstUserMutation . error }
80
83
onSubmit = { async ( firstUser ) => {
Original file line number Diff line number Diff line change @@ -31,6 +31,12 @@ export const TrialError: Story = {
31
31
} ,
32
32
} ;
33
33
34
+ export const PasswordValidation : Story = {
35
+ args : {
36
+ passwordValidator : { valid : false , details : "Password is too short" } ,
37
+ } ,
38
+ } ;
39
+
34
40
export const Loading : Story = {
35
41
args : {
36
42
isLoading : true ,
Original file line number Diff line number Diff line change @@ -84,15 +84,15 @@ const numberOfDevelopersOptions = [
84
84
export interface SetupPageViewProps {
85
85
onSubmit : ( firstUser : TypesGen . CreateFirstUserRequest ) => void ;
86
86
onPasswordChange ?: ( password : string ) => void ;
87
- passwordIsValid ?: boolean ;
87
+ passwordValidator : TypesGen . ValidateUserPasswordResponse ;
88
88
error ?: unknown ;
89
89
isLoading ?: boolean ;
90
90
}
91
91
92
92
export const SetupPageView : FC < SetupPageViewProps > = ( {
93
93
onSubmit,
94
94
onPasswordChange,
95
- passwordIsValid = true ,
95
+ passwordValidator ,
96
96
error,
97
97
isLoading,
98
98
} ) => {
@@ -183,9 +183,9 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
183
183
id = "password"
184
184
label = { Language . passwordLabel }
185
185
type = "password"
186
- error = { ! ! ( form . values . password !== "" && ! passwordIsValid ) }
186
+ error = { ! ! ( form . values . password !== "" && ! passwordValidator . valid ) }
187
187
helperText = {
188
- ! passwordIsValid ? "Password is not strong enough." : ""
188
+ ! passwordValidator . valid ? passwordValidator . details : ""
189
189
}
190
190
/>
191
191
< label
Original file line number Diff line number Diff line change 1
1
import LoadingButton from "@mui/lab/LoadingButton" ;
2
2
import TextField from "@mui/material/TextField" ;
3
+ import type * as TypesGen from "api/typesGenerated" ;
3
4
import { Alert } from "components/Alert/Alert" ;
4
5
import { ErrorAlert } from "components/Alert/ErrorAlert" ;
5
6
import { Form , FormFields } from "components/Form/Form" ;
@@ -42,7 +43,7 @@ export interface SecurityFormProps {
42
43
disabled : boolean ;
43
44
isLoading : boolean ;
44
45
onPasswordChange : ( password : string ) => void ;
45
- passwordIsValid : boolean ;
46
+ passwordValidator : TypesGen . ValidateUserPasswordResponse ;
46
47
onSubmit : ( values : SecurityFormValues ) => void ;
47
48
error ?: unknown ;
48
49
}
@@ -51,7 +52,7 @@ export const SecurityForm: FC<SecurityFormProps> = ({
51
52
disabled,
52
53
isLoading,
53
54
onPasswordChange,
54
- passwordIsValid ,
55
+ passwordValidator ,
55
56
onSubmit,
56
57
error,
57
58
} ) => {
@@ -96,10 +97,10 @@ export const SecurityForm: FC<SecurityFormProps> = ({
96
97
autoComplete = "password"
97
98
fullWidth
98
99
label = { Language . newPasswordLabel }
99
- error = { ! ! ( form . values . password !== "" && ! passwordIsValid ) }
100
+ error = { ! ! ( form . values . password !== "" && ! passwordValidator . valid ) }
100
101
helperText = {
101
- form . values . password !== "" && ! passwordIsValid
102
- ? "Password is not strong enough."
102
+ form . values . password !== "" && ! passwordValidator . valid
103
+ ? passwordValidator . details
103
104
: ""
104
105
}
105
106
type = "password"
Original file line number Diff line number Diff line change @@ -29,12 +29,15 @@ export const SecurityPage: FC = () => {
29
29
} ) ;
30
30
const singleSignOnSection = useSingleSignOnSection ( ) ;
31
31
32
- const [ passwordIsValid , setPasswordIsValid ] = useState ( false ) ;
32
+ const [ passwordValidator , setPasswordValidator ] = useState ( {
33
+ valid : false ,
34
+ details : "" ,
35
+ } ) ;
33
36
34
37
const validateUserPassword = async ( password : string ) => {
35
38
validatePasswordMutation . mutate ( password , {
36
39
onSuccess : ( data ) => {
37
- setPasswordIsValid ( data ) ;
40
+ setPasswordValidator ( { valid : data . valid , details : data . details } ) ;
38
41
} ,
39
42
} ) ;
40
43
} ;
@@ -56,7 +59,7 @@ export const SecurityPage: FC = () => {
56
59
error : updatePasswordMutation . error ,
57
60
isLoading : updatePasswordMutation . isLoading ,
58
61
onPasswordChange : debouncedValidateUserPassword ,
59
- passwordIsValid : passwordIsValid ,
62
+ passwordValidator : passwordValidator ,
60
63
onSubmit : async ( data ) => {
61
64
await updatePasswordMutation . mutateAsync ( {
62
65
userId : me . id ,
Original file line number Diff line number Diff line change @@ -16,7 +16,7 @@ const defaultArgs: ComponentProps<typeof SecurityPageView> = {
16
16
isLoading : false ,
17
17
onSubmit : action ( "onSubmit" ) ,
18
18
onPasswordChange : ( password : string ) => { } ,
19
- passwordIsValid : false ,
19
+ passwordValidator : { valid : false , details : "" } ,
20
20
} ,
21
21
} ,
22
22
oidc : {
You can’t perform that action at this time.
0 commit comments