UNPKG

n8n

Version:

n8n Workflow Automation Tool

150 lines • 7.37 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SlackManualSetupService = void 0; const node_crypto_1 = require("node:crypto"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const is_record_1 = require("@n8n/utils/is-record"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const bad_request_error_1 = require("../../../../../errors/response-errors/bad-request.error"); const not_found_error_1 = require("../../../../../errors/response-errors/not-found.error"); const cache_service_1 = require("../../../../../services/cache/cache.service"); const slack_methods_service_1 = require("./slack-methods.service"); const SLACK_APP_SETUP_CACHE_PREFIX = 'agents:slack-app-setup:'; const SLACK_APP_SETUP_TTL_MS = 60 * 60 * 1000; function hasSessionShape(value) { const keys = [ 'projectId', 'agentId', 'userId', 'appId', 'clientId', 'clientSecret', 'signingSecret', 'redirectUrl', ]; return (0, is_record_1.isRecord)(value) && keys.every((key) => typeof value[key] === 'string'); } let SlackManualSetupService = class SlackManualSetupService { constructor(methods, userRepository, cacheService, cipher) { this.methods = methods; this.userRepository = userRepository; this.cacheService = cacheService; this.cipher = cipher; } async createApp(options) { const appConfigurationToken = options.appConfigurationToken.trim(); if (!appConfigurationToken) { throw new bad_request_error_1.BadRequestError('Slack app configuration token is required'); } const agent = await this.methods.getAgent(options.agentId, options.projectId); const redirectUrl = this.methods.callbackUrl(options.projectId, options.agentId); const manifest = this.methods.buildManifest(agent.name, options.projectId, options.agentId, { redirectUrl, }); const response = await this.methods.callSlackApi('apps.manifest.create', { token: appConfigurationToken, manifest: JSON.stringify(manifest), }); if (response.ok !== true) { throw this.methods.slackError('create the Slack app', response); } const credentials = this.methods.childRecord(response, 'credentials'); const appId = this.methods.stringProperty(response, 'app_id'); const clientId = this.methods.stringProperty(credentials, 'client_id'); const clientSecret = this.methods.stringProperty(credentials, 'client_secret'); const signingSecret = this.methods.stringProperty(credentials, 'signing_secret'); const oauthAuthorizeUrl = this.methods.stringProperty(response, 'oauth_authorize_url'); if (!appId || !clientId || !clientSecret || !signingSecret || !oauthAuthorizeUrl) { throw new bad_request_error_1.BadRequestError('Slack returned an incomplete app setup response'); } const state = (0, node_crypto_1.randomBytes)(32).toString('hex'); const setupSession = { projectId: options.projectId, agentId: options.agentId, userId: options.user.id, appId, clientId, clientSecret, signingSecret, redirectUrl, }; await this.cacheService.set(this.cacheKey(state), await this.cipher.encryptV2(JSON.stringify(setupSession)), SLACK_APP_SETUP_TTL_MS); return { appId, installUrl: this.methods.installUrl(oauthAuthorizeUrl, state, redirectUrl), }; } async getManifest(options) { const agent = await this.methods.getAgent(options.agentId, options.projectId); return { manifest: this.methods.buildManifest(agent.name, options.projectId, options.agentId), }; } async completeInstall(options) { const session = await this.consumeSession(options.state); if (session.projectId !== options.projectId || session.agentId !== options.agentId) { throw new bad_request_error_1.BadRequestError('Slack app setup state does not match this agent'); } const user = await this.userRepository.findOne({ where: { id: session.userId }, relations: ['role'], }); if (!user) throw new not_found_error_1.NotFoundError(`User "${session.userId}" not found`); const agent = await this.methods.getAgent(session.agentId, session.projectId); const tokenResponse = await this.methods.callSlackApi('oauth.v2.access', { code: options.code, redirect_uri: session.redirectUrl, }, { authorization: `Basic ${Buffer.from(`${session.clientId}:${session.clientSecret}`).toString('base64')}`, }); if (tokenResponse.ok !== true) { throw this.methods.slackError('finish Slack app installation', tokenResponse); } const accessToken = this.methods.stringProperty(tokenResponse, 'access_token'); if (!accessToken?.startsWith('xoxb-')) { throw new bad_request_error_1.BadRequestError('Slack did not return a Bot User OAuth Token'); } await this.methods.createAndConnectBotCredential({ agent, user, accessToken, signingSecret: session.signingSecret, }); } async consumeSession(state) { const cached = await this.cacheService.take(this.cacheKey(state)); if (typeof cached !== 'string') { throw new bad_request_error_1.BadRequestError('Slack app setup state has expired or is invalid'); } try { const decrypted = await this.cipher.decryptV2(cached); const session = (0, n8n_workflow_1.jsonParse)(decrypted, { fallbackValue: null }); if (hasSessionShape(session)) return session; } catch { } throw new bad_request_error_1.BadRequestError('Slack app setup state has expired or is invalid'); } cacheKey(state) { return `${SLACK_APP_SETUP_CACHE_PREFIX}${state}`; } }; exports.SlackManualSetupService = SlackManualSetupService; exports.SlackManualSetupService = SlackManualSetupService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [slack_methods_service_1.SlackMethodsService, db_1.UserRepository, cache_service_1.CacheService, n8n_core_1.Cipher]) ], SlackManualSetupService); //# sourceMappingURL=slack-manual-setup.service.js.map