mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
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 <thomas@getunleash.ai> Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
This commit is contained in:
parent
38cfc549f2
commit
566d0613a4
@ -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<Record<string, string>>({});
|
||||
const [error, setError] = useState<Record<string, string>>({});
|
||||
const [error, setError] = useState<string>();
|
||||
const [validPassword, setValidPassword] = useState(false);
|
||||
const { classes: themeStyles } = useThemeStyles();
|
||||
|
||||
const updateField: React.ChangeEventHandler<HTMLInputElement> = 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"
|
||||
>
|
||||
<form
|
||||
onSubmit={submit}
|
||||
@ -85,10 +81,6 @@ const ChangePassword = ({
|
||||
themeStyles.flexColumn
|
||||
)}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(error.general)}
|
||||
show={<Alert severity="error">{error.general}</Alert>}
|
||||
/>
|
||||
<Typography variant="subtitle1">
|
||||
Changing password for user
|
||||
</Typography>
|
||||
@ -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"
|
||||
|
@ -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<React.SetStateAction<boolean>>;
|
||||
@ -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<string>();
|
||||
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
|
||||
</Typography>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(error)}
|
||||
show={
|
||||
<Alert data-loading severity="error">
|
||||
{error}
|
||||
</Alert>
|
||||
}
|
||||
/>
|
||||
<form
|
||||
className={classnames(styles.form, themeStyles.contentSpacingY)}
|
||||
>
|
||||
@ -115,6 +106,8 @@ const EditProfile = ({
|
||||
label="Password"
|
||||
name="password"
|
||||
value={password}
|
||||
error={Boolean(error)}
|
||||
helperText={error}
|
||||
autoComplete="new-password"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setPassword(e.target.value)
|
||||
|
@ -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 = ({
|
||||
<>
|
||||
<Typography variant="body2" className={styles.title} data-loading>
|
||||
Please set a strong password
|
||||
<HelpIcon tooltip="Your password needs to be at least ten characters long, and include an uppercase letter, a lowercase letter, a number and a symbol to be a valid OWASP password" />
|
||||
<HelpIcon tooltip={PASSWORD_FORMAT_MESSAGE} />
|
||||
</Typography>
|
||||
<div
|
||||
className={styles.container}
|
||||
|
Loading…
Reference in New Issue
Block a user