n8n
Version:
n8n Workflow Automation Tool
151 lines • 7.89 kB
JavaScript
;
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