| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- import React from 'react';
- import PropTypes from 'prop-types';
- import ReactCardFlip from 'react-card-flip';
- import { withTranslation } from 'react-i18next';
- import AppContainer from '~/client/services/AppContainer';
- import { withUnstatedContainers } from './UnstatedUtils';
- class LoginForm extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isRegistering: false,
- };
- this.switchForm = this.switchForm.bind(this);
- this.handleLoginWithExternalAuth = this.handleLoginWithExternalAuth.bind(this);
- this.renderLocalOrLdapLoginForm = this.renderLocalOrLdapLoginForm.bind(this);
- this.renderExternalAuthLoginForm = this.renderExternalAuthLoginForm.bind(this);
- this.renderExternalAuthInput = this.renderExternalAuthInput.bind(this);
- this.renderRegisterForm = this.renderRegisterForm.bind(this);
- const { hash } = window.location;
- if (hash === '#register') {
- this.state.isRegistering = true;
- }
- }
- switchForm() {
- this.setState({ isRegistering: !this.state.isRegistering });
- }
- handleLoginWithExternalAuth(e) {
- const auth = e.currentTarget.id;
- const { csrf } = this.props.appContainer;
- window.location.href = `/passport/${auth}?_csrf=${csrf}`;
- }
- renderLocalOrLdapLoginForm() {
- const { t, appContainer, isLdapStrategySetup } = this.props;
- return (
- <form role="form" action="/login" method="post">
- <div className="input-group">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-user"></i>
- </span>
- </div>
- <input type="text" className="form-control rounded-0" data-testid="tiUsernameForLogin" placeholder="Username or E-mail" name="loginForm[username]" />
- {isLdapStrategySetup && (
- <div className="input-group-append">
- <small className="input-group-text text-success">
- <i className="icon-fw icon-check"></i> LDAP
- </small>
- </div>
- )}
- </div>
- <div className="input-group">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-lock"></i>
- </span>
- </div>
- <input type="password" className="form-control rounded-0" data-testid="tiPasswordForLogin" placeholder="Password" name="loginForm[password]" />
- </div>
- <div className="input-group my-4">
- <input type="hidden" name="_csrf" value={appContainer.csrfToken} />
- <button type="submit" id="login" className="btn btn-fill rounded-0 login mx-auto" data-testid="btnSubmitForLogin">
- <div className="eff"></div>
- <span className="btn-label">
- <i className="icon-login"></i>
- </span>
- <span className="btn-label-text">{t('Sign in')}</span>
- </button>
- </div>
- </form>
- );
- }
- renderExternalAuthInput(auth) {
- const { t } = this.props;
- const authIconNames = {
- google: 'google',
- github: 'github',
- facebook: 'facebook',
- twitter: 'twitter',
- oidc: 'openid',
- saml: 'key',
- basic: 'lock',
- };
- return (
- <div key={auth} className="col-6 my-2">
- <button type="button" className="btn btn-fill rounded-0" id={auth} onClick={this.handleLoginWithExternalAuth}>
- <div className="eff"></div>
- <span className="btn-label">
- <i className={`fa fa-${authIconNames[auth]}`}></i>
- </span>
- <span className="btn-label-text">{t('Sign in')}</span>
- </button>
- <div className="small text-right">by {auth} Account</div>
- </div>
- );
- }
- renderExternalAuthLoginForm() {
- const { isLocalStrategySetup, isLdapStrategySetup, objOfIsExternalAuthEnableds } = this.props;
- const isExternalAuthCollapsible = isLocalStrategySetup || isLdapStrategySetup;
- const collapsibleClass = isExternalAuthCollapsible ? 'collapse collapse-external-auth' : '';
- return (
- <>
- <div className="grw-external-auth-form border-top border-bottom">
- <div id="external-auth" className={`external-auth ${collapsibleClass}`}>
- <div className="row mt-2">
- {Object.keys(objOfIsExternalAuthEnableds).map((auth) => {
- if (!objOfIsExternalAuthEnableds[auth]) {
- return;
- }
- return this.renderExternalAuthInput(auth);
- })}
- </div>
- </div>
- </div>
- <div className="text-center">
- <button
- type="button"
- className="btn btn-secondary btn-external-auth-tab btn-sm rounded-0 mb-3"
- data-toggle={isExternalAuthCollapsible ? 'collapse' : ''}
- data-target="#external-auth"
- aria-expanded="false"
- aria-controls="external-auth"
- >
- External Auth
- </button>
- </div>
- </>
- );
- }
- renderRegisterForm() {
- const {
- t,
- appContainer,
- isEmailAuthenticationEnabled,
- username,
- name,
- email,
- registrationMode,
- registrationWhiteList,
- } = this.props;
- const { isMailerSetup } = appContainer.config;
- let registerAction = '/register';
- let submitText = t('Sign up');
- if (isEmailAuthenticationEnabled) {
- registerAction = '/user-activation/register';
- submitText = t('page_register.send_email');
- }
- return (
- <React.Fragment>
- {registrationMode === 'Restricted' && (
- <p className="alert alert-warning">
- {t('page_register.notice.restricted')}
- <br />
- {t('page_register.notice.restricted_defail')}
- </p>
- )}
- { (!isMailerSetup && isEmailAuthenticationEnabled) && (
- <p className="alert alert-danger">
- <span>{t('security_setting.Local.please_enable_mailer')}</span>
- </p>
- )}
- <form role="form" action={registerAction} method="post" id="register-form">
- {!isEmailAuthenticationEnabled && (
- <div>
- <div className="input-group" id="input-group-username">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-user"></i>
- </span>
- </div>
- <input
- type="text"
- className="form-control rounded-0"
- placeholder={t('User ID')}
- name="registerForm[username]"
- defaultValue={username}
- required
- />
- </div>
- <p className="form-text text-danger">
- <span id="help-block-username"></span>
- </p>
- <div className="input-group">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-tag"></i>
- </span>
- </div>
- <input type="text" className="form-control rounded-0" placeholder={t('Name')} name="registerForm[name]" defaultValue={name} required />
- </div>
- </div>
- )}
- <div className="input-group">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-envelope"></i>
- </span>
- </div>
- <input type="email" className="form-control rounded-0" placeholder={t('Email')} name="registerForm[email]" defaultValue={email} required />
- </div>
- {registrationWhiteList.length > 0 && (
- <>
- <p className="form-text">{t('page_register.form_help.email')}</p>
- <ul>
- {registrationWhiteList.map((elem) => {
- return (
- <li key={elem}>
- <code>{elem}</code>
- </li>
- );
- })}
- </ul>
- </>
- )}
- {!isEmailAuthenticationEnabled && (
- <div>
- <div className="input-group">
- <div className="input-group-prepend">
- <span className="input-group-text">
- <i className="icon-lock"></i>
- </span>
- </div>
- <input type="password" className="form-control rounded-0" placeholder={t('Password')} name="registerForm[password]" required />
- </div>
- </div>
- )}
- <div className="input-group justify-content-center my-4">
- <input type="hidden" name="_csrf" value={appContainer.csrfToken} />
- <button type="submit" className="btn btn-fill rounded-0" id="register" disabled={(!isMailerSetup && isEmailAuthenticationEnabled)}>
- <div className="eff"></div>
- <span className="btn-label">
- <i className="icon-user-follow"></i>
- </span>
- <span className="btn-label-text">{submitText}</span>
- </button>
- </div>
- </form>
- <div className="border-bottom"></div>
- <div className="row">
- <div className="text-right col-12 mt-2 py-2">
- <a href="#login" id="login" className="link-switch" onClick={this.switchForm}>
- <i className="icon-fw icon-login"></i>
- {t('Sign in is here')}
- </a>
- </div>
- </div>
- </React.Fragment>
- );
- }
- render() {
- const {
- t,
- isLocalStrategySetup,
- isLdapStrategySetup,
- isRegistrationEnabled,
- isPasswordResetEnabled,
- objOfIsExternalAuthEnableds,
- } = this.props;
- const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
- const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
- return (
- <div className="login-dialog mx-auto" id="login-dialog">
- <div className="row mx-0">
- <div className="col-12">
- <ReactCardFlip isFlipped={this.state.isRegistering} flipDirection="horizontal" cardZIndex="3">
- <div className="front">
- {isLocalOrLdapStrategiesEnabled && this.renderLocalOrLdapLoginForm()}
- {isSomeExternalAuthEnabled && this.renderExternalAuthLoginForm()}
- {isLocalOrLdapStrategiesEnabled && isPasswordResetEnabled && (
- <div className="text-right mb-2">
- <a href="/forgot-password" className="d-block link-switch">
- <i className="icon-key"></i> {t('forgot_password.forgot_password')}
- </a>
- </div>
- )}
- {isRegistrationEnabled && (
- <div className="text-right mb-2">
- <a href="#register" id="register" className="link-switch" onClick={this.switchForm}>
- <i className="ti-check-box"></i> {t('Sign up is here')}
- </a>
- </div>
- )}
- </div>
- <div className="back">
- {isRegistrationEnabled && this.renderRegisterForm()}
- </div>
- </ReactCardFlip>
- </div>
- </div>
- <a href="https://growi.org" className="link-growi-org pl-3">
- <span className="growi">GROWI</span>.<span className="org">ORG</span>
- </a>
- </div>
- );
- }
- }
- /**
- * Wrapper component for using unstated
- */
- const LoginFormWrapper = withUnstatedContainers(LoginForm, [AppContainer]);
- LoginForm.propTypes = {
- // i18next
- t: PropTypes.func.isRequired,
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
- isRegistering: PropTypes.bool,
- username: PropTypes.string,
- name: PropTypes.string,
- email: PropTypes.string,
- isRegistrationEnabled: PropTypes.bool,
- registrationMode: PropTypes.string,
- registrationWhiteList: PropTypes.array,
- isPasswordResetEnabled: PropTypes.bool,
- isEmailAuthenticationEnabled: PropTypes.bool,
- isLocalStrategySetup: PropTypes.bool,
- isLdapStrategySetup: PropTypes.bool,
- objOfIsExternalAuthEnableds: PropTypes.object,
- };
- export default withTranslation()(LoginFormWrapper);
|