slack-app-integration.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { defaultSupportedSlackEventActions } from '@growi/slack';
  2. import crypto from 'crypto';
  3. import mongoose from 'mongoose';
  4. import { configManager } from '../service/config-manager';
  5. import { getModelSafely } from '../util/mongoose-utils';
  6. const schema = new mongoose.Schema({
  7. tokenGtoP: { type: String, required: true, unique: true },
  8. tokenPtoG: { type: String, required: true, unique: true },
  9. isPrimary: { type: Boolean, unique: true, sparse: true },
  10. permissionsForBroadcastUseCommands: Map,
  11. permissionsForSingleUseCommands: Map,
  12. permissionsForSlackEventActions: {
  13. type: Map,
  14. default: new Map(
  15. defaultSupportedSlackEventActions.map((action) => [action, false]),
  16. ),
  17. },
  18. });
  19. class SlackAppIntegration {
  20. static generateAccessTokens(saltForGtoP, saltForPtoG) {
  21. const now = new Date().getTime();
  22. const hasher1 = crypto.createHash('sha512');
  23. const hasher2 = crypto.createHash('sha512');
  24. const tokenGtoP = hasher1
  25. .update(`gtop-${saltForGtoP}-${now.toString()}`)
  26. .digest('base64');
  27. const tokenPtoG = hasher2
  28. .update(`ptog-${saltForPtoG}-${now.toString()}`)
  29. .digest('base64');
  30. return [tokenGtoP, tokenPtoG];
  31. }
  32. static async generateUniqueAccessTokens() {
  33. let duplicateTokens;
  34. let tokenGtoP;
  35. let tokenPtoG;
  36. let generateTokens;
  37. // get salt strings
  38. const saltForGtoP = configManager.getConfig(
  39. 'slackbot:withProxy:saltForGtoP',
  40. );
  41. const saltForPtoG = configManager.getConfig(
  42. 'slackbot:withProxy:saltForPtoG',
  43. );
  44. do {
  45. generateTokens = SlackAppIntegration.generateAccessTokens(
  46. saltForGtoP,
  47. saltForPtoG,
  48. );
  49. tokenGtoP = generateTokens[0];
  50. tokenPtoG = generateTokens[1];
  51. // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
  52. duplicateTokens = await this.findOne({
  53. $or: [{ tokenGtoP }, { tokenPtoG }],
  54. });
  55. } while (duplicateTokens != null);
  56. return { tokenGtoP, tokenPtoG };
  57. }
  58. }
  59. const factory = (crowi) => {
  60. const modelExists = getModelSafely('SlackAppIntegration');
  61. if (modelExists != null) {
  62. return modelExists;
  63. }
  64. schema.loadClass(SlackAppIntegration);
  65. return mongoose.model('SlackAppIntegration', schema);
  66. };
  67. export default factory;