"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 node_defined_field_redaction_strategy_1 = require("./strategies/node-defined-field-redaction.strategy");
const MANUAL_MODES = new Set(['manual']);
let ExecutionRedactionService = class ExecutionRedactionService {
    constructor(logger, licenseState, workflowFinderService, eventService, fullItemRedactionStrategy, nodeDefinedFieldRedactionStrategy) {
        this.logger = logger;
        this.licenseState = licenseState;
        this.workflowFinderService = workflowFinderService;
        this.eventService = eventService;
        this.fullItemRedactionStrategy = fullItemRedactionStrategy;
        this.nodeDefinedFieldRedactionStrategy = nodeDefinedFieldRedactionStrategy;
    }
    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);
        }
        pipeline.push(this.nodeDefinedFieldRedactionStrategy);
        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,
        node_defined_field_redaction_strategy_1.NodeDefinedFieldRedactionStrategy])
], ExecutionRedactionService);
//# sourceMappingURL=execution-redaction.service.js.map