From 566d0613a496b71b5b61157bb48db14567e4160d Mon Sep 17 00:00:00 2001 From: olav Date: Tue, 28 Jun 2022 09:47:22 +0200 Subject: [PATCH] refactor: improve password error handling (#1118) * refactor: improve password error handling * Update src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.tsx Co-authored-by: Thomas Heartman Co-authored-by: Thomas Heartman --- .../ChangePassword/ChangePassword.tsx | 45 ++++++++----------- .../UserProfile/EditProfile/EditProfile.tsx | 37 +++++++-------- .../PasswordChecker/PasswordChecker.tsx | 4 +- 3 files changed, 36 insertions(+), 50 deletions(-) diff --git a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx index fbfc678c47..1448058144 100644 --- a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx +++ b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx @@ -1,13 +1,14 @@ import React, { useState } from 'react'; import classnames from 'classnames'; -import { Avatar, TextField, Typography, Alert } from '@mui/material'; +import { Avatar, TextField, Typography } from '@mui/material'; import { trim } from 'component/common/util'; import { modalStyles } from 'component/admin/users/util'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; -import PasswordChecker from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; +import PasswordChecker, { + PASSWORD_FORMAT_MESSAGE, +} from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; import { useThemeStyles } from 'themes/themeStyles'; import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { IUser } from 'interfaces/user'; interface IChangePasswordProps { @@ -24,30 +25,25 @@ const ChangePassword = ({ user, }: IChangePasswordProps) => { const [data, setData] = useState>({}); - const [error, setError] = useState>({}); + const [error, setError] = useState(); const [validPassword, setValidPassword] = useState(false); const { classes: themeStyles } = useThemeStyles(); const updateField: React.ChangeEventHandler = event => { - setError({}); + setError(undefined); setData({ ...data, [event.target.name]: trim(event.target.value) }); }; const submit = async (event: React.SyntheticEvent) => { event.preventDefault(); + if (data.password !== data.confirm) { + return; + } + if (!validPassword) { - if (!data.password || data.password.length < 8) { - setError({ - password: - 'You must specify a password with at least 8 chars.', - }); - return; - } - if (!(data.password === data.confirm)) { - setError({ confirm: 'Passwords does not match' }); - return; - } + setError(PASSWORD_FORMAT_MESSAGE); + return; } try { @@ -55,16 +51,15 @@ const ChangePassword = ({ setData({}); closeDialog(); } catch (error: unknown) { - const msg = - (error instanceof Error && error.message) || - 'Could not update password'; - setError({ general: msg }); + console.warn(error); + setError(PASSWORD_FORMAT_MESSAGE); } }; const onCancel = (event: React.SyntheticEvent) => { event.preventDefault(); setData({}); + setError(undefined); closeDialog(); }; @@ -77,6 +72,7 @@ const ChangePassword = ({ primaryButtonText="Save" title="Update password" secondaryButtonText="Cancel" + maxWidth="xs" >
- {error.general}} - /> Changing password for user @@ -117,7 +109,8 @@ const ChangePassword = ({ name="password" type="password" value={data.password} - helperText={error.password} + error={Boolean(error)} + helperText={error} onChange={updateField} variant="outlined" size="small" @@ -127,8 +120,6 @@ const ChangePassword = ({ name="confirm" type="password" value={data.confirm} - error={error.confirm !== undefined} - helperText={error.confirm} onChange={updateField} variant="outlined" size="small" diff --git a/frontend/src/component/user/UserProfile/EditProfile/EditProfile.tsx b/frontend/src/component/user/UserProfile/EditProfile/EditProfile.tsx index 410199b5bc..ab2bb2e5b6 100644 --- a/frontend/src/component/user/UserProfile/EditProfile/EditProfile.tsx +++ b/frontend/src/component/user/UserProfile/EditProfile/EditProfile.tsx @@ -3,10 +3,10 @@ import { Button, Typography } from '@mui/material'; import classnames from 'classnames'; import { useStyles } from './EditProfile.styles'; import { useThemeStyles } from 'themes/themeStyles'; -import PasswordChecker from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; +import PasswordChecker, { + PASSWORD_FORMAT_MESSAGE, +} from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher'; -import { Alert } from '@mui/material'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import useLoading from 'hooks/useLoading'; import { BAD_REQUEST, @@ -17,6 +17,7 @@ import { import { formatApiPath } from 'utils/formatPath'; import PasswordField from 'component/common/PasswordField/PasswordField'; import { headers } from 'utils/apiUtils'; +import { formatUnknownError } from 'utils/formatUnknownError'; interface IEditProfileProps { setEditingProfile: React.Dispatch>; @@ -31,7 +32,7 @@ const EditProfile = ({ const { classes: themeStyles } = useThemeStyles(); const [loading, setLoading] = useState(false); const [validPassword, setValidPassword] = useState(false); - const [error, setError] = useState(''); + const [error, setError] = useState(); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const ref = useLoading(loading); @@ -39,13 +40,13 @@ const EditProfile = ({ const submit = async (e: SyntheticEvent) => { e.preventDefault(); - if (!validPassword || password !== confirmPassword) { - setError( - 'Password is not valid, or your passwords do not match. Please provide a password with length over 10 characters, an uppercase letter, a lowercase letter, a number and a symbol.' - ); + if (password !== confirmPassword) { + return; + } else if (!validPassword) { + setError(PASSWORD_FORMAT_MESSAGE); } else { setLoading(true); - setError(''); + setError(undefined); try { const path = formatApiPath('api/admin/user/change-password'); const res = await fetch(path, { @@ -55,8 +56,8 @@ const EditProfile = ({ credentials: 'include', }); handleResponse(res); - } catch (e: any) { - setError(e); + } catch (error: unknown) { + setError(formatUnknownError(error)); } } setLoading(false); @@ -64,9 +65,7 @@ const EditProfile = ({ const handleResponse = (res: Response) => { if (res.status === BAD_REQUEST) { - setError( - 'Password could not be accepted. Please make sure you are inputting a valid password.' - ); + setError(PASSWORD_FORMAT_MESSAGE); } if (res.status === UNAUTHORIZED) { @@ -94,14 +93,6 @@ const EditProfile = ({ > Update password - - {error} - - } - /> @@ -115,6 +106,8 @@ const EditProfile = ({ label="Password" name="password" value={password} + error={Boolean(error)} + helperText={error} autoComplete="new-password" onChange={(e: React.ChangeEvent) => setPassword(e.target.value) diff --git a/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.tsx b/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.tsx index d2edbfa190..a1ee72fa61 100644 --- a/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.tsx +++ b/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.tsx @@ -35,6 +35,8 @@ const LOWERCASE_ERROR = 'The password must contain at least one lowercase letter.'; const REPEATING_CHARACTER_ERROR = 'The password may not contain sequences of three or more repeated characters.'; +export const PASSWORD_FORMAT_MESSAGE = + 'The password must be at least 10 characters long and must include an uppercase letter, a lowercase letter, a number, and a symbol.'; const PasswordChecker = ({ password, @@ -159,7 +161,7 @@ const PasswordChecker = ({ <> Please set a strong password - +