Просмотр исходного кода

Merge pull request #1421 from weseek/reactify-admin/google-security-setting

Reactify admin/google security setting
Yuki Takei 6 лет назад
Родитель
Сommit
68d5484554

+ 3 - 2
resource/locales/en-US/translation.json

@@ -550,11 +550,12 @@
       "register": "Register for %s",
       "register": "Register for %s",
       "change_redirect_url": "Enter <code>%s</code> <br>(where <code>%s</code> is your host name) for \"Authorized redirect URIs\".",
       "change_redirect_url": "Enter <code>%s</code> <br>(where <code>%s</code> is your host name) for \"Authorized redirect URIs\".",
       "Google": {
       "Google": {
+        "enable_google":"enable Google OAuth",
         "name": "Google OAuth",
         "name": "Google OAuth",
-        "register_1": "Access <a href=\"%s\" target=\"_blank\">%s</a>",
+        "register_1": "Access {{link}}",
         "register_2": "Create Project if no projects exist",
         "register_2": "Create Project if no projects exist",
         "register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
         "register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
-        "register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>%s</code>",
+        "register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>{{url}}</code>",
         "register_5": "Copy and paste your ClientID and Client Secret above"
         "register_5": "Copy and paste your ClientID and Client Secret above"
       },
       },
       "Facebook": {
       "Facebook": {

+ 3 - 2
resource/locales/ja/translation.json

@@ -545,11 +545,12 @@
       "register": "%sに登録",
       "register": "%sに登録",
       "change_redirect_url": "承認済みのリダイレクトURLに、 <code>%s</code> を入力",
       "change_redirect_url": "承認済みのリダイレクトURLに、 <code>%s</code> を入力",
       "Google": {
       "Google": {
+        "enable_google":"Google OAuth を有効にする",
         "name": "Google OAuth",
         "name": "Google OAuth",
-        "register_1": "<a href=\"%s\" target=\"_blank\">%s</a>へアクセス",
+        "register_1": "{{link}}へアクセス",
         "register_2": "プロジェクトがない場合はプロジェクトを作成",
         "register_2": "プロジェクトがない場合はプロジェクトを作成",
         "register_3": "認証情報を作成 &rightarrow; OAuthクライアントID &rightarrow; ウェブアプリケーションを選択",
         "register_3": "認証情報を作成 &rightarrow; OAuthクライアントID &rightarrow; ウェブアプリケーションを選択",
-        "register_4": "承認済みのリダイレクトURIを<code>%s</code>としてGrowiを登録",
+        "register_4": "承認済みのリダイレクトURIを<code>{{url}}</code>としてGrowiを登録",
         "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力"
         "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力"
       },
       },
       "Facebook": {
       "Facebook": {

+ 3 - 0
src/client/js/app.jsx

@@ -63,6 +63,7 @@ import AdminExternalAccountsContainer from './services/AdminExternalAccountsCont
 import AdminSamlSecurityContainer from './services/AdminSamlSecurityContainer';
 import AdminSamlSecurityContainer from './services/AdminSamlSecurityContainer';
 import AdminOidcSecurityContainer from './services/AdminOidcSecurityContainer';
 import AdminOidcSecurityContainer from './services/AdminOidcSecurityContainer';
 import AdminBasicSecurityContainer from './services/AdminBasicSecurityContainer';
 import AdminBasicSecurityContainer from './services/AdminBasicSecurityContainer';
+import AdminGoogleSecurityContainer from './services/AdminGoogleSecurityContainer';
 
 
 const logger = loggerFactory('growi:app');
 const logger = loggerFactory('growi:app');
 
 
@@ -223,8 +224,10 @@ if (adminSecuritySettingElem != null) {
   const adminSamlSecurityContainer = new AdminSamlSecurityContainer(appContainer);
   const adminSamlSecurityContainer = new AdminSamlSecurityContainer(appContainer);
   const adminOidcSecurityContainer = new AdminOidcSecurityContainer(appContainer);
   const adminOidcSecurityContainer = new AdminOidcSecurityContainer(appContainer);
   const adminBasicSecurityContainer = new AdminBasicSecurityContainer(appContainer);
   const adminBasicSecurityContainer = new AdminBasicSecurityContainer(appContainer);
+  const adminGoogleSecurityContainer = new AdminGoogleSecurityContainer(appContainer);
   const adminSecurityContainers = [
   const adminSecurityContainers = [
     adminGeneralSecurityContainer, adminLdapSecurityContainer, adminSamlSecurityContainer, adminOidcSecurityContainer, adminBasicSecurityContainer,
     adminGeneralSecurityContainer, adminLdapSecurityContainer, adminSamlSecurityContainer, adminOidcSecurityContainer, adminBasicSecurityContainer,
+    adminGoogleSecurityContainer,
   ];
   ];
   ReactDOM.render(
   ReactDOM.render(
     <Provider inject={[injectableContainers, adminSecurityContainers]}>
     <Provider inject={[injectableContainers, adminSecurityContainers]}>

+ 159 - 0
src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx

@@ -0,0 +1,159 @@
+/* eslint-disable react/no-danger */
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+
+import AppContainer from '../../../services/AppContainer';
+import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
+import AdminGoogleSecurityContainer from '../../../services/AdminGoogleSecurityContainer';
+
+class GoogleSecurityManagement extends React.Component {
+
+  render() {
+    const { t, adminGeneralSecurityContainer, adminGoogleSecurityContainer } = this.props;
+    return (
+
+      <React.Fragment>
+
+        <h2 className="alert-anchor border-bottom">
+          { t('security_setting.OAuth.Google.name') } { t('security_setting.configuration') }
+        </h2>
+
+        <div className="row mb-5">
+          <strong className="col-xs-3 text-right">{ t('security_setting.OAuth.Google.name') }</strong>
+          <div className="col-xs-6 text-left">
+            <div className="checkbox checkbox-success">
+              <input
+                id="isGoogleEnabled"
+                type="checkbox"
+                checked={adminGeneralSecurityContainer.state.isGoogleOAuthEnabled}
+                onChange={() => { adminGeneralSecurityContainer.switchIsGoogleOAuthEnabled() }}
+              />
+              <label htmlFor="isGoogleEnabled">
+                { t('security_setting.OAuth.Google.enable_google') }
+              </label>
+            </div>
+          </div>
+        </div>
+
+        <div className="row mb-5">
+          <label className="col-xs-3 text-right">{ t('security_setting.callback_URL') }</label>
+          <div className="col-xs-6">
+            <input
+              className="form-control"
+              type="text"
+              value={adminGoogleSecurityContainer.state.callbackUrl}
+              readOnly
+            />
+            <p className="help-block small">{ t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' }) }</p>
+            {!adminGeneralSecurityContainer.state.appSiteUrl && (
+            <div className="alert alert-danger">
+              <i
+                className="icon-exclamation"
+                // eslint-disable-next-line max-len
+                dangerouslySetInnerHTML={{ __html: t('security_setting.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('App settings')}<i class="icon-login"></i></a>` }) }}
+              />
+            </div>
+            )}
+          </div>
+        </div>
+
+
+        {adminGeneralSecurityContainer.state.isGoogleOAuthEnabled && (
+          <React.Fragment>
+
+            <div className="row mb-5">
+              <label htmlFor="googleClientId" className="col-xs-3 text-right">{ t('security_setting.clientID') }</label>
+              <div className="col-xs-6">
+                <input
+                  className="form-control"
+                  type="text"
+                  name="googleClientId"
+                  value={adminGoogleSecurityContainer.state.googleClientId}
+                  onChange={e => adminGoogleSecurityContainer.changeGoogleClientId(e.target.value)}
+                />
+                <p className="help-block">
+                  <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_ID' }) }} />
+                </p>
+              </div>
+            </div>
+
+            <div className="row mb-5">
+              <label htmlFor="googleClientSecret" className="col-xs-3 text-right">{ t('security_setting.client_secret') }</label>
+              <div className="col-xs-6">
+                <input
+                  className="form-control"
+                  type="text"
+                  name="googleClientSecret"
+                  value={adminGoogleSecurityContainer.state.googleClientSecret}
+                  onChange={e => adminGoogleSecurityContainer.changeGoogleClientSecret(e.target.value)}
+                />
+                <p className="help-block">
+                  <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_SECRET' }) }} />
+                </p>
+              </div>
+            </div>
+
+            <div className="row mb-5">
+              <div className="col-xs-offset-3 col-xs-6 text-left">
+                <div className="checkbox checkbox-success">
+                  <input
+                    id="bindByUserNameGoogle"
+                    type="checkbox"
+                    checked={adminGoogleSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser}
+                    onChange={() => { adminGoogleSecurityContainer.switchIsSameUsernameTreatedAsIdenticalUser() }}
+                  />
+                  <label
+                    htmlFor="bindByUserNameGoogle"
+                    dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
+                  />
+                </div>
+                <p className="help-block">
+                  <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
+                </p>
+              </div>
+            </div>
+
+          </React.Fragment>
+        )}
+
+        <hr />
+
+        <div style={{ minHeight: '300px' }}>
+          <h4>
+            <i className="icon-question" aria-hidden="true"></i>
+            <a href="#collapseHelpForGoogleOauth" data-toggle="collapse">{ t('security_setting.OAuth.how_to.google') }</a>
+          </h4>
+          <ol id="collapseHelpForGoogleOauth" className="collapse">
+            {/* eslint-disable-next-line max-len */}
+            <li dangerouslySetInnerHTML={{ __html:  t('security_setting.OAuth.Google.register_1', { link: '<a href="https://console.cloud.google.com/apis/credentials" target=_blank>Google Cloud Platform API Manager</a>' }) }} />
+            <li dangerouslySetInnerHTML={{ __html:  t('security_setting.OAuth.Google.register_2') }} />
+            <li dangerouslySetInnerHTML={{ __html:  t('security_setting.OAuth.Google.register_3') }} />
+            <li dangerouslySetInnerHTML={{ __html:  t('security_setting.OAuth.Google.register_4', { url: adminGoogleSecurityContainer.state.callbackUrl }) }} />
+            <li dangerouslySetInnerHTML={{ __html:  t('security_setting.OAuth.Google.register_5') }} />
+          </ol>
+        </div>
+
+      </React.Fragment>
+
+
+    );
+  }
+
+}
+
+
+GoogleSecurityManagement.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
+  adminGoogleSecurityContainer: PropTypes.instanceOf(AdminGoogleSecurityContainer).isRequired,
+};
+
+const GoogleSecurityManagementWrapper = (props) => {
+  return createSubscribedElement(GoogleSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminGoogleSecurityContainer]);
+};
+
+export default withTranslation()(GoogleSecurityManagementWrapper);

+ 2 - 1
src/client/js/components/Admin/Security/SecurityManagement.jsx

@@ -10,6 +10,7 @@ import LocalSecuritySetting from './LocalSecuritySetting';
 import SamlSecuritySetting from './SamlSecuritySetting';
 import SamlSecuritySetting from './SamlSecuritySetting';
 import OidcSecuritySetting from './OidcSecuritySetting';
 import OidcSecuritySetting from './OidcSecuritySetting';
 import BasicSecuritySetting from './BasicSecuritySetting';
 import BasicSecuritySetting from './BasicSecuritySetting';
+import GoogleSecuritySetting from './GoogleSecuritySetting';
 
 
 class SecurityManagement extends React.Component {
 class SecurityManagement extends React.Component {
 
 
@@ -159,7 +160,7 @@ class SecurityManagement extends React.Component {
                 <BasicSecuritySetting />
                 <BasicSecuritySetting />
               </div>
               </div>
               <div id="passport-google-oauth" className="tab-pane" role="tabpanel">
               <div id="passport-google-oauth" className="tab-pane" role="tabpanel">
-                {/* TODO GW-547 reactify google-oauth.html */}
+                <GoogleSecuritySetting />
               </div>
               </div>
               <div id="passport-github" className="tab-pane" role="tabpanel">
               <div id="passport-github" className="tab-pane" role="tabpanel">
                 {/* TODO GW-548 reactify github.html */}
                 {/* TODO GW-548 reactify github.html */}

+ 8 - 0
src/client/js/services/AdminGeneralSecurityContainer.js

@@ -27,6 +27,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       isSamlEnabled: true,
       isSamlEnabled: true,
       isOidcEnabled: true,
       isOidcEnabled: true,
       isBasicEnabled: true,
       isBasicEnabled: true,
+      isGoogleOAuthEnabled: true,
     };
     };
 
 
     this.init();
     this.init();
@@ -89,4 +90,11 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ isBasicEnabled: !this.state.isBasicEnabled });
     this.setState({ isBasicEnabled: !this.state.isBasicEnabled });
   }
   }
 
 
+  /**
+   * Switch GoogleOAuth enabled
+   */
+  switchIsGoogleOAuthEnabled() {
+    this.setState({ isGoogleOAuthEnabled: !this.state.isGoogleOAuthEnabled });
+  }
+
 }
 }

+ 63 - 0
src/client/js/services/AdminGoogleSecurityContainer.js

@@ -0,0 +1,63 @@
+import { Container } from 'unstated';
+
+import loggerFactory from '@alias/logger';
+
+// eslint-disable-next-line no-unused-vars
+const logger = loggerFactory('growi:security:AdminGoogleSecurityContainer');
+
+/**
+ * Service container for admin security page (GoogleSecurityManagement.jsx)
+ * @extends {Container} unstated Container
+ */
+export default class AdminGoogleSecurityContainer extends Container {
+
+  constructor(appContainer) {
+    super();
+
+    this.appContainer = appContainer;
+
+    this.state = {
+      // TODO GW-583 set value
+      appSiteUrl: '',
+      googleClientId: '',
+      googleClientSecret: '',
+      isSameUsernameTreatedAsIdenticalUser: true,
+    };
+
+    this.init();
+
+  }
+
+  init() {
+    // TODO GW-583 fetch config value with api
+  }
+
+  /**
+   * Workaround for the mangling in production build to break constructor.name
+   */
+  static getClassName() {
+    return 'AdminGoogleSecurityContainer';
+  }
+
+  /**
+   * Change googleClientId
+   */
+  changeGoogleClientId(value) {
+    this.setState({ googleClientId: value });
+  }
+
+  /**
+   * Change googleClientSecret
+   */
+  changeGoogleClientSecret(value) {
+    this.setState({ googleClientSecret: value });
+  }
+
+  /**
+   * Switch isSameUsernameTreatedAsIdenticalUser
+   */
+  switchIsSameUsernameTreatedAsIdenticalUser() {
+    this.setState({ isSameUsernameTreatedAsIdenticalUser: !this.state.isSameUsernameTreatedAsIdenticalUser });
+  }
+
+}