UNPKG

strapi-plugin-users-permissions

Version:

Protect your API with a full-authentication process based on JWT

396 lines (354 loc) 10.6 kB
/** * * AuthPage * */ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { bindActionCreators, compose } from 'redux'; import { Link } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import { findIndex, get, isBoolean, isEmpty, map, replace } from 'lodash'; import cn from 'classnames'; // Design import { auth, Button, InputsIndex as Input } from 'strapi-helper-plugin'; import pluginId from '../../pluginId'; // Logo import LogoStrapi from '../../assets/images/logo_strapi.png'; import { hideLoginErrorsInput, onChangeInput, setErrors, setForm, submit, } from './actions'; import form from './form.json'; import reducer from './reducer'; import saga from './saga'; import makeSelectAuthPage from './selectors'; import styles from './styles.scss'; export class AuthPage extends React.Component { // eslint-disable-line react/prefer-stateless-function componentDidMount() { auth.clearAppStorage(); this.setForm(); } componentDidUpdate(prevProps) { const { hideLoginErrorsInput, match: { params: { authType }, }, submitSuccess, } = this.props; if (authType !== prevProps.match.params.authType) { this.setForm(); hideLoginErrorsInput(false); } if (submitSuccess) { switch (authType) { case 'login': case 'reset-password': // Check if we have token to handle redirection to login or admin. // Done to prevent redirection to admin after reset password if user should // not have access. auth.getToken() ? this.redirect('/') : this.redirect('/plugins/users-permissions/auth/login'); break; case 'register': this.redirect('/'); // NOTE: prepare for comfirm email; // this.redirect(`/plugins/users-permissions/auth/register-success/${this.props.modifiedData.email}`); break; default: } } } // Get form Errors shortcut. getFormErrors = () => { const { formErrors } = this.props; return get(formErrors, ['0', 'errors', '0', 'id']); }; setForm = () => { const { location: { search }, match: { params: { authType, id }, }, setForm, } = this.props; const params = search ? replace(search, '?code=', '') : id; setForm(authType, params); }; isAuthType = type => { const { match: { params: { authType }, }, } = this.props; return authType === type; }; handleSubmit = e => { const { modifiedData, setErrors, submit } = this.props; e.preventDefault(); const formErrors = Object.keys(modifiedData).reduce((acc, key) => { if ( isEmpty(get(modifiedData, key)) && !isBoolean(get(modifiedData, key)) ) { acc.push({ name: key, errors: [{ id: 'components.Input.error.validation.required' }], }); } if ( !isEmpty(get(modifiedData, 'password')) && !isEmpty(get(modifiedData, 'confirmPassword')) && findIndex(acc, ['name', 'confirmPassword']) === -1 ) { if (modifiedData.password.length < 6) { acc.push({ name: 'password', errors: [ { id: 'users-permissions.components.Input.error.password.length', }, ], }); } if ( get(modifiedData, 'password') !== get(modifiedData, 'confirmPassword') ) { acc.push({ name: 'confirmPassword', errors: [ { id: 'users-permissions.components.Input.error.password.noMatch', }, ], }); } } return acc; }, []); setErrors(formErrors); if (isEmpty(formErrors)) { submit(this.context); } }; redirect = path => this.props.history.push(path); renderButton = () => { const { match: { params: { authType }, }, submitSuccess, } = this.props; if (this.isAuthType('login')) { return ( <div className={cn('col-md-6', styles.loginButton)}> <Button primary label="users-permissions.Auth.form.button.login" type="submit" /> </div> ); } const isEmailForgotSent = this.isAuthType('forgot-password') && submitSuccess; const label = isEmailForgotSent ? 'users-permissions.Auth.form.button.forgot-password.success' : `users-permissions.Auth.form.button.${authType}`; return ( <div className={cn('col-md-12', styles.buttonContainer)}> <Button className={cn(isEmailForgotSent && styles.buttonForgotSuccess)} label={label} style={{ width: '100%' }} primary={!isEmailForgotSent} type="submit" /> </div> ); }; renderLogo = () => this.isAuthType('register') && ( <div className={styles.logoContainer}> <img src={LogoStrapi} alt="logo" /> </div> ); renderLink = () => { if (this.isAuthType('login')) { return ( <Link to="/plugins/users-permissions/auth/forgot-password"> <FormattedMessage id="users-permissions.Auth.link.forgot-password" /> </Link> ); } if ( this.isAuthType('forgot-password') || this.isAuthType('register-success') ) { return ( <Link to="/plugins/users-permissions/auth/login"> <FormattedMessage id="users-permissions.Auth.link.ready" /> </Link> ); } return <div />; }; renderInputs = () => { const { didCheckErrors, formErrors, match: { params: { authType }, }, modifiedData, noErrorsDescription, onChangeInput, submitSuccess, } = this.props; const inputs = get(form, ['form', authType]); const isForgotEmailSent = this.isAuthType('forgot-password') && submitSuccess; return map(inputs, (input, key) => { const label = isForgotEmailSent ? { id: 'users-permissions.Auth.form.forgot-password.email.label.success', } : get(input, 'label'); return ( <Input autoFocus={key === 0} customBootstrapClass={get(input, 'customBootstrapClass')} didCheckErrors={didCheckErrors} errors={get(formErrors, [ findIndex(formErrors, ['name', input.name]), 'errors', ])} key={get(input, 'name')} label={label} name={get(input, 'name')} onChange={onChangeInput} placeholder={get(input, 'placeholder')} type={get(input, 'type')} validations={{ required: true }} value={get(modifiedData, get(input, 'name'), get(input, 'value'))} noErrorsDescription={noErrorsDescription} /> ); }); }; render() { const { modifiedData, noErrorsDescription, submitSuccess } = this.props; let divStyle = this.isAuthType('register') ? { marginTop: '3.2rem' } : { marginTop: '.9rem' }; if (this.isAuthType('forgot-password') && submitSuccess) { divStyle = { marginTop: '.9rem', minHeight: '18.2rem' }; } return ( <div className={styles.authPage}> <div className={styles.wrapper}> <div className={styles.headerContainer}> {this.isAuthType('register') ? ( <FormattedMessage id="users-permissions.Auth.form.header.register" /> ) : ( <img src={LogoStrapi} alt="logo" /> )} </div> <div className={styles.headerDescription}> {this.isAuthType('register') && ( <FormattedMessage id="users-permissions.Auth.header.register.description" /> )} </div> <div className={cn( styles.formContainer, this.isAuthType('forgot-password') && submitSuccess ? styles.borderedSuccess : styles.bordered, )} style={divStyle} > <form onSubmit={this.handleSubmit}> <div className="container-fluid"> {noErrorsDescription && !isEmpty(this.getFormErrors()) ? ( <div className={styles.errorsContainer}> <FormattedMessage id={this.getFormErrors()} /> </div> ) : ( '' )} <div className="row" style={{ textAlign: 'start' }}> {!submitSuccess && this.renderInputs()} {this.isAuthType('forgot-password') && submitSuccess && ( <div className={styles.forgotSuccess}> <FormattedMessage id="users-permissions.Auth.form.forgot-password.email.label.success" /> <br /> <p>{get(modifiedData, 'email', '')}</p> </div> )} {this.renderButton()} </div> </div> </form> </div> <div className={styles.linkContainer}>{this.renderLink()}</div> </div> {this.renderLogo()} </div> ); } } AuthPage.contextTypes = { updatePlugin: PropTypes.func, }; AuthPage.propTypes = { didCheckErrors: PropTypes.bool.isRequired, formErrors: PropTypes.array.isRequired, hideLoginErrorsInput: PropTypes.func.isRequired, history: PropTypes.object.isRequired, location: PropTypes.object.isRequired, match: PropTypes.object.isRequired, modifiedData: PropTypes.object.isRequired, noErrorsDescription: PropTypes.bool.isRequired, onChangeInput: PropTypes.func.isRequired, setErrors: PropTypes.func.isRequired, setForm: PropTypes.func.isRequired, submit: PropTypes.func.isRequired, submitSuccess: PropTypes.bool.isRequired, }; const mapStateToProps = makeSelectAuthPage(); function mapDispatchToProps(dispatch) { return bindActionCreators( { hideLoginErrorsInput, onChangeInput, setErrors, setForm, submit, }, dispatch, ); } const withConnect = connect( mapStateToProps, mapDispatchToProps, ); const withReducer = window.strapi.injectReducer({ key: 'authPage', reducer, pluginId, }); const withSaga = window.strapi.injectSaga({ key: 'authPage', saga, pluginId }); export default compose( withReducer, withSaga, withConnect, )(AuthPage);