slack-integration.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. const loggerFactory = require('@alias/logger');
  2. const logger = loggerFactory('growi:routes:apiv3:notification-setting');
  3. const express = require('express');
  4. const { body } = require('express-validator');
  5. const crypto = require('crypto');
  6. const ErrorV3 = require('../../models/vo/error-apiv3');
  7. const router = express.Router();
  8. /**
  9. * @swagger
  10. * tags:
  11. * name: SlackIntegration
  12. */
  13. /**
  14. * @swagger
  15. *
  16. * components:
  17. * schemas:
  18. * CustomBotWithoutProxy:
  19. * description: CustomBotWithoutProxy
  20. * type: object
  21. * properties:
  22. * slackSigningSecret:
  23. * type: string
  24. * slackBotToken:
  25. * type: string
  26. * currentBotType:
  27. * type: string
  28. * SlackIntegration:
  29. * description: SlackIntegration
  30. * type: object
  31. * properties:
  32. * currentBotType:
  33. * type: string
  34. */
  35. module.exports = (crowi) => {
  36. const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
  37. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  38. const adminRequired = require('../../middlewares/admin-required')(crowi);
  39. const csrf = require('../../middlewares/csrf')(crowi);
  40. const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
  41. const validator = {
  42. CustomBotWithoutProxy: [
  43. body('slackSigningSecret').isString(),
  44. body('slackBotToken').isString(),
  45. body('currentBotType').isString(),
  46. ],
  47. SlackIntegration: [
  48. body('currentBotType')
  49. .isIn(['official-bot', 'custom-bot-without-proxy', 'custom-bot-with-proxy']),
  50. ],
  51. };
  52. async function updateSlackBotSettings(params) {
  53. const { configManager } = crowi;
  54. // update config without publishing S2sMessage
  55. return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
  56. }
  57. function generateAccessToken(user) {
  58. const hasher = crypto.createHash('sha512');
  59. hasher.update(new Date().getTime() + user._id);
  60. return hasher.digest('base64');
  61. }
  62. /**
  63. * @swagger
  64. *
  65. * /slack-integration/:
  66. * get:
  67. * tags: [SlackBotSettingParams]
  68. * operationId: getSlackBotSettingParams
  69. * summary: get /slack-integration
  70. * description: Get slackBot setting params.
  71. * responses:
  72. * 200:
  73. * description: Succeeded to get slackBot setting params.
  74. */
  75. router.get('/', accessTokenParser, loginRequiredStrictly, adminRequired, async(req, res) => {
  76. const slackBotSettingParams = {
  77. accessToken: crowi.configManager.getConfig('crowi', 'slackbot:access-token'),
  78. currentBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType'),
  79. // TODO impl when creating official bot
  80. officialBotSettings: {
  81. // TODO impl this after GW-4939
  82. // AccessToken: "tempaccessdatahogehoge",
  83. },
  84. customBotWithoutProxySettings: {
  85. // TODO impl this after GW-4939
  86. // AccessToken: "tempaccessdatahogehoge",
  87. slackSigningSecretEnvVars: crowi.configManager.getConfigFromEnvVars('crowi', 'slackbot:signingSecret'),
  88. slackBotTokenEnvVars: crowi.configManager.getConfigFromEnvVars('crowi', 'slackbot:token'),
  89. slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
  90. slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
  91. isSetupSlackBot: crowi.slackBotService.isSetupSlackBot,
  92. isConnectedToSlack: crowi.slackBotService.isConnectedToSlack,
  93. },
  94. // TODO imple when creating with proxy
  95. customBotWithProxySettings: {
  96. // TODO impl this after GW-4939
  97. // AccessToken: "tempaccessdatahogehoge",
  98. },
  99. };
  100. return res.apiv3({ slackBotSettingParams });
  101. });
  102. /**
  103. * @swagger
  104. *
  105. * /slack-integration/:
  106. * put:
  107. * tags: [SlackIntegration]
  108. * operationId: putSlackIntegration
  109. * summary: put /slack-integration
  110. * description: Put SlackIntegration setting.
  111. * requestBody:
  112. * required: true
  113. * content:
  114. * application/json:
  115. * schema:
  116. * $ref: '#/components/schemas/SlackIntegration'
  117. * responses:
  118. * 200:
  119. * description: Succeeded to put Slack Integration setting.
  120. */
  121. router.put('/',
  122. accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.SlackIntegration, apiV3FormValidator, async(req, res) => {
  123. const { currentBotType } = req.body;
  124. const requestParams = {
  125. 'slackbot:currentBotType': currentBotType,
  126. };
  127. try {
  128. await updateSlackBotSettings(requestParams);
  129. // initialize slack service
  130. await crowi.slackBotService.initialize();
  131. crowi.slackBotService.publishUpdatedMessage();
  132. const slackIntegrationSettingsParams = {
  133. currentBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType'),
  134. };
  135. return res.apiv3({ slackIntegrationSettingsParams });
  136. }
  137. catch (error) {
  138. const msg = 'Error occured in updating Slack bot setting';
  139. logger.error('Error', error);
  140. return res.apiv3Err(new ErrorV3(msg, 'update-SlackIntegrationSetting-failed'), 500);
  141. }
  142. });
  143. /**
  144. * @swagger
  145. *
  146. * /slack-integration/custom-bot-without-proxy/:
  147. * put:
  148. * tags: [CustomBotWithoutProxy]
  149. * operationId: putCustomBotWithoutProxy
  150. * summary: /slack-integration/custom-bot-without-proxy
  151. * description: Put customBotWithoutProxy setting.
  152. * requestBody:
  153. * required: true
  154. * content:
  155. * application/json:
  156. * schema:
  157. * $ref: '#/components/schemas/CustomBotWithoutProxy'
  158. * responses:
  159. * 200:
  160. * description: Succeeded to put CustomBotWithoutProxy setting.
  161. */
  162. router.put('/custom-bot-without-proxy',
  163. accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.CustomBotWithoutProxy, apiV3FormValidator, async(req, res) => {
  164. const { slackSigningSecret, slackBotToken, currentBotType } = req.body;
  165. const requestParams = {
  166. 'slackbot:signingSecret': slackSigningSecret,
  167. 'slackbot:token': slackBotToken,
  168. 'slackbot:currentBotType': currentBotType,
  169. };
  170. try {
  171. await updateSlackBotSettings(requestParams);
  172. // initialize slack service
  173. await crowi.slackBotService.initialize();
  174. crowi.slackBotService.publishUpdatedMessage();
  175. // TODO Impl to delete AccessToken both of Proxy and GROWI when botType changes.
  176. const customBotWithoutProxySettingParams = {
  177. slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
  178. slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
  179. slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType'),
  180. };
  181. return res.apiv3({ customBotWithoutProxySettingParams });
  182. }
  183. catch (error) {
  184. const msg = 'Error occured in updating Custom bot setting';
  185. logger.error('Error', error);
  186. return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
  187. }
  188. });
  189. /**
  190. * @swagger
  191. *
  192. * /slack-integration/custom-bot-without-proxy/slack-workspace-name:
  193. * get:
  194. * tags: [slackWorkSpaceName]
  195. * operationId: getSlackWorkSpaceName
  196. * summary: Get slack work space name for custom bot without proxy
  197. * description: get slack WS name in custom bot without proxy
  198. * responses:
  199. * 200:
  200. * description: Succeeded to get slack ws name for custom bot without proxy
  201. */
  202. router.get('/custom-bot-without-proxy/slack-workspace-name', loginRequiredStrictly, adminRequired, async(req, res) => {
  203. try {
  204. const slackWorkSpaceName = await crowi.slackBotService.getSlackChannelName();
  205. return res.apiv3({ slackWorkSpaceName });
  206. }
  207. catch (error) {
  208. const msg = 'Error occured in slack_bot_token';
  209. logger.error('Error', error);
  210. return res.apiv3Err(new ErrorV3(msg, 'get-SlackWorkSpaceName-failed'), 500);
  211. }
  212. });
  213. /**
  214. * @swagger
  215. *
  216. * /slack-integration/access-token:
  217. * put:
  218. * tags: [SlackIntegration]
  219. * operationId: getCustomBotSetting
  220. * summary: /slack-integration
  221. * description: Generate accessToken
  222. * responses:
  223. * 200:
  224. * description: Succeeded to update access token for slack
  225. */
  226. router.put('/access-token', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
  227. try {
  228. const accessToken = generateAccessToken(req.user);
  229. await updateSlackBotSettings({ 'slackbot:access-token': accessToken });
  230. // initialize slack service
  231. await crowi.slackBotService.initialize();
  232. crowi.slackBotService.publishUpdatedMessage();
  233. return res.apiv3({ accessToken });
  234. }
  235. catch (error) {
  236. const msg = 'Error occured in updating access token for access token';
  237. logger.error('Error', error);
  238. return res.apiv3Err(new ErrorV3(msg, 'update-accessToken-failed'), 500);
  239. }
  240. });
  241. /**
  242. * @swagger
  243. *
  244. * /slack-integration/access-token:
  245. * delete:
  246. * tags: [SlackIntegration]
  247. * operationId: deleteAccessTokenForSlackBot
  248. * summary: /slack-integration
  249. * description: Delete accessToken
  250. * responses:
  251. * 200:
  252. * description: Succeeded to delete accessToken
  253. */
  254. router.delete('/access-token', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
  255. try {
  256. await updateSlackBotSettings({ 'slackbot:access-token': null });
  257. // initialize slack service
  258. await crowi.slackBotService.initialize();
  259. crowi.slackBotService.publishUpdatedMessage();
  260. return res.apiv3({});
  261. }
  262. catch (error) {
  263. const msg = 'Error occured in discard of slackbotAccessToken';
  264. logger.error('Error', error);
  265. return res.apiv3Err(new ErrorV3(msg, 'discard-slackbotAccessToken-failed'), 500);
  266. }
  267. });
  268. return router;
  269. };