Parcourir la source

Merge pull request #2088 from weseek/support/reactify-login-page-accessable-ex-auth-url

Support/reactify login page accessable ex auth url
Yuki Takei il y a 6 ans
Parent
commit
6c033541e1

+ 32 - 22
src/client/js/components/LoginForm.jsx

@@ -3,6 +3,9 @@ import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
 
+import { createSubscribedElement } from './UnstatedUtils';
+import NoLoginContainer from '../services/NoLoginContainer';
+
 class LoginForm extends React.Component {
 
   constructor(props) {
@@ -13,6 +16,7 @@ class LoginForm extends React.Component {
     };
 
     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);
@@ -28,8 +32,14 @@ class LoginForm extends React.Component {
     this.setState({ isRegistering: !this.state.isRegistering });
   }
 
+  handleLoginWithExternalAuth(e) {
+    const auth = e.currentTarget.id;
+    const csrf = this.props.noLoginContainer.csrfToken;
+    window.location.href = `/passport/${auth}?_csrf=${csrf}`;
+  }
+
   renderLocalOrLdapLoginForm() {
-    const { t, csrf, isLdapStrategySetup } = this.props;
+    const { t, noLoginContainer, isLdapStrategySetup } = this.props;
 
     return (
       <form role="form" action="/login" method="post">
@@ -59,8 +69,7 @@ class LoginForm extends React.Component {
         </div>
 
         <div className="input-group justify-content-center d-flex mt-5">
-          {/* [TODO][GW-2112] An AppContainer gets csrf data */}
-          <input type="hidden" name="_csrf" value={csrf} />
+          <input type="hidden" name="_csrf" value={noLoginContainer.csrfToken} />
           <button type="submit" id="login" className="btn btn-fill login px-0 py-2">
             <div className="eff"></div>
             <span className="btn-label p-3">
@@ -74,22 +83,17 @@ class LoginForm extends React.Component {
   }
 
   renderExternalAuthInput(auth) {
-    const { t, csrf } = this.props;
+    const { t } = this.props;
     return (
       <div key={auth} className="input-group justify-content-center d-flex mt-5">
-        {/* [TODO][GW-2112] use onClick, and delete form tag */}
-        <form role="form" action={`/passport/${auth}`} className="d-inline-flex flex-column">
-          {/* [TODO][GW-2112] An AppContainer gets csrf data */}
-          <input type="hidden" name="_csrf" value={csrf} />
-          <button type="submit" className="btn btn-fill px-0 py-2" id={auth}>
-            <div className="eff"></div>
-            <span className="btn-label p-3">
-              <i className={`fa fa-${auth}`}></i>
-            </span>
-            <span className="btn-label-text p-3">{t('Sign in')}</span>
-          </button>
-          <div className="small text-center">by {auth} Account</div>
-        </form>
+        <button type="button" className="btn btn-fill px-0 py-2" id={auth} onClick={this.handleLoginWithExternalAuth}>
+          <div className="eff"></div>
+          <span className="btn-label p-3">
+            <i className={`fa fa-${auth}`}></i>
+          </span>
+          <span className="btn-label-text p-3">{t('Sign in')}</span>
+        </button>
+        <div className="small text-center">by {auth} Account</div>
       </div>
     );
   }
@@ -134,7 +138,7 @@ class LoginForm extends React.Component {
   renderRegisterForm() {
     const {
       t,
-      csrf,
+      noLoginContainer,
       registrationMode,
       registrationWhiteList,
     } = this.props;
@@ -204,8 +208,7 @@ class LoginForm extends React.Component {
           </div>
 
           <div className="input-group justify-content-center mt-5">
-            {/* [TODO][GW-2112] An AppContainer gets csrf data */}
-            <input type="hidden" name="_csrf" value={csrf} />
+            <input type="hidden" name="_csrf" value={noLoginContainer.csrfToken} />
             <button type="submit" className="btn btn-fill px-0 py-2" id="register">
               <div className="eff"></div>
               <span className="btn-label p-3">
@@ -272,14 +275,21 @@ class LoginForm extends React.Component {
 
 }
 
+/**
+ * Wrapper component for using unstated
+ */
+const LoginFormWrapper = (props) => {
+  return createSubscribedElement(LoginForm, props, [NoLoginContainer]);
+};
+
 LoginForm.propTypes = {
   // i18next
   t: PropTypes.func.isRequired,
+  noLoginContainer: PropTypes.instanceOf(NoLoginContainer).isRequired,
   isRegistering: PropTypes.bool,
   username: PropTypes.string,
   name: PropTypes.string,
   email: PropTypes.string,
-  csrf: PropTypes.string,
   isRegistrationEnabled: PropTypes.bool,
   registrationMode: PropTypes.string,
   registrationWhiteList: PropTypes.array,
@@ -288,4 +298,4 @@ LoginForm.propTypes = {
   objOfIsExternalAuthEnableds: PropTypes.object,
 };
 
-export default withTranslation()(LoginForm);
+export default withTranslation()(LoginFormWrapper);

+ 18 - 14
src/client/js/nologin.jsx

@@ -1,9 +1,12 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
+import { Provider } from 'unstated';
 import { I18nextProvider } from 'react-i18next';
 
 import i18nFactory from './util/i18n';
 
+import NoLoginContainer from './services/NoLoginContainer';
+
 import InstallerForm from './components/InstallerForm';
 import LoginForm from './components/LoginForm';
 
@@ -27,11 +30,11 @@ if (installerFormElem) {
 // render loginForm
 const loginFormElem = document.getElementById('login-form');
 if (loginFormElem) {
+  const noLoginContainer = new NoLoginContainer();
+
   const username = loginFormElem.dataset.username;
   const name = loginFormElem.dataset.name;
   const email = loginFormElem.dataset.email;
-  // [TODO][GW-2112] An AppContainer gets csrf data
-  const csrf = loginFormElem.dataset.csrf;
   const isRegistrationEnabled = loginFormElem.dataset.isRegistrationEnabled === 'true';
   const registrationMode = loginFormElem.dataset.registrationMode;
   const registrationWhiteList = loginFormElem.dataset.registrationWhiteList.split(',');
@@ -49,18 +52,19 @@ if (loginFormElem) {
 
   ReactDOM.render(
     <I18nextProvider i18n={i18n}>
-      <LoginForm
-        username={username}
-        name={name}
-        email={email}
-        csrf={csrf}
-        isRegistrationEnabled={isRegistrationEnabled}
-        registrationMode={registrationMode}
-        registrationWhiteList={registrationWhiteList}
-        isLocalStrategySetup={isLocalStrategySetup}
-        isLdapStrategySetup={isLdapStrategySetup}
-        objOfIsExternalAuthEnableds={objOfIsExternalAuthEnableds}
-      />
+      <Provider inject={[noLoginContainer]}>
+        <LoginForm
+          username={username}
+          name={name}
+          email={email}
+          isRegistrationEnabled={isRegistrationEnabled}
+          registrationMode={registrationMode}
+          registrationWhiteList={registrationWhiteList}
+          isLocalStrategySetup={isLocalStrategySetup}
+          isLdapStrategySetup={isLdapStrategySetup}
+          objOfIsExternalAuthEnableds={objOfIsExternalAuthEnableds}
+        />
+      </Provider>
     </I18nextProvider>,
     loginFormElem,
   );

+ 23 - 0
src/client/js/services/NoLoginContainer.js

@@ -0,0 +1,23 @@
+import { Container } from 'unstated';
+
+/**
+ * Service container related to Nologin (installer, login)
+ * @extends {Container} unstated Container
+ */
+export default class NoLoginContainer extends Container {
+
+  constructor() {
+    super();
+
+    const body = document.querySelector('body');
+    this.csrfToken = body.dataset.csrftoken;
+  }
+
+  /**
+   * Workaround for the mangling in production build to break constructor.name
+   */
+  static getClassName() {
+    return 'NoLoginContainer';
+  }
+
+}

+ 0 - 2
src/server/views/login.html

@@ -108,14 +108,12 @@
       {% set registrationMode = getConfig('crowi', 'security:registrationMode') %}
       {% set isRegistrationEnabled = passportService.isLocalStrategySetup && registrationMode != 'Closed' %}
 
-      <!-- [TODO][GW-2112] An AppContainer gets csrf data -->
       <div
         id="login-form"
         data-is-registering="{{ req.query.register or req.body.registerForm or isRegistering }}"
         data-username ="{{ req.body.registerForm.username }}"
         data-name ="{{ req.body.registerForm.name }}"
         data-email ="{{ req.body.registerForm.email }}"
-        data-csrf="{{ csrf() }}"
         data-is-registration-enabled="{{ isRegistrationEnabled }}"
         data-registration-mode = "{{ registrationMode }}"
         data-registration-white-list = "{{ getConfig('crowi', 'security:registrationWhiteList') }}"