UNPKG

n8n

Version:

n8n Workflow Automation Tool

151 lines 7.89 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.ExecutionRedactionService = void 0; const backend_common_1 = require("@n8n/backend-common"); const di_1 = require("@n8n/di"); const forbidden_error_1 = require("../../../errors/response-errors/forbidden.error"); const scope_forbidden_error_1 = require("../../../errors/response-errors/scope-forbidden.error"); const event_service_1 = require("../../../events/event.service"); const workflow_finder_service_1 = require("../../../workflows/workflow-finder.service"); const full_item_redaction_strategy_1 = require("./strategies/full-item-redaction.strategy"); const MANUAL_MODES = new Set(['manual']); let ExecutionRedactionService = class ExecutionRedactionService { constructor(logger, licenseState, workflowFinderService, eventService, fullItemRedactionStrategy) { this.logger = logger; this.licenseState = licenseState; this.workflowFinderService = workflowFinderService; this.eventService = eventService; this.fullItemRedactionStrategy = fullItemRedactionStrategy; } async init() { this.logger.debug('Initializing ExecutionRedactionService...'); } async processExecution(execution, options) { const executions = [execution]; await this.processExecutions(executions, options); return executions[0]; } async processExecutions(executions, options) { if (executions.length === 0) return; const needsCheck = executions.filter((e) => !this.policyAllowsReveal(e)); let revealableIds = new Set(); if (needsCheck.length > 0) { const uniqueWorkflowIds = [...new Set(needsCheck.map((e) => e.workflowId))]; revealableIds = await this.workflowFinderService.findWorkflowIdsWithScopeForUser(uniqueWorkflowIds, options.user, ['execution:reveal']); } if (options.redactExecutionData === false) { for (const execution of executions) { if (this.hasDynamicCredentials(execution)) { throw new forbidden_error_1.ForbiddenError(); } } for (const execution of needsCheck) { if (!revealableIds.has(execution.workflowId)) { this.eventService.emit('execution-data-reveal-failure', { user: options.user, executionId: execution.id ?? '', workflowId: execution.workflowId, ipAddress: options.ipAddress ?? '', userAgent: options.userAgent ?? '', redactionPolicy: this.resolvePolicy(execution), rejectionReason: 'User lacks execution:reveal scope for this workflow', }); throw new scope_forbidden_error_1.ScopeForbiddenError("You do not have permission to reveal execution data. The 'execution:reveal' scope is required.", { errorCode: 'EXECUTION_REVEAL_FORBIDDEN', requiredScope: 'execution:reveal' }, 'Contact a project admin to request the required scope.'); } } } for (let i = 0; i < executions.length; i++) { const execution = executions[i]; const hasDynCreds = this.hasDynamicCredentials(execution); const policyAllowsReveal = this.policyAllowsReveal(execution); const userCanReveal = hasDynCreds ? false : policyAllowsReveal || revealableIds.has(execution.workflowId); const context = { user: options.user, redactExecutionData: options.redactExecutionData, userCanReveal, hasDynamicCredentials: hasDynCreds, memo: new Map(), }; const pipeline = this.buildPipeline(execution, context, policyAllowsReveal, hasDynCreds); let target = execution; if (options.keepOriginal) { const needsClone = pipeline.some((s) => s.requiresRedaction(execution, context)); if (!needsClone) continue; target = structuredClone(execution); executions[i] = target; } for (const strategy of pipeline) { await strategy.apply(target, context); } if (hasDynCreds && target.data.executionData?.runtimeData) { delete target.data.executionData.runtimeData.credentials; } } if (options.redactExecutionData === false) { for (const execution of executions) { this.eventService.emit('execution-data-revealed', { user: options.user, executionId: execution.id ?? '', workflowId: execution.workflowId, ipAddress: options.ipAddress ?? '', userAgent: options.userAgent ?? '', redactionPolicy: this.resolvePolicy(execution), }); } } } buildPipeline(execution, context, policyAllowsReveal, hasDynamicCredentials) { const pipeline = []; const policy = this.resolvePolicy(execution); const shouldClearItems = context.redactExecutionData !== false && (context.redactExecutionData === true || hasDynamicCredentials || (!policyAllowsReveal && (policy === 'all' || (policy === 'non-manual' && !MANUAL_MODES.has(execution.mode)) || (policy === 'manual-only' && MANUAL_MODES.has(execution.mode))))); if (shouldClearItems) { pipeline.push(this.fullItemRedactionStrategy); } return pipeline; } hasDynamicCredentials(execution) { return Object.values(execution.data.resultData?.runData ?? {}).some((taskDataList) => taskDataList.some((taskData) => taskData.usedDynamicCredentials)); } policyAllowsReveal(execution) { const policy = this.resolvePolicy(execution); return (policy === 'none' || (policy === 'non-manual' && MANUAL_MODES.has(execution.mode)) || (policy === 'manual-only' && !MANUAL_MODES.has(execution.mode))); } resolvePolicy(execution) { if (!this.licenseState.isDataRedactionLicensed()) return 'none'; return (execution.data.executionData?.runtimeData?.redaction?.policy ?? execution.workflowData.settings?.redactionPolicy ?? 'none'); } }; exports.ExecutionRedactionService = ExecutionRedactionService; exports.ExecutionRedactionService = ExecutionRedactionService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, backend_common_1.LicenseState, workflow_finder_service_1.WorkflowFinderService, event_service_1.EventService, full_item_redaction_strategy_1.FullItemRedactionStrategy]) ], ExecutionRedactionService); //# sourceMappingURL=execution-redaction.service.js.map