"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CredentialsIsNotUpdatableError = void 0;
exports.buildSharedForCredential = buildSharedForCredential;
exports.getCredential = getCredential;
exports.getSharedCredentials = getSharedCredentials;
exports.saveCredential = saveCredential;
exports.updateCredential = updateCredential;
exports.removeCredential = removeCredential;
exports.encryptCredential = encryptCredential;
exports.sanitizeCredentials = sanitizeCredentials;
exports.toJsonSchema = toJsonSchema;
const api_types_1 = require("@n8n/api-types");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const n8n_core_1 = require("n8n-core");
const n8n_workflow_1 = require("n8n-workflow");
const credentials_service_1 = require("../../../../credentials/credentials.service");
const validation_1 = require("../../../../credentials/validation");
const event_service_1 = require("../../../../events/event.service");
const external_hooks_1 = require("../../../../external-hooks");
const external_secrets_config_1 = require("../../../../modules/external-secrets.ee/external-secrets.config");
const secret_provider_access_check_service_ee_1 = require("../../../../modules/external-secrets.ee/secret-provider-access-check.service.ee");
class CredentialsIsNotUpdatableError extends n8n_workflow_1.BaseError {
}
exports.CredentialsIsNotUpdatableError = CredentialsIsNotUpdatableError;
function buildSharedForCredential(credential) {
    const shared = credential.shared;
    return shared
        .filter((sh) => typeof sh.project?.id === 'string')
        .map((sh) => ({
        id: sh.project.id,
        name: sh.project.name,
        role: sh.role,
        createdAt: sh.createdAt,
        updatedAt: sh.updatedAt,
    }));
}
async function getCredential(credentialId) {
    return await di_1.Container.get(db_1.CredentialsRepository).findOne({
        where: { id: credentialId },
        relations: ['shared', 'shared.project'],
    });
}
function isProjectScopedExternalSecretsEnabled() {
    return di_1.Container.get(external_secrets_config_1.ExternalSecretsConfig).externalSecretsForProjects;
}
async function getSharedCredentials(userId, credentialId) {
    return await di_1.Container.get(db_1.SharedCredentialsRepository).findOne({
        where: {
            project: { projectRelations: { userId } },
            credentialsId: credentialId,
        },
        relations: ['credentials'],
    });
}
async function saveCredential(payload, user) {
    const { scopes: _scopes, ...credential } = await di_1.Container.get(credentials_service_1.CredentialsService).createUnmanagedCredential({ ...payload, projectId: payload.projectId ?? undefined }, user);
    const project = await di_1.Container.get(db_1.SharedCredentialsRepository).findCredentialOwningProject(credential.id);
    di_1.Container.get(event_service_1.EventService).emit('credentials-created', {
        user,
        credentialType: credential.type,
        credentialId: credential.id,
        publicApi: true,
        projectId: project?.id,
        projectType: project?.type,
        isDynamic: credential.isResolvable ?? false,
    });
    const credentialForApi = {
        id: credential.id,
        name: credential.name,
        type: credential.type,
        isManaged: credential.isManaged,
        isGlobal: credential.isGlobal,
        isResolvable: credential.isResolvable,
        resolvableAllowFallback: credential.resolvableAllowFallback,
        resolverId: credential.resolverId,
        createdAt: credential.createdAt,
        updatedAt: credential.updatedAt,
    };
    const parsed = api_types_1.publicApiCreatedCredentialSchema.safeParse(credentialForApi);
    if (!parsed.success) {
        throw new n8n_workflow_1.UnexpectedError('Credential create response failed validation');
    }
    return Object.assign(new db_1.CredentialsEntity(), parsed.data, { shared: [] });
}
async function updateCredential(existingCredential, user, updateData) {
    if (existingCredential.isManaged) {
        throw new CredentialsIsNotUpdatableError('Managed credentials cannot be updated.');
    }
    const credentialId = existingCredential.id;
    const credentialData = {};
    if (updateData.name !== undefined) {
        credentialData.name = updateData.name;
    }
    if (updateData.type !== undefined) {
        credentialData.type = updateData.type;
    }
    if (updateData.data !== undefined) {
        const credentialsService = di_1.Container.get(credentials_service_1.CredentialsService);
        const decryptedData = credentialsService.decrypt(existingCredential, true);
        const projectOwningCredential = existingCredential.shared?.find((shared) => shared.role === 'credential:owner');
        await (0, validation_1.validateExternalSecretsPermissions)({
            user,
            projectId: projectOwningCredential.project.id,
            dataToSave: updateData.data,
            decryptedExistingData: decryptedData,
        });
        if (isProjectScopedExternalSecretsEnabled() && decryptedData) {
            await (0, validation_1.validateAccessToReferencedSecretProviders)(projectOwningCredential.project.id, updateData.data, di_1.Container.get(secret_provider_access_check_service_ee_1.SecretsProviderAccessCheckService), 'update');
        }
        let dataToEncrypt;
        if (updateData.isPartialData === true) {
            const mergedData = {
                ...decryptedData,
                ...updateData.data,
            };
            dataToEncrypt = credentialsService.unredact(mergedData, decryptedData);
        }
        else {
            dataToEncrypt = updateData.data;
        }
        const newCredential = new db_1.CredentialsEntity();
        Object.assign(newCredential, {
            id: credentialId,
            name: updateData.name ?? existingCredential.name,
            type: updateData.type ?? existingCredential.type,
            data: dataToEncrypt,
        });
        const encryptedData = await encryptCredential(newCredential);
        Object.assign(credentialData, encryptedData);
    }
    if (updateData.isResolvable !== undefined) {
        credentialData.isResolvable = updateData.isResolvable;
    }
    if (updateData.isGlobal !== undefined) {
        credentialData.isGlobal = updateData.isGlobal;
    }
    credentialData.updatedAt = new Date();
    await di_1.Container.get(db_1.CredentialsRepository).update(credentialId, credentialData);
    return (await getCredential(credentialId));
}
async function removeCredential(user, credentials) {
    await di_1.Container.get(external_hooks_1.ExternalHooks).run('credentials.delete', [credentials.id]);
    di_1.Container.get(event_service_1.EventService).emit('credentials-deleted', {
        user,
        credentialType: credentials.type,
        credentialId: credentials.id,
    });
    return await di_1.Container.get(db_1.CredentialsRepository).remove(credentials);
}
async function encryptCredential(credential) {
    const coreCredential = new n8n_core_1.Credentials({ id: null, name: credential.name }, credential.type);
    coreCredential.setData(credential.data);
    return coreCredential.getDataToSave();
}
function sanitizeCredentials(credentials) {
    const argIsArray = Array.isArray(credentials);
    const credentialsList = argIsArray ? credentials : [credentials];
    const sanitizedCredentials = credentialsList.map((credential) => {
        const { data, shared, ...rest } = credential;
        return rest;
    });
    return argIsArray ? sanitizedCredentials : sanitizedCredentials[0];
}
function toJsonSchema(properties) {
    const jsonSchema = {
        additionalProperties: false,
        type: 'object',
        properties: {},
        allOf: [],
        required: [],
    };
    const optionsValues = {};
    const resolveProperties = [];
    properties
        .filter((property) => property.type === 'options')
        .forEach((property) => {
        Object.assign(optionsValues, {
            [property.name]: property.options?.map((option) => option.value),
        });
    });
    let requiredFields = [];
    const propertyRequiredDependencies = {};
    properties.forEach((property) => {
        if (property.required) {
            requiredFields.push(property.name);
        }
        if (property.type === 'options') {
            Object.assign(jsonSchema.properties, {
                [property.name]: {
                    type: 'string',
                    enum: property.options?.map((data) => data.value),
                },
            });
        }
        else {
            Object.assign(jsonSchema.properties, {
                [property.name]: {
                    type: property.type,
                },
            });
        }
        if (property.displayOptions?.show) {
            const dependantName = Object.keys(property.displayOptions?.show)[0] || '';
            const displayOptionsValues = property.displayOptions.show[dependantName];
            let dependantValue = '';
            if (displayOptionsValues &&
                Array.isArray(displayOptionsValues) &&
                displayOptionsValues[0] !== undefined &&
                displayOptionsValues[0] !== null) {
                dependantValue = displayOptionsValues[0];
            }
            const dependencyKey = `${dependantName}:${JSON.stringify(dependantValue)}`;
            if (!resolveProperties.includes(dependencyKey)) {
                let conditionalValue;
                if (typeof dependantValue === 'object' && dependantValue._cnd) {
                    const [key, targetValue] = Object.entries(dependantValue._cnd)[0];
                    if (key === 'eq') {
                        conditionalValue = {
                            const: [targetValue],
                        };
                    }
                    else if (key === 'not') {
                        conditionalValue = {
                            not: {
                                const: [targetValue],
                            },
                        };
                    }
                    else if (key === 'gt') {
                        conditionalValue = {
                            type: 'number',
                            exclusiveMinimum: [targetValue],
                        };
                    }
                    else if (key === 'gte') {
                        conditionalValue = {
                            type: 'number',
                            minimum: [targetValue],
                        };
                    }
                    else if (key === 'lt') {
                        conditionalValue = {
                            type: 'number',
                            exclusiveMaximum: [targetValue],
                        };
                    }
                    else if (key === 'lte') {
                        conditionalValue = {
                            type: 'number',
                            maximum: [targetValue],
                        };
                    }
                    else if (key === 'startsWith') {
                        conditionalValue = {
                            type: 'string',
                            pattern: `^${targetValue}`,
                        };
                    }
                    else if (key === 'endsWith') {
                        conditionalValue = {
                            type: 'string',
                            pattern: `${targetValue}$`,
                        };
                    }
                    else if (key === 'includes') {
                        conditionalValue = {
                            type: 'string',
                            pattern: `${targetValue}`,
                        };
                    }
                    else if (key === 'regex') {
                        conditionalValue = {
                            type: 'string',
                            pattern: `${targetValue}`,
                        };
                    }
                    else {
                        conditionalValue = {
                            enum: [dependantValue],
                        };
                    }
                }
                else {
                    conditionalValue = {
                        enum: [dependantValue],
                    };
                }
                propertyRequiredDependencies[dependencyKey] = {
                    if: {
                        properties: {
                            [dependantName]: conditionalValue,
                        },
                    },
                    then: {
                        allOf: [],
                    },
                    else: {
                        allOf: [],
                    },
                };
                resolveProperties.push(dependencyKey);
            }
            propertyRequiredDependencies[dependencyKey].then?.allOf.push({ required: [property.name] });
            propertyRequiredDependencies[dependencyKey].else?.allOf.push({
                not: { required: [property.name] },
            });
            requiredFields = requiredFields.filter((field) => field !== property.name);
        }
    });
    Object.assign(jsonSchema, { required: requiredFields });
    jsonSchema.allOf = Object.values(propertyRequiredDependencies);
    if (!jsonSchema.allOf.length) {
        delete jsonSchema.allOf;
    }
    return jsonSchema;
}
//# sourceMappingURL=credentials.service.js.map