UNPKG

n8n

Version:

n8n Workflow Automation Tool

323 lines • 13.7 kB
"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 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"); const credentials_mapper_1 = require("./credentials.mapper"); class CredentialsIsNotUpdatableError extends n8n_workflow_1.BaseError { } exports.CredentialsIsNotUpdatableError = CredentialsIsNotUpdatableError; function isNodePropertyOptions(options) { return (Array.isArray(options) && options.every((item) => typeof item === 'object' && item !== null && 'value' in item && 'name' in item)); } 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, }; return (0, credentials_mapper_1.toPublicApiCredentialResponse)(credentialForApi); } 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 = await 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); await 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]: isNodePropertyOptions(property.options) ? property.options.map((option) => option.value) : undefined, }); }); 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: isNodePropertyOptions(property.options) ? property.options.map((data) => data.value) : undefined, }, }); } 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