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

Merge branch 'feat/growi-bot' into fix/GW-5902-prxoy-url

Shun Miyazawa 4 лет назад
Родитель
Сommit
8a0b02ef53

+ 2 - 0
config/env.dev.js

@@ -24,4 +24,6 @@ module.exports = {
   // PROMSTER_ENABLED: true,
   // SLACK_SIGNING_SECRET: '',
   // SLACK_BOT_TOKEN: '',
+  SALT_FOR_GTOP_TOKEN: 'proxy',
+  SALT_FOR_PTOG_TOKEN: 'growi',
 };

+ 9 - 14
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx

@@ -4,30 +4,22 @@ import PropTypes from 'prop-types';
 import AppContainer from '../../../services/AppContainer';
 import AdminAppContainer from '../../../services/AdminAppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
-import { toastSuccess, toastError } from '../../../util/apiNotification';
 import CustomBotWithoutProxySettingsAccordion, { botInstallationStep } from './CustomBotWithoutProxySettingsAccordion';
 import CustomBotWithoutProxyIntegrationCard from './CustomBotWithoutProxyIntegrationCard';
 import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
 
 const CustomBotWithoutProxySettings = (props) => {
-  const { appContainer } = props;
+  const { appContainer, onResetSettings } = props;
   const { t } = useTranslation();
 
   const [siteName, setSiteName] = useState('');
   const [isDeleteConfirmModalShown, setIsDeleteConfirmModalShown] = useState(false);
 
-  const deleteSlackSettingsHandler = async() => {
-    try {
-      await appContainer.apiv3.put('/slack-integration-settings/bot-type', {
-        slackSigningSecret: null,
-        slackBotToken: null,
-        currentBotType: null,
-      });
-      toastSuccess('success');
-    }
-    catch (err) {
-      toastError(err);
+  const resetSettings = async() => {
+    if (onResetSettings == null) {
+      return;
     }
+    onResetSettings();
   };
 
   useEffect(() => {
@@ -46,12 +38,14 @@ const CustomBotWithoutProxySettings = (props) => {
 
       <h2 className="admin-setting-header">{t('admin:slack_integration.custom_bot_without_proxy_settings')}</h2>
 
+      {(props.slackSigningSecret || props.slackBotToken) && (
       <button
         className="mx-3 pull-right btn text-danger border-danger"
         type="button"
         onClick={() => setIsDeleteConfirmModalShown(true)}
       >{t('admin:slack_integration.reset')}
       </button>
+      )}
 
       <div className="my-5 mx-3">
         <CustomBotWithoutProxySettingsAccordion
@@ -63,7 +57,7 @@ const CustomBotWithoutProxySettings = (props) => {
         isResetAll={false}
         isOpen={isDeleteConfirmModalShown}
         onClose={() => setIsDeleteConfirmModalShown(false)}
-        onClickDeleteButton={deleteSlackSettingsHandler}
+        onClickDeleteButton={resetSettings}
       />
     </>
   );
@@ -80,6 +74,7 @@ CustomBotWithoutProxySettings.propTypes = {
   slackBotTokenEnv: PropTypes.string,
   isRgisterSlackCredentials: PropTypes.bool,
   slackWSNameInWithoutProxy: PropTypes.string,
+  onResetSettings: PropTypes.func,
 };
 
 export default CustomBotWithoutProxySettingsWrapper;

+ 1 - 1
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettingsAccordion.jsx

@@ -34,7 +34,7 @@ const CustomBotWithoutProxySettingsAccordion = ({
 
   const updateSecretTokenHandler = async() => {
     try {
-      await appContainer.apiv3.put('/slack-integration-settings/bot-type', {
+      await appContainer.apiv3.put('/slack-integration-settings/without-proxy/update-settings', {
         slackSigningSecret,
         slackBotToken,
         currentBotType,

+ 21 - 5
src/client/js/components/Admin/SlackIntegration/OfficialbotSettingsAccordion.jsx

@@ -1,20 +1,37 @@
-import React from 'react';
+import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
+import loggerFactory from '@alias/logger';
 import Accordion from '../Common/Accordion';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
-import { toastSuccess } from '../../../util/apiNotification';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
+const logger = loggerFactory('growi:SlackBotSettings'); //
 
 const OfficialBotSettingsAccordion = (props) => {
   // TODO: apply i18n by GW-5878
   const { t } = useTranslation();
   const { appContainer } = props;
+  const [proxyUri, setProxyUri] = useState(null);
+
   const growiUrl = appContainer.config.crowi.url;
 
+  const updateProxyUri = async() => {
+    try {
+      await appContainer.apiv3.put('/slack-integration-settings/proxy-uri', {
+        proxyUri,
+      });
+      toastSuccess(t('toaster.update_successed', { target: t('Proxy URL') }));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  };
+
   return (
     <div className="card border-0 rounded-lg shadow overflow-hidden">
       <Accordion
@@ -104,14 +121,13 @@ const OfficialBotSettingsAccordion = (props) => {
               <input
                 className="form-control"
                 type="text"
+                onChange={(e) => { setProxyUri(e.target.value) }}
               />
             </div>
           </div>
           <AdminUpdateButtonRow
             disabled={false}
-            // TODO: Add Proxy URL submit logic
-            // eslint-disable-next-line no-console
-            onClick={() => console.log('Update')}
+            onClick={() => updateProxyUri()}
           />
         </div>
       </Accordion>

+ 14 - 3
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -40,7 +40,7 @@ const SlackIntegration = (props) => {
         setSlackWSNameInWithoutProxy(workspaceName);
       }
 
-      setCurrentBotType(currentBotType);
+      setCurrentBotType(data.currentBotType);
       setSlackSigningSecret(slackSigningSecret);
       setSlackBotToken(slackBotToken);
       setSlackSigningSecretEnv(slackSigningSecretEnvVars);
@@ -49,8 +49,18 @@ const SlackIntegration = (props) => {
     catch (err) {
       toastError(err);
     }
-  }, [appContainer.apiv3, currentBotType]);
+  }, [appContainer.apiv3]);
 
+  const resetWithOutSettings = async() => {
+    try {
+      await appContainer.apiv3.put('/slack-integration-settings/bot-type', { currentBotType: 'customBotWithoutProxy' });
+      fetchSlackIntegrationData();
+      toastSuccess(t('admin:slack_integration.bot_reset_successful'));
+    }
+    catch (error) {
+      toastError(error);
+    }
+  };
 
   useEffect(() => {
     fetchSlackIntegrationData();
@@ -78,12 +88,12 @@ const SlackIntegration = (props) => {
       });
       setCurrentBotType(res.data.slackBotTypeParam.slackBotType);
       setSelectedBotType(null);
-      toastSuccess(t('admin:slack_integration.bot_reset_successful'));
       setIsRegisterSlackCredentials(false);
       setSlackSigningSecret(null);
       setSlackBotToken(null);
       setIsSendTestMessage(false);
       setSlackWSNameInWithoutProxy(null);
+      toastSuccess(t('admin:slack_integration.bot_reset_successful'));
     }
     catch (err) {
       toastError(err);
@@ -109,6 +119,7 @@ const SlackIntegration = (props) => {
           onSetSlackSigningSecret={setSlackSigningSecret}
           onSetSlackBotToken={setSlackBotToken}
           onSetIsSendTestMessage={setIsSendTestMessage}
+          onResetSettings={resetWithOutSettings}
           fetchSlackIntegrationData={fetchSlackIntegrationData}
         />
       );

+ 3 - 3
src/server/models/slack-app-integration.js

@@ -10,9 +10,9 @@ class SlackAppIntegration {
   static generateAccessToken() {
     const hasher1 = crypto.createHash('sha512');
     const hasher2 = crypto.createHash('sha512');
-    const tokenGtoP = hasher1.update(`${new Date().getTime().toString()}proxy`).digest('base64');
-    const tokenPtoG = hasher2.update(`${new Date().getTime().toString()}growi`).digest('base64');
-    return [tokenGtoP, tokenPtoG];
+    const tokenGtoP = hasher1.update(new Date().getTime().toString() + process.env.SALT_FOR_GTOP_TOKEN);
+    const tokenPtoG = hasher2.update(new Date().getTime().toString() + process.env.SALT_FOR_PTOG_TOKEN);
+    return [tokenGtoP.digest('base64'), tokenPtoG.digest('base64')];
   }
 
 }

+ 75 - 19
src/server/routes/apiv3/slack-integration-settings.js

@@ -2,8 +2,6 @@ const mongoose = require('mongoose');
 const express = require('express');
 const { body } = require('express-validator');
 const axios = require('axios');
-const crypto = require('crypto');
-
 const loggerFactory = require('@alias/logger');
 
 const { getConnectionStatuses } = require('@growi/slack');
@@ -82,14 +80,6 @@ module.exports = (crowi) => {
     return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
   }
 
-  // eslint-disable-next-line no-unused-vars
-  function generateAccessToken(user) {
-    const hasher = crypto.createHash('sha512');
-    hasher.update(new Date().getTime() + user._id);
-
-    return hasher.digest('base64');
-  }
-
   async function getConnectionStatusesFromProxy(tokens) {
     const csv = tokens.join(',');
 
@@ -139,7 +129,10 @@ module.exports = (crowi) => {
 
     // retrieve connection statuses
     let connectionStatuses;
-    if (currentBotType === 'customBotWithoutProxy') {
+    if (currentBotType == null) {
+      // TODO imple null action
+    }
+    else if (currentBotType === 'customBotWithoutProxy') {
       const token = settings.slackBotToken;
       // check the token is not null
       if (token != null) {
@@ -268,29 +261,73 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /slack-integration/access-token:
+   *    /slack-integration/without-proxy/update-settings/:
+   *      put:
+   *        tags: [UpdateWithoutProxySettings]
+   *        operationId: putWithoutProxySettings
+   *        summary: update customBotWithoutProxy settings
+   *        description: Update customBotWithoutProxy setting.
+   *        responses:
+   *           200:
+   *             description: Succeeded to put CustomBotWithoutProxy setting.
+   */
+  router.put('/without-proxy/update-settings', async(req, res) => {
+    const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
+    if (currentBotType !== 'customBotWithoutProxy') {
+      const msg = 'Not CustomBotWithoutProxy';
+      return res.apiv3Err(new ErrorV3(msg, 'not-customBotWithoutProxy'), 400);
+    }
+    const { slackSigningSecret, slackBotToken } = req.body;
+    const requestParams = {
+      'slackbot:signingSecret': slackSigningSecret,
+      'slackbot:token': slackBotToken,
+    };
+    try {
+      await updateSlackBotSettings(requestParams);
+      crowi.slackBotService.publishUpdatedMessage();
+
+      const customBotWithoutProxySettingParams = {
+        slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
+        slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
+      };
+      return res.apiv3({ customBotWithoutProxySettingParams });
+    }
+    catch (error) {
+      const msg = 'Error occured in updating Custom bot setting';
+      logger.error('Error', error);
+      return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
+    }
+
+
+  });
+
+
+  /**
+   * @swagger
+   *
+   *    /slack-integration/access-tokens:
    *      put:
    *        tags: [SlackIntegration]
-   *        operationId:
+   *        operationId: putAccessTokens
    *        summary: /slack-integration
-   *        description: Generate accessToken
+   *        description: Generate accessTokens
    *        responses:
    *          200:
-   *            description: Succeeded to update access token for slack
+   *            description: Succeeded to update access tokens for slack
    */
   router.put('/access-tokens', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
     const SlackAppIntegration = mongoose.model('SlackAppIntegration');
     let checkTokens;
     let tokenGtoP;
     let tokenPtoG;
+    let generateTokens;
     do {
-      // TODO imple generate tokens at GW-5859. The following tokens is temporary.
-      tokenGtoP = 'v2';
-      tokenPtoG = 'v2';
+      generateTokens = SlackAppIntegration.generateAccessToken();
+      tokenGtoP = generateTokens[0];
+      tokenPtoG = generateTokens[1];
       // eslint-disable-next-line no-await-in-loop
       checkTokens = await SlackAppIntegration.findOne({ $or: [{ tokenGtoP }, { tokenPtoG }] });
     } while (checkTokens != null);
-
     try {
       const slackAppTokens = await SlackAppIntegration.create({ tokenGtoP, tokenPtoG });
       return res.apiv3(slackAppTokens, 200);
@@ -302,5 +339,24 @@ module.exports = (crowi) => {
     }
   });
 
+  router.put('/proxy-uri', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
+    const { proxyUri } = req.body;
+    console.log('proxyUri', proxyUri);
+
+    const requestParams = { 'slackbot:serverUri': proxyUri };
+
+    try {
+      await updateSlackBotSettings(requestParams);
+      crowi.slackBotService.publishUpdatedMessage();
+      return res.apiv3({});
+    }
+    catch (error) {
+      const msg = 'Error occured in updating Custom bot setting';
+      logger.error('Error', error);
+      return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
+    }
+
+  });
+
   return router;
 };

+ 6 - 0
src/server/service/config-loader.js

@@ -410,6 +410,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    TYPES.STRING,
     default: null,
   },
+  SERVER_URI: {
+    ns:      'crowi',
+    key:     'slackbot:serverUri',
+    type:    TYPES.STRING,
+    default: null,
+  },
   SLACK_BOT_TYPE: {
     ns:      'crowi',
     key:     'slackbot:currentBotType', // 'officialBot' || 'customBotWithoutProxy' || 'customBotWithProxy'