UNPKG

n8n

Version:

n8n Workflow Automation Tool

175 lines • 8.54 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.PublicApiControllerRegistry = void 0; const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const express_1 = require("express"); const n8n_workflow_1 = require("n8n-workflow"); const bad_request_error_1 = require("../errors/response-errors/bad-request.error"); const event_service_1 = require("../events/event.service"); const check_access_1 = require("../permissions.ee/check-access"); const public_api_error_response_1 = require("../public-api/v1/public-api-error-response"); const auth_strategy_registry_1 = require("../services/auth-strategy.registry"); const last_active_at_service_1 = require("../services/last-active-at.service"); function apiKeyScopesSatisfy(granted, requirement) { if (!granted) return false; if (typeof requirement === 'string') { return granted.includes(requirement); } if ('anyOf' in requirement) { return requirement.anyOf.some((scope) => granted.includes(scope)); } return requirement.allOf.every((scope) => granted.includes(scope)); } let PublicApiControllerRegistry = class PublicApiControllerRegistry { constructor(metadata, authStrategyRegistry, lastActiveAtService, eventService) { this.metadata = metadata; this.authStrategyRegistry = authStrategyRegistry; this.lastActiveAtService = lastActiveAtService; this.eventService = eventService; } activate(router, apiVersion) { for (const controllerClass of this.metadata.controllerClasses) { const metadata = this.metadata.getControllerMetadata(controllerClass); if (!metadata.isPublicApi) continue; this.activateController(router, controllerClass, apiVersion); } } activateController(parent, controllerClass, apiVersion) { const metadata = this.metadata.getControllerMetadata(controllerClass); const controllerRouter = (0, express_1.Router)({ mergeParams: true }); const prefix = metadata.basePath.replace(/\/+/g, '/').replace(/\/$/, '') || '/'; parent.use(prefix === '' ? '/' : prefix, controllerRouter); const controller = di_1.Container.get(controllerClass); const controllerMiddlewares = metadata.middlewares.map((handlerName) => controller[handlerName].bind(controller)); for (const [handlerName, route] of metadata.routes) { const argTypes = Reflect.getMetadata('design:paramtypes', controller, handlerName); const handler = async (req, res) => { const args = [req, res]; for (let index = 0; index < route.args.length; index++) { const arg = route.args[index]; if (!arg) continue; if (arg.type === 'param') { args.push(req.params[arg.key]); } else if (arg.type === 'body' || arg.type === 'query') { const paramType = argTypes[index]; if (paramType && 'safeParse' in paramType) { const output = paramType.safeParse(req[arg.type]); if (output.success) { args.push(output.data); } else { throw new bad_request_error_1.BadRequestError(output.error.errors[0]?.message ?? 'Invalid request'); } } else { throw new n8n_workflow_1.UnexpectedError(`Public API route ${controllerClass.name}.${handlerName} is missing a Zod DTO for @${arg.type}`); } } else { throw new n8n_workflow_1.UnexpectedError(`Unknown arg type: ${String(arg.type)}`); } } const result = await controller[handlerName](...args); if (res.headersSent) return; if (route.responseDto) { res.json(route.responseDto.parse(result)); return; } res.json(result); }; const middlewares = [this.createAuthMiddleware(apiVersion)]; if (route.apiKeyScope) { middlewares.push(this.createApiKeyScopeMiddleware(route.apiKeyScope)); } if (route.accessScope) { middlewares.push(this.createAccessScopeMiddleware(route.accessScope)); } middlewares.push(...controllerMiddlewares, ...(route.middlewares ?? [])); const finalHandler = async (req, res, next) => { try { await handler(req, res); } catch (error) { if (res.headersSent) { next(error); return; } (0, public_api_error_response_1.sendPublicApiErrorResponse)(res, error instanceof Error ? error : new Error(String(error))); } }; controllerRouter[route.method](route.path, ...middlewares, finalHandler); } } createAuthMiddleware(apiVersion) { return async (req, res, next) => { const authenticated = await this.authStrategyRegistry.authenticate(req); if (!authenticated) { res.status(401).json({ message: 'Unauthorized' }); return; } const userId = req.user?.id; if (userId) { this.lastActiveAtService.updateLastActiveIfStale(userId).catch(() => undefined); this.eventService.emit('public-api-invoked', { userId, path: req.path, method: req.method, apiVersion, userAgent: req.headers['user-agent'], }); } next(); }; } createApiKeyScopeMiddleware(requirement) { return (req, res, next) => { const { tokenGrant } = req; if (!tokenGrant || !apiKeyScopesSatisfy(tokenGrant.apiKeyScopes, requirement)) { res.status(403).json({ message: 'Forbidden' }); return; } next(); }; } createAccessScopeMiddleware(accessScope) { return async (req, res, next) => { const authReq = req; if (!authReq.user) { res.status(401).json({ message: 'Unauthorized' }); return; } try { if (!(await (0, check_access_1.userHasScopes)(authReq.user, [accessScope.scope], accessScope.globalOnly, req.params))) { res.status(403).json({ message: 'Forbidden' }); return; } } catch (error) { (0, public_api_error_response_1.sendPublicApiErrorResponse)(res, error instanceof Error ? error : new Error(String(error))); return; } next(); }; } }; exports.PublicApiControllerRegistry = PublicApiControllerRegistry; exports.PublicApiControllerRegistry = PublicApiControllerRegistry = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [decorators_1.ControllerRegistryMetadata, auth_strategy_registry_1.AuthStrategyRegistry, last_active_at_service_1.LastActiveAtService, event_service_1.EventService]) ], PublicApiControllerRegistry); //# sourceMappingURL=public-api-controller.registry.js.map