Преглед изворни кода

Merge pull request #1718 from weseek/reactify/create-LDAP-modal

Reactify/create ldap modal
Yuki Takei пре 6 година
родитељ
комит
3102f5ac2c

+ 144 - 0
src/client/js/components/Admin/Security/LdapAuthTest.jsx

@@ -0,0 +1,144 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+import loggerFactory from '@alias/logger';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+import AppContainer from '../../../services/AppContainer';
+import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
+
+const logger = loggerFactory('growi:security:AdminLdapSecurityContainer');
+
+class LdapAuthTest extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      logs: '',
+      errorMessage: null,
+      successMessage: null,
+    };
+
+    this.addLogs = this.addLogs.bind(this);
+    this.testLdapCredentials = this.testLdapCredentials.bind(this);
+  }
+
+  /**
+   * add logs
+   */
+  addLogs(log) {
+    const newLog = `${new Date()} - ${log}\n\n`;
+    this.setState({
+      logs: `${newLog}${this.state.logs}`,
+    });
+  }
+
+  /**
+   * Test ldap auth
+   */
+  async testLdapCredentials() {
+    try {
+      const response = await this.props.appContainer.apiPost('/login/testLdap', {
+        loginForm: {
+          username: this.props.username,
+          password: this.props.password,
+        },
+      });
+
+      // add logs
+      if (response.err) {
+        toastError(response.err);
+        this.addLogs(response.err);
+      }
+
+      if (response.status === 'warning') {
+        this.addLogs(response.message);
+        this.setState({ errorMessage: response.message, successMessage: null });
+      }
+
+      if (response.status === 'success') {
+        toastSuccess(response.message);
+        this.setState({ successMessage: response.message, errorMessage: null });
+      }
+
+      if (response.ldapConfiguration) {
+        const prettified = JSON.stringify(response.ldapConfiguration.server, undefined, 4);
+        this.addLogs(`LDAP Configuration : ${prettified}`);
+      }
+      if (response.ldapAccountInfo) {
+        const prettified = JSON.stringify(response.ldapAccountInfo, undefined, 4);
+        this.addLogs(`Retrieved LDAP Account : ${prettified}`);
+      }
+
+    }
+    // Catch server communication error
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  render() {
+    const { t } = this.props;
+
+    return (
+      <React.Fragment>
+        {this.state.successMessage != null && <div className="alert alert-success">{this.state.successMessage}</div>}
+        {this.state.errorMessage != null && <div className="alert alert-warning">{this.state.errorMessage}</div>}
+        <div className="row p-3">
+          <label htmlFor="username" className="col-xs-3 text-right">{t('username')}</label>
+          <div className="col-xs-6">
+            <input
+              className="form-control"
+              name="username"
+              value={this.props.username}
+              onChange={(e) => { this.props.onChangeUsername(e.target.value) }}
+            />
+          </div>
+        </div>
+        <div className="row p-3">
+          <label htmlFor="password" className="col-xs-3 text-right">{t('Password')}</label>
+          <div className="col-xs-6">
+            <input
+              className="form-control"
+              type="password"
+              name="password"
+              value={this.props.password}
+              onChange={(e) => { this.props.onChangePassword(e.target.value) }}
+            />
+          </div>
+        </div>
+        <div>
+          <h5>Logs</h5>
+          <textarea id="taLogs" className="col-xs-12" rows="4" value={this.state.logs} readOnly />
+        </div>
+
+        <button type="button" className="btn btn-default mt-3 col-xs-offset-5 col-xs-2" onClick={this.testLdapCredentials}>Test</button>
+
+      </React.Fragment>
+
+    );
+  }
+
+}
+
+
+LdapAuthTest.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminLdapSecurityContainer: PropTypes.instanceOf(AdminLdapSecurityContainer).isRequired,
+
+  username: PropTypes.string.isRequired,
+  password: PropTypes.string.isRequired,
+  onChangeUsername: PropTypes.func.isRequired,
+  onChangePassword: PropTypes.func.isRequired,
+};
+
+const LdapAuthTestWrapper = (props) => {
+  return createSubscribedElement(LdapAuthTest, props, [AppContainer, AdminLdapSecurityContainer]);
+};
+
+export default withTranslation()(LdapAuthTestWrapper);

+ 8 - 96
src/client/js/components/Admin/Security/LdapAuthTestModal.jsx

@@ -1,17 +1,15 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import Modal from 'react-bootstrap/es/Modal';
 import Modal from 'react-bootstrap/es/Modal';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
-import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 
 import AppContainer from '../../../services/AppContainer';
 import AppContainer from '../../../services/AppContainer';
 import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
 import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
+import LdapAuthTest from './LdapAuthTest';
 
 
-const logger = loggerFactory('growi:security:AdminLdapSecurityContainer');
 
 
 class LdapAuthTestModal extends React.Component {
 class LdapAuthTestModal extends React.Component {
 
 
@@ -21,15 +19,10 @@ class LdapAuthTestModal extends React.Component {
     this.state = {
     this.state = {
       username: '',
       username: '',
       password: '',
       password: '',
-      logs: '',
-      errorMessage: null,
-      successMessage: null,
     };
     };
 
 
     this.onChangeUsername = this.onChangeUsername.bind(this);
     this.onChangeUsername = this.onChangeUsername.bind(this);
     this.onChangePassword = this.onChangePassword.bind(this);
     this.onChangePassword = this.onChangePassword.bind(this);
-    this.addLogs = this.addLogs.bind(this);
-    this.testLdapCredentials = this.testLdapCredentials.bind(this);
   }
   }
 
 
   /**
   /**
@@ -46,63 +39,7 @@ class LdapAuthTestModal extends React.Component {
     this.setState({ password });
     this.setState({ password });
   }
   }
 
 
-  /**
-   * add logs
-   */
-  addLogs(log) {
-    const newLog = `${new Date()} - ${log}\n\n`;
-    this.setState({
-      logs: `${newLog}${this.state.logs}`,
-    });
-  }
-
-  /**
-   * Test ldap auth
-   */
-  async testLdapCredentials() {
-    try {
-      const response = await this.props.appContainer.apiPost('/login/testLdap', {
-        loginForm: {
-          username: this.state.username,
-          password: this.state.password,
-        },
-      });
-
-      // add logs
-      if (response.err) {
-        toastError(response.err);
-        this.addLogs(response.err);
-      }
-
-      if (response.status === 'warning') {
-        this.addLogs(response.message);
-        this.setState({ errorMessage: response.message, successMessage: null });
-      }
-
-      if (response.status === 'success') {
-        toastSuccess(response.message);
-        this.setState({ successMessage: response.message, errorMessage: null });
-      }
-
-      if (response.ldapConfiguration) {
-        const prettified = JSON.stringify(response.ldapConfiguration.server, undefined, 4);
-        this.addLogs(`LDAP Configuration : ${prettified}`);
-      }
-      if (response.ldapAccountInfo) {
-        const prettified = JSON.stringify(response.ldapAccountInfo, undefined, 4);
-        this.addLogs(`Retrieved LDAP Account : ${prettified}`);
-      }
-
-    }
-    // Catch server communication error
-    catch (err) {
-      toastError(err);
-      logger.error(err);
-    }
-  }
-
   render() {
   render() {
-    const { t } = this.props;
 
 
     return (
     return (
       <Modal show={this.props.isOpen} onHide={this.props.onClose}>
       <Modal show={this.props.isOpen} onHide={this.props.onClose}>
@@ -112,39 +49,14 @@ class LdapAuthTestModal extends React.Component {
           </Modal.Title>
           </Modal.Title>
         </Modal.Header>
         </Modal.Header>
         <Modal.Body>
         <Modal.Body>
-          {this.state.successMessage != null && <div className="alert alert-success">{this.state.successMessage}</div>}
-          {this.state.errorMessage != null && <div className="alert alert-warning">{this.state.errorMessage}</div>}
-          <div className="row p-3">
-            <label htmlFor="username" className="col-xs-3 text-right">{t('username')}</label>
-            <div className="col-xs-6">
-              <input
-                className="form-control"
-                name="username"
-                value={this.state.username}
-                onChange={(e) => { this.onChangeUsername(e.target.value) }}
-              />
-            </div>
-          </div>
-          <div className="row p-3">
-            <label htmlFor="password" className="col-xs-3 text-right">{t('Password')}</label>
-            <div className="col-xs-6">
-              <input
-                className="form-control"
-                type="password"
-                name="password"
-                value={this.state.password}
-                onChange={(e) => { this.onChangePassword(e.target.value) }}
-              />
-            </div>
-          </div>
-          <div>
-            <h5>Logs</h5>
-            <textarea id="taLogs" className="col-xs-12" rows="4" value={this.state.logs} readOnly />
-          </div>
+          <LdapAuthTest
+            username={this.state.username}
+            password={this.state.password}
+            onChangeUsername={this.onChangeUsername}
+            onChangePassword={this.onChangePassword}
+          />
         </Modal.Body>
         </Modal.Body>
-        <Modal.Footer>
-          <button type="button" className="btn btn-default mt-3 col-xs-offset-5 col-xs-2" onClick={this.testLdapCredentials}>Test</button>
-        </Modal.Footer>
+        <Modal.Footer />
       </Modal>
       </Modal>
     );
     );
   }
   }

+ 104 - 0
src/client/js/components/Me/AssociateModal.jsx

@@ -0,0 +1,104 @@
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import Modal from 'react-bootstrap/es/Modal';
+import { toastSuccess, toastError } from '../../util/apiNotification';
+import { createSubscribedElement } from '../UnstatedUtils';
+
+import AppContainer from '../../services/AppContainer';
+import PersonalContainer from '../../services/PersonalContainer';
+
+import LdapAuthTest from '../Admin/Security/LdapAuthTest';
+
+class AssociateModal extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      username: '',
+      password: '',
+    };
+
+    this.onChangeUsername = this.onChangeUsername.bind(this);
+    this.onChangePassword = this.onChangePassword.bind(this);
+    this.onClickAddBtn = this.onClickAddBtn.bind(this);
+  }
+
+  /**
+   * Change username
+   */
+  onChangeUsername(username) {
+    this.setState({ username });
+  }
+
+  /**
+   * Change password
+   */
+  onChangePassword(password) {
+    this.setState({ password });
+  }
+
+  async onClickAddBtn() {
+    const { t, personalContainer } = this.props;
+    try {
+      await personalContainer.associateLdapAccount();
+      toastSuccess(t('security_setting.updated_general_security_setting'));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }
+
+  render() {
+    const { t } = this.props;
+
+    return (
+      <Modal show={this.props.isOpen} onHide={this.props.onClose}>
+        <Modal.Header className="bg-info" closeButton>
+          <Modal.Title className="text-white">
+            { t('Create External Account') }
+          </Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <ul className="nav nav-tabs passport-settings mb-2" role="tablist">
+            <li className="active">
+              <a href="#passport-ldap" data-toggle="tab" role="tab"><i className="fa fa-sitemap"></i> LDAP</a>
+            </li>
+          </ul>
+          <LdapAuthTest
+            username={this.state.username}
+            password={this.state.password}
+            onChangeUsername={this.onChangeUsername}
+            onChangePassword={this.onChangePassword}
+          />
+        </Modal.Body>
+        <Modal.Footer>
+          <button type="button" className="btn btn-info mt-3" onClick={this.onClickAddBtn}>
+            <i className="fa fa-plus-circle" aria-hidden="true"></i>
+            {t('add')}
+          </button>
+        </Modal.Footer>
+      </Modal>
+    );
+  }
+
+}
+
+const AssociateModalWrapper = (props) => {
+  return createSubscribedElement(AssociateModal, props, [AppContainer, PersonalContainer]);
+};
+
+AssociateModal.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  personalContainer: PropTypes.instanceOf(PersonalContainer).isRequired,
+
+  isOpen: PropTypes.bool.isRequired,
+  onClose: PropTypes.func.isRequired,
+};
+
+
+export default withTranslation()(AssociateModalWrapper);

+ 27 - 1
src/client/js/components/Me/ExternalAccountLinkedMe.jsx

@@ -9,9 +9,21 @@ import { toastError } from '../../util/apiNotification';
 import AppContainer from '../../services/AppContainer';
 import AppContainer from '../../services/AppContainer';
 import PersonalContainer from '../../services/PersonalContainer';
 import PersonalContainer from '../../services/PersonalContainer';
 import ExternalAccountRow from './ExternalAccountRow';
 import ExternalAccountRow from './ExternalAccountRow';
+import AssociateModal from './AssociateModal';
 
 
 class ExternalAccountLinkedMe extends React.Component {
 class ExternalAccountLinkedMe extends React.Component {
 
 
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      isAssociatModalOpen: false,
+    };
+
+    this.openAssociatModal = this.openAssociatModal.bind(this);
+    this.closeAssociatModal = this.closeAssociatModal.bind(this);
+  }
+
   async componentDidMount() {
   async componentDidMount() {
     try {
     try {
       await this.props.personalContainer.retrieveExternalAccounts();
       await this.props.personalContainer.retrieveExternalAccounts();
@@ -21,6 +33,14 @@ class ExternalAccountLinkedMe extends React.Component {
     }
     }
   }
   }
 
 
+  openAssociatModal() {
+    this.setState({ isAssociatModalOpen: true });
+  }
+
+  closeAssociatModal() {
+    this.setState({ isAssociatModalOpen: false });
+  }
+
   render() {
   render() {
     const { t, personalContainer } = this.props;
     const { t, personalContainer } = this.props;
     const { externalAccounts } = personalContainer.state;
     const { externalAccounts } = personalContainer.state;
@@ -29,7 +49,7 @@ class ExternalAccountLinkedMe extends React.Component {
       <Fragment>
       <Fragment>
         <div className="container-fluid">
         <div className="container-fluid">
           <h2 className="border-bottom">
           <h2 className="border-bottom">
-            <button type="button" className="btn btn-default btn-sm pull-right">
+            <button type="button" className="btn btn-default btn-sm pull-right" onClick={this.openAssociatModal}>
               <i className="icon-plus" aria-hidden="true" />
               <i className="icon-plus" aria-hidden="true" />
             Add
             Add
             </button>
             </button>
@@ -56,6 +76,12 @@ class ExternalAccountLinkedMe extends React.Component {
             </table>
             </table>
           </div>
           </div>
         </div>
         </div>
+
+        <AssociateModal
+          isOpen={this.state.isAssociatModalOpen}
+          onClose={this.closeAssociatModal}
+        />
+
       </Fragment>
       </Fragment>
     );
     );
   }
   }

+ 7 - 0
src/client/js/services/PersonalContainer.js

@@ -217,4 +217,11 @@ export default class PersonalContainer extends Container {
     }
     }
   }
   }
 
 
+  /**
+   * Associate LDAP account
+   */
+  async associateLdapAccount() {
+    // TODO create apiV3 for associate LDAP account
+  }
+
 }
 }