mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-04 16:31:13 +00:00
set-password-strength (#6477)
* set-password-strength * update-uni-test * Update views.py * update * remove userless code * Update settings.py
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormGroup, Label, InputGroup, Input, InputGroupAddon, Button } from 'reactstrap';
|
||||
import classnames from 'classnames';
|
||||
import PasswordStrengthChecker from './password-strength-checker';
|
||||
import { isMobile } from '../../../utils/utils';
|
||||
|
||||
import '../../../css/password-input.css';
|
||||
|
||||
const propTypes = {
|
||||
value: PropTypes.string,
|
||||
labelValue: PropTypes.string,
|
||||
enableCheckStrength: PropTypes.bool,
|
||||
onChangeValue: PropTypes.func,
|
||||
};
|
||||
|
||||
const PasswordInput = ({ value, labelValue, enableCheckStrength, onChangeValue }) => {
|
||||
const [passwordValue, setPasswordValue] = useState(value || '');
|
||||
const [isShowPassword, setIsShowPassword] = useState(false);
|
||||
const [isShowChecker, setIsShowChecker] = useState(false);
|
||||
|
||||
const changePasswordValue = (e) => {
|
||||
const updatedValue = e.target.value;
|
||||
onChangeValue(updatedValue);
|
||||
setPasswordValue(updatedValue);
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
if (enableCheckStrength) {
|
||||
setIsShowChecker(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (enableCheckStrength) {
|
||||
setIsShowChecker(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup className={classnames('password-input-container position-relative', { 'mobile': isMobile })}>
|
||||
<Label>{labelValue}</Label>
|
||||
<InputGroup className='password'>
|
||||
<Input
|
||||
type={isShowPassword ? 'text' : 'password'}
|
||||
value={value}
|
||||
onChange={changePasswordValue}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
{isShowChecker && enableCheckStrength && (
|
||||
<PasswordStrengthChecker
|
||||
passwordValue={passwordValue}
|
||||
/>
|
||||
)}
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button onClick={() => setIsShowPassword(!isShowPassword)}>
|
||||
<i className={`password-icon sf3-font sf3-font-eye${isShowPassword ? '' : '-slash'}`} />
|
||||
</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordInput.propTypes = propTypes;
|
||||
|
||||
PasswordInput.defaultProps = { enableCheckStrength: true };
|
||||
|
||||
export default PasswordInput;
|
@@ -0,0 +1,51 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Progress } from 'reactstrap';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { evaluatePasswordStrength } from '../../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
passwordValue: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const PASSWORD_STRENGTH_VALUES = {
|
||||
empty: { classNames: ['default', 'default', 'default', 'default'], textValue: '' },
|
||||
too_short: { classNames: ['too-short', 'default', 'default', 'default'], textValue: 'too short' },
|
||||
weak: { classNames: ['weak', 'default', 'default', 'default'], textValue: 'weak' },
|
||||
medium: { classNames: ['medium', 'medium', 'default', 'default'], textValue: 'medium' },
|
||||
strong: { classNames: ['strong', 'strong', 'strong', 'default'], textValue: 'strong' },
|
||||
very_strong: { classNames: ['very-strong', 'very-strong', 'very-strong', 'very-strong'], textValue: 'very strong' },
|
||||
};
|
||||
|
||||
const PasswordStrengthChecker = ({ passwordValue }) => {
|
||||
const { classNames: progressClassNames = [], textValue = '' } = useMemo(() => PASSWORD_STRENGTH_VALUES[evaluatePasswordStrength(passwordValue)] || {}, [passwordValue]);
|
||||
const labelClassName = Array.isArray(progressClassNames) && progressClassNames.length > 0 ? progressClassNames[0] : '';
|
||||
|
||||
return (
|
||||
<div className="password-strength-check-container">
|
||||
<div className='password-strength-check-box'>
|
||||
<div className="password-strength-value">
|
||||
<span>{gettext('Password strength')}: </span>
|
||||
<span className={labelClassName}>{gettext(textValue)}</span>
|
||||
</div>
|
||||
<Progress multi>
|
||||
{progressClassNames.map((className, index) => (
|
||||
<Progress
|
||||
bar
|
||||
key={index}
|
||||
className={className}
|
||||
value='25'
|
||||
/>
|
||||
))}
|
||||
</Progress>
|
||||
<div className='password-strength-description'>
|
||||
<span>{gettext('Password must be at least 8 characters long and contain different characters: uppercase letters, lowercase letters, numbers, and special symbols')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordStrengthChecker.propTypes = propTypes;
|
||||
|
||||
export default PasswordStrengthChecker;
|
@@ -0,0 +1,81 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Form, Alert } from 'reactstrap';
|
||||
import toaster from '../../toast';
|
||||
import PasswordInput from './password-input';
|
||||
import { userAPI } from '../../../utils/user-api';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { Utils, validatePassword } from '../../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func,
|
||||
};
|
||||
|
||||
const UserSetPassword = ({ toggle }) => {
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmedPassword, setConfirmedPassword] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [canSubmit, setCanSubmit] = useState(true);
|
||||
|
||||
const submitPassword = () => {
|
||||
if (!password) {
|
||||
setErrorMessage(gettext('Password cannot be blank'));
|
||||
return;
|
||||
}
|
||||
if (!confirmedPassword) {
|
||||
setErrorMessage(gettext('Please enter the password again'));
|
||||
return;
|
||||
}
|
||||
if (password !== confirmedPassword) {
|
||||
setErrorMessage(gettext('Passwords don\'t match'));
|
||||
return;
|
||||
}
|
||||
if (!validatePassword(password)) {
|
||||
setErrorMessage(gettext('Insufficient password strength'));
|
||||
return;
|
||||
}
|
||||
|
||||
setErrorMessage('');
|
||||
setCanSubmit(false);
|
||||
userAPI.resetPassword(null, password).then(() => {
|
||||
toaster.success('Password set');
|
||||
location.reload();
|
||||
toggle();
|
||||
}).catch(error => {
|
||||
const errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
setCanSubmit(true);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal centered={true} isOpen={true} toggle={toggle}>
|
||||
<ModalHeader toggle={toggle}>{gettext('Set password')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<PasswordInput
|
||||
value={password}
|
||||
labelValue={gettext('Password')}
|
||||
onChangeValue={setPassword}
|
||||
/>
|
||||
<PasswordInput
|
||||
value={confirmedPassword}
|
||||
labelValue={gettext('Confirm password')}
|
||||
onChangeValue={setConfirmedPassword}
|
||||
/>
|
||||
</Form>
|
||||
{errorMessage && (
|
||||
<Alert color='danger'>{errorMessage}</Alert>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color='secondary' onClick={toggle}>{gettext('Cancel')}</Button>
|
||||
<Button color="primary" disabled={!canSubmit} onClick={submitPassword}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
UserSetPassword.propTypes = propTypes;
|
||||
|
||||
export default UserSetPassword;
|
@@ -0,0 +1,93 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Form, Alert } from 'reactstrap';
|
||||
import toaster from '../../toast';
|
||||
import PasswordInput from './password-input';
|
||||
import { userAPI } from '../../../utils/user-api';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { Utils, validatePassword } from '../../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func,
|
||||
};
|
||||
|
||||
const UserUpdatePassword = ({ toggle }) => {
|
||||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [confirmedNewPassword, setConfirmedNewPassword] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [canSubmit, setCanSubmit] = useState(true);
|
||||
|
||||
const updatePassword = () => {
|
||||
if (!currentPassword) {
|
||||
setErrorMessage(gettext('Current password cannot be blank'));
|
||||
return;
|
||||
}
|
||||
if (!newPassword) {
|
||||
setErrorMessage(gettext('Password cannot be blank'));
|
||||
return;
|
||||
}
|
||||
if (!confirmedNewPassword) {
|
||||
setErrorMessage(gettext('Please enter the password again'));
|
||||
return;
|
||||
}
|
||||
if (newPassword !== confirmedNewPassword) {
|
||||
setErrorMessage(gettext('Passwords don\'t match'));
|
||||
return;
|
||||
}
|
||||
if (currentPassword === newPassword) {
|
||||
setErrorMessage(gettext('New password cannot be the same as old password'));
|
||||
}
|
||||
if (!validatePassword(newPassword)) {
|
||||
setErrorMessage(gettext('Insufficient password strength'));
|
||||
return;
|
||||
}
|
||||
setErrorMessage('');
|
||||
setCanSubmit(false);
|
||||
userAPI.resetPassword(currentPassword, newPassword).then(() => {
|
||||
toaster.success('Password updated');
|
||||
toggle();
|
||||
}).catch(error => {
|
||||
const errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
setCanSubmit(true);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal centered={true} isOpen={true} toggle={toggle}>
|
||||
<ModalHeader toggle={toggle}>{gettext('Update password')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<PasswordInput
|
||||
value={currentPassword}
|
||||
labelValue={gettext('Current password')}
|
||||
enableCheckStrength={false}
|
||||
onChangeValue={setCurrentPassword}
|
||||
/>
|
||||
<PasswordInput
|
||||
value={newPassword}
|
||||
labelValue={gettext('New password')}
|
||||
onChangeValue={setNewPassword}
|
||||
/>
|
||||
<PasswordInput
|
||||
value={confirmedNewPassword}
|
||||
labelValue={gettext('Confirm password')}
|
||||
onChangeValue={setConfirmedNewPassword}
|
||||
/>
|
||||
</Form>
|
||||
{errorMessage && (
|
||||
<Alert color='danger'>{errorMessage}</Alert>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color='secondary' onClick={toggle}>{gettext('Cancel')}</Button>
|
||||
<Button color="primary" disabled={!canSubmit} onClick={updatePassword}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
UserUpdatePassword.propTypes = propTypes;
|
||||
|
||||
export default UserUpdatePassword;
|
Reference in New Issue
Block a user