UNPKG

n8n

Version:

n8n Workflow Automation Tool

783 lines 37.7 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.CredentialsService = void 0; const backend_common_1 = require("@n8n/backend-common"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const permissions_1 = require("@n8n/permissions"); const typeorm_1 = require("@n8n/typeorm"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const credential_types_1 = require("../credential-types"); const credentials_helper_1 = require("../credentials-helper"); const bad_request_error_1 = require("../errors/response-errors/bad-request.error"); const credential_not_found_error_1 = require("../errors/credential-not-found.error"); const forbidden_error_1 = require("../errors/response-errors/forbidden.error"); const not_found_error_1 = require("../errors/response-errors/not-found.error"); const external_hooks_1 = require("../external-hooks"); const generic_helpers_1 = require("../generic-helpers"); 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 validate_oauth_url_1 = require("../oauth/validate-oauth-url"); const check_access_1 = require("../permissions.ee/check-access"); const credentials_tester_service_1 = require("../services/credentials-tester.service"); const ownership_service_1 = require("../services/ownership.service"); const project_service_ee_1 = require("../services/project.service.ee"); const role_service_1 = require("../services/role.service"); const credential_dependency_service_1 = require("./credential-dependency.service"); const credentials_finder_service_1 = require("./credentials-finder.service"); const validation_1 = require("./validation"); const CUSTOM_AUTH_JSON_REDACTED_VALUE = '***'; let CredentialsService = class CredentialsService { constructor(credentialsRepository, credentialDependencyService, sharedCredentialsRepository, ownershipService, logger, errorReporter, credentialsTester, externalHooks, credentialTypes, projectRepository, projectService, roleService, userRepository, credentialsFinderService, credentialsHelper, externalSecretsConfig, externalSecretsProviderAccessCheckService) { this.credentialsRepository = credentialsRepository; this.credentialDependencyService = credentialDependencyService; this.sharedCredentialsRepository = sharedCredentialsRepository; this.ownershipService = ownershipService; this.logger = logger; this.errorReporter = errorReporter; this.credentialsTester = credentialsTester; this.externalHooks = externalHooks; this.credentialTypes = credentialTypes; this.projectRepository = projectRepository; this.projectService = projectService; this.roleService = roleService; this.userRepository = userRepository; this.credentialsFinderService = credentialsFinderService; this.credentialsHelper = credentialsHelper; this.externalSecretsConfig = externalSecretsConfig; this.externalSecretsProviderAccessCheckService = externalSecretsProviderAccessCheckService; } async addGlobalCredentials(credentials, includeData, dependencyFilter, type) { const globalCredentials = await this.credentialsRepository.findAllGlobalCredentials({ includeData, ...(type ? { type } : {}), filters: { dependency: dependencyFilter }, }); const credentialIds = new Set(credentials.map((c) => c.id)); const newGlobalCreds = globalCredentials.filter((gc) => !credentialIds.has(gc.id)); return [...credentials, ...newGlobalCreds]; } extractTypeFilter(listQueryOptions) { const filterType = listQueryOptions.filter?.type; return typeof filterType === 'string' && filterType !== '' ? filterType : undefined; } async getMany(user, { listQueryOptions = {}, includeScopes = false, includeData = false, onlySharedWithMe = false, includeGlobal = false, filters = {}, } = {}) { const { externalSecretsStore } = filters; const returnAll = (0, permissions_1.hasGlobalScope)(user, 'credential:list'); const isDefaultSelect = !listQueryOptions.select; const dependencyFilter = externalSecretsStore ? await this.credentialDependencyService.resolveExternalSecretsStoreDependencyFilter(externalSecretsStore) : undefined; if (externalSecretsStore && !dependencyFilter) { return []; } if (includeData) { includeScopes = true; } let credentials; if (returnAll) { credentials = await this.getManyForAdminUser(user, { listQueryOptions, includeGlobal, includeData, onlySharedWithMe, filters: { dependency: dependencyFilter }, }); } else { credentials = await this.getManyForMemberUser(user, { listQueryOptions, includeGlobal, includeData, onlySharedWithMe, filters: { dependency: dependencyFilter }, }); } return await this.enrichCredentials(credentials, user, isDefaultSelect, includeScopes, includeData, listQueryOptions, onlySharedWithMe); } async getManyForAdminUser(user, { listQueryOptions, includeGlobal, includeData, onlySharedWithMe, filters, }) { const { dependency: dependencyFilter } = filters ?? {}; const typeFilter = this.extractTypeFilter(listQueryOptions); if (onlySharedWithMe || dependencyFilter) { const sharingOptions = { ...(onlySharedWithMe ? { onlySharedWithMe: true } : {}), }; const { credentials } = await this.credentialsRepository.getManyAndCountWithSharingSubquery(user, sharingOptions, { ...listQueryOptions, ...(includeData ? { includeData: true } : {}), filters: { dependency: dependencyFilter, }, }); if (includeGlobal) { return await this.addGlobalCredentials(credentials, includeData, dependencyFilter, typeFilter); } return credentials; } await this.applyPersonalProjectFilter(listQueryOptions); let credentials = await this.credentialsRepository.findMany({ ...listQueryOptions, ...(includeData ? { includeData: true } : {}), }); if (includeGlobal) { credentials = await this.addGlobalCredentials(credentials, includeData, dependencyFilter, typeFilter); } return credentials; } async getManyForMemberUser(user, { listQueryOptions, includeGlobal, includeData, onlySharedWithMe, filters, }) { const { dependency: dependencyFilter } = filters ?? {}; const typeFilter = this.extractTypeFilter(listQueryOptions); let isPersonalProject = false; let personalProjectOwnerId = null; if (listQueryOptions.filter?.projectId) { const project = await this.projectRepository.findOneBy({ id: listQueryOptions.filter.projectId, }); if (!project) { return []; } isPersonalProject = project.type === 'personal'; personalProjectOwnerId = project.creatorId; } const sharingOptions = {}; if (isPersonalProject && personalProjectOwnerId) { if (personalProjectOwnerId !== user.id && !(0, permissions_1.hasGlobalScope)(user, 'credential:read')) { return []; } sharingOptions.isPersonalProject = true; sharingOptions.personalProjectOwnerId = personalProjectOwnerId; } else if (onlySharedWithMe) { sharingOptions.onlySharedWithMe = true; } else { const projectRoles = await this.roleService.rolesWithScope('project', ['credential:read']); const credentialRoles = await this.roleService.rolesWithScope('credential', [ 'credential:read', ]); sharingOptions.scopes = ['credential:read']; sharingOptions.projectRoles = projectRoles; sharingOptions.credentialRoles = credentialRoles; } const { credentials } = await this.credentialsRepository.getManyAndCountWithSharingSubquery(user, sharingOptions, { ...listQueryOptions, ...(includeData ? { includeData: true } : {}), filters: { dependency: dependencyFilter, }, }); if (includeGlobal) { return await this.addGlobalCredentials(credentials, includeData, dependencyFilter, typeFilter); } return credentials; } async applyPersonalProjectFilter(listQueryOptions) { const projectId = typeof listQueryOptions.filter?.projectId === 'string' ? listQueryOptions.filter.projectId : undefined; if (!projectId) { return; } let project; try { project = await this.projectService.getProject(projectId); } catch { } if (project?.type === 'personal') { listQueryOptions.filter = { ...listQueryOptions.filter, withRole: 'credential:owner', }; } } async enrichCredentials(credentials, user, isDefaultSelect, includeScopes, includeData, listQueryOptions, onlySharedWithMe) { if (isDefaultSelect) { credentials = await this.populateSharedRelations(credentials, listQueryOptions, onlySharedWithMe); } if (includeScopes) { credentials = await this.addScopesToCredentials(credentials, user); } if (includeData) { return await this.addDecryptedDataToCredentials(credentials); } return credentials; } async populateSharedRelations(credentials, listQueryOptions, onlySharedWithMe) { const needsRelations = listQueryOptions.filter?.shared && typeof listQueryOptions.filter.shared === 'object' && 'projectId' in listQueryOptions.filter.shared ? listQueryOptions.filter.shared.projectId : onlySharedWithMe; if (needsRelations) { const relations = await this.sharedCredentialsRepository.getAllRelationsForCredentials(credentials.map((c) => c.id)); credentials.forEach((c) => { c.shared = relations.filter((r) => r.credentialsId === c.id); }); } return credentials.map((c) => this.ownershipService.addOwnedByAndSharedWith(c)); } async addScopesToCredentials(credentials, user) { const projectRelations = await this.projectService.getProjectRelationsForUser(user); return credentials.map((c) => this.roleService.addScopes(c, user, projectRelations)); } async addDecryptedDataToCredentials(credentials) { return await Promise.all(credentials.map(async (c) => { const data = c.scopes.includes('credential:update') ? await this.decrypt(c) : undefined; if (data?.oauthTokenData) { data.oauthTokenData = true; } return { ...c, data, }; })); } async getCredentialsAUserCanUseInAWorkflow(user, options) { const projectRelations = await this.projectService.getProjectRelationsForUser(user); const allCredentials = await this.credentialsFinderService.findCredentialsForUser(user, [ 'credential:read', ]); const allCredentialsForWorkflow = 'workflowId' in options ? (await this.findAllCredentialIdsForWorkflow(options.workflowId)).map((c) => c.id) : (await this.findAllCredentialIdsForProject(options.projectId)).map((c) => c.id); const intersection = allCredentials.filter((c) => allCredentialsForWorkflow.includes(c.id) || c.isGlobal); return intersection .map((c) => this.roleService.addScopes(c, user, projectRelations)) .map((c) => ({ id: c.id, name: c.name, type: c.type, scopes: c.scopes, isManaged: c.isManaged, isGlobal: c.isGlobal, isResolvable: c.isResolvable, })); } async findAllGlobalCredentialIds(includeData = false) { const globalCredentials = await this.credentialsRepository.findAllGlobalCredentials({ includeData, }); return globalCredentials; } async findAllCredentialIdsForWorkflow(workflowId) { const user = await this.userRepository.findPersonalOwnerForWorkflow(workflowId); if (user && (0, permissions_1.hasGlobalScope)(user, 'credential:read')) { return await this.credentialsRepository.findAllPersonalCredentials(); } return await this.credentialsRepository.findAllCredentialsForWorkflow(workflowId); } async findAllCredentialIdsForProject(projectId) { const user = await this.userRepository.findPersonalOwnerForProject(projectId); if (user && (0, permissions_1.hasGlobalScope)(user, 'credential:read')) { return await this.credentialsRepository.findAllPersonalCredentials(); } return await this.credentialsRepository.findAllCredentialsForProject(projectId); } async getSharing(user, credentialId, globalScopes, relations = { credentials: true }) { let where = { credentialsId: credentialId }; if (!(0, permissions_1.hasGlobalScope)(user, globalScopes, { mode: 'allOf' })) { where = { ...where, role: 'credential:owner', project: { projectRelations: { role: { slug: permissions_1.PROJECT_OWNER_ROLE_SLUG }, userId: user.id, }, }, }; } return await this.sharedCredentialsRepository.findOne({ where, relations, }); } async prepareUpdateData(user, data, existingCredential) { const decryptedData = await this.decrypt(existingCredential, true); const projectOwningCredential = existingCredential.shared?.find((shared) => shared.role === 'credential:owner'); await (0, validation_1.validateExternalSecretsPermissions)({ user, projectId: projectOwningCredential.projectId, dataToSave: data.data, decryptedExistingData: decryptedData, }); if (this.externalSecretsConfig.externalSecretsForProjects && data.data) { await (0, validation_1.validateAccessToReferencedSecretProviders)(projectOwningCredential.projectId, data.data, this.externalSecretsProviderAccessCheckService, 'update'); } const mergedData = (0, n8n_workflow_1.deepCopy)(data); if (mergedData.data) { mergedData.data = this.unredact(mergedData.data, decryptedData, this.getCredentialTypeProperties(existingCredential.type)); } const updateData = this.credentialsRepository.create(mergedData); await (0, generic_helpers_1.validateEntity)(updateData); if (decryptedData.oauthTokenData) { updateData.data.oauthTokenData = decryptedData.oauthTokenData; } this.validateOAuthCredentialUrls(updateData.type, updateData.data); return updateData; } async createEncryptedData(credential) { const credentials = new n8n_core_1.Credentials({ id: credential.id, name: credential.name }, credential.type); await credentials.setData(credential.data); const newCredentialData = credentials.getDataToSave(); newCredentialData.updatedAt = new Date(); return newCredentialData; } async decrypt(credential, includeRawData = false) { const coreCredential = (0, credentials_helper_1.createCredentialsFromCredentialsEntity)(credential); try { const data = await coreCredential.getData(); if (includeRawData) { return data; } return this.redact(data, credential); } catch (error) { if (error instanceof n8n_core_1.CredentialDataError) { this.errorReporter.error(error, { level: 'error', extra: { credentialId: credential.id }, tags: { credentialType: credential.type }, }); return {}; } throw error; } } async update(credentialId, newCredentialData, decryptedCredentialData) { await this.externalHooks.run('credentials.update', [newCredentialData]); return await this.credentialsRepository.manager.transaction(async (transactionManager) => { await transactionManager.update(db_1.CredentialsEntity, credentialId, newCredentialData); if (decryptedCredentialData) { await this.credentialDependencyService.syncExternalSecretProviderDependenciesForCredential({ credentialId, decryptedCredentialData, entityManager: transactionManager, }); } return await transactionManager.findOneBy(db_1.CredentialsEntity, { id: credentialId }); }); } async resolveOwningProjectIdForNewCredential(user, projectId, entityManager) { if (projectId !== undefined) { return projectId; } const personalProject = await this.projectRepository.getPersonalProjectForUserOrFail(user.id, entityManager); return personalProject.id; } async save(credential, encryptedData, user, projectId, decryptedCredentialData) { const newCredential = new db_1.CredentialsEntity(); Object.assign(newCredential, credential, encryptedData); await this.externalHooks.run('credentials.create', [encryptedData]); const { manager: dbManager } = this.credentialsRepository; const result = await dbManager.transaction(async (transactionManager) => { const project = await this.projectService.getProjectWithScope(user, projectId, ['credential:create'], transactionManager); if (project === null) { if (!(await transactionManager.existsBy(db_1.Project, { id: projectId }))) { throw new not_found_error_1.NotFoundError('Project not found'); } throw new forbidden_error_1.ForbiddenError("You don't have the permissions to save the credential in this project."); } const savedCredential = await transactionManager.save(newCredential); savedCredential.data = newCredential.data; const newSharedCredential = this.sharedCredentialsRepository.create({ role: 'credential:owner', credentials: savedCredential, projectId: project.id, }); await transactionManager.save(newSharedCredential); if (decryptedCredentialData) { await this.credentialDependencyService.upsertExternalSecretProviderDependenciesForCredential({ credentialId: savedCredential.id, decryptedCredentialData, entityManager: transactionManager, }); } return savedCredential; }); this.logger.debug('New credential created', { credentialId: newCredential.id, ownerId: user.id, }); return result; } async delete(user, credentialId) { await this.externalHooks.run('credentials.delete', [credentialId]); const credential = await this.credentialsFinderService.findCredentialForUser(credentialId, user, ['credential:delete']); if (!credential) { return; } await this.credentialsRepository.remove(credential); } async test(userId, credentials) { return await this.credentialsTester.testCredentials(userId, credentials.type, credentials); } async testById(userId, credentialId) { const storedCredential = await this.credentialsFinderService.findCredentialById(credentialId); if (!storedCredential) { throw new credential_not_found_error_1.CredentialNotFoundError(credentialId); } const credentials = await this.prepareCredentialsForTest({ storedCredential }); return await this.test(userId, credentials); } async testWithCredentials(user, credentials) { const storedCredential = await this.credentialsFinderService.findCredentialForUser(credentials.id, user, ['credential:read']); if (!storedCredential) { throw new credential_not_found_error_1.CredentialNotFoundError(credentials.id); } const mergedCredentials = await this.prepareCredentialsForTest({ storedCredential, user, credentialsToTest: credentials, }); return await this.test(user.id, mergedCredentials); } redact(data, credential) { const copiedData = (0, n8n_workflow_1.deepCopy)(data); let credType; try { credType = this.credentialTypes.getByName(credential.type); } catch { return data; } const getExtendedProps = (type) => { const props = []; for (const e of type.extends ?? []) { const extendsType = this.credentialTypes.getByName(e); const extendedProps = getExtendedProps(extendsType); n8n_workflow_1.NodeHelpers.mergeNodeProperties(props, extendedProps); } n8n_workflow_1.NodeHelpers.mergeNodeProperties(props, type.properties); return props; }; const properties = getExtendedProps(credType); return this.redactValues(copiedData, properties); } redactValues(data, props) { for (const dataKey of Object.keys(data)) { if (dataKey === 'oauthTokenData' || dataKey === 'csrfSecret') { if (data[dataKey].toString().length > 0) { data[dataKey] = n8n_workflow_1.CREDENTIAL_BLANKING_VALUE; } else { data[dataKey] = n8n_workflow_1.CREDENTIAL_EMPTY_VALUE; } continue; } const prop = props.find((v) => v.name === dataKey); if (!prop) { continue; } if (prop.type === 'fixedCollection' && prop.options?.length) { const dataObject = data[dataKey]; for (const option of prop.options) { if ((0, n8n_workflow_1.isINodePropertyCollection)(option)) { this.redactCollectionOption(dataObject, option); } } } if (prop.typeOptions?.password && (!data[dataKey].toString().startsWith('={{') || prop.noDataExpression)) { if (data[dataKey].toString().length > 0) { data[dataKey] = n8n_workflow_1.CREDENTIAL_BLANKING_VALUE; } else { data[dataKey] = n8n_workflow_1.CREDENTIAL_EMPTY_VALUE; } continue; } if (prop.typeOptions?.redactJsonLeaves) { const jsonStr = String(data[dataKey] ?? ''); if (!jsonStr) { data[dataKey] = n8n_workflow_1.CREDENTIAL_EMPTY_VALUE; } else { try { const parsed = (0, n8n_workflow_1.jsonParse)(jsonStr); data[dataKey] = JSON.stringify(this.redactJsonLeaves(parsed), null, 2); } catch { } } } } return data; } redactCollectionOption(data, option) { const collectionValuesKey = option.name; const values = data?.[collectionValuesKey]; if (Array.isArray(values)) { for (let i = 0; i < values.length; i++) { values[i] = this.redactValues(values[i], option.values); } } else if (typeof values === 'object' && values !== null) { data[collectionValuesKey] = this.redactValues(values, option.values); } } redactJsonLeaves(obj) { if (Array.isArray(obj)) return obj.map((item) => this.redactJsonLeaves(item)); if (typeof obj === 'object' && obj !== null) { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [ k, this.redactJsonLeaves(v), ])); } return CUSTOM_AUTH_JSON_REDACTED_VALUE; } mergeRedactedJsonLeaves(newVal, savedVal) { if (newVal === CUSTOM_AUTH_JSON_REDACTED_VALUE) return savedVal; if (Array.isArray(newVal) && Array.isArray(savedVal)) { return newVal.map((item, i) => this.mergeRedactedJsonLeaves(item, savedVal[i])); } if (Array.isArray(newVal) || Array.isArray(savedVal)) { return newVal; } if (typeof newVal === 'object' && newVal !== null && typeof savedVal === 'object' && savedVal !== null) { return Object.fromEntries(Object.entries(newVal).map(([k, v]) => [ k, this.mergeRedactedJsonLeaves(v, savedVal[k]), ])); } return newVal; } unredactRestoreValues(unmerged, replacement) { for (const [key, value] of Object.entries(unmerged)) { if (value === n8n_workflow_1.CREDENTIAL_BLANKING_VALUE || value === n8n_workflow_1.CREDENTIAL_EMPTY_VALUE) { unmerged[key] = replacement[key]; } else if (typeof value === 'object' && value !== null && key in replacement && typeof replacement[key] === 'object' && replacement[key] !== null) { this.unredactRestoreValues(value, replacement[key]); } } } getCredentialTypeProperties(credentialType) { try { return this.credentialTypes.getByName(credentialType).properties; } catch { return []; } } unredact(redactedData, savedData, props = []) { const mergedData = (0, n8n_workflow_1.deepCopy)(redactedData); this.unredactRestoreValues(mergedData, savedData); for (const prop of props) { if (!prop.typeOptions?.redactJsonLeaves) continue; if (!(prop.name in mergedData) || !(prop.name in savedData)) continue; try { const newJson = (0, n8n_workflow_1.jsonParse)(String(mergedData[prop.name])); const savedJson = (0, n8n_workflow_1.jsonParse)(String(savedData[prop.name])); mergedData[prop.name] = (0, n8n_workflow_1.jsonStringify)(this.mergeRedactedJsonLeaves(newJson, savedJson)); } catch { } } return mergedData; } async getOne(user, credentialId, includeDecryptedData) { let sharing = null; let decryptedData = null; sharing = includeDecryptedData ? await this.getSharing(user, credentialId, [ 'credential:read', ]) : null; if (sharing) { decryptedData = await this.decrypt(sharing.credentials); } else { sharing = await this.getSharing(user, credentialId, ['credential:read']); } if (!sharing) { throw new not_found_error_1.NotFoundError(`Credential with ID "${credentialId}" could not be found.`); } const { credentials: credential } = sharing; const { data: _, ...rest } = credential; if (decryptedData) { if (decryptedData?.oauthTokenData) { decryptedData.oauthTokenData = true; } return { data: decryptedData, ...rest }; } return { ...rest }; } async getCredentialScopes(user, credentialId) { const userProjectRelations = await this.projectService.getProjectRelationsForUser(user); const projectIds = [...new Set(userProjectRelations.map((pr) => pr.projectId))]; if (projectIds.length === 0) { return this.roleService.combineResourceScopes('credential', user, [], userProjectRelations); } const shared = await this.sharedCredentialsRepository.find({ where: { projectId: (0, typeorm_1.In)(projectIds), credentialsId: credentialId, }, }); return this.roleService.combineResourceScopes('credential', user, shared, userProjectRelations); } async transferAll(fromProjectId, toProjectId, trx) { trx = trx ?? this.credentialsRepository.manager; const allSharedCredentials = await trx.findBy(db_1.SharedCredentials, { projectId: (0, typeorm_1.In)([fromProjectId, toProjectId]), }); const sharedCredentialsOfFromProject = allSharedCredentials.filter((sc) => sc.projectId === fromProjectId); const ownedCredentialIds = sharedCredentialsOfFromProject .filter((sc) => sc.role === 'credential:owner') .map((sc) => sc.credentialsId); await this.sharedCredentialsRepository.makeOwner(ownedCredentialIds, toProjectId, trx); await this.sharedCredentialsRepository.deleteByIds(ownedCredentialIds, fromProjectId, trx); const sharedCredentialIdsOfTransferee = allSharedCredentials .filter((sc) => sc.projectId === toProjectId) .map((sc) => sc.credentialsId); const sharedCredentialsToTransfer = sharedCredentialsOfFromProject.filter((sc) => sc.role !== 'credential:owner' && !sharedCredentialIdsOfTransferee.includes(sc.credentialsId)); await trx.insert(db_1.SharedCredentials, sharedCredentialsToTransfer.map((sc) => ({ credentialsId: sc.credentialsId, projectId: toProjectId, role: sc.role, }))); } async replaceCredentialContentsForSharee(user, credential, decryptedData, mergedCredentials) { if (!(await (0, check_access_1.userHasScopes)(user, ['credential:update'], false, { credentialId: credential.id }))) { mergedCredentials.data = decryptedData; } } async createUnmanagedCredential(dto, user) { return await this.createCredential({ ...dto, isManaged: false }, user); } async checkCredentialData(type, data, user, projectId) { const credentialProperties = this.credentialsHelper.getCredentialsProperties(type); for (const property of credentialProperties) { if (property.required && property.displayOptions !== undefined && (0, n8n_workflow_1.displayParameter)(data, property, null, null)) { const value = data[property.name]; const hasDefault = property.default !== undefined && property.default !== null && property.default !== ''; if ((value === undefined || value === null || value === '') && !hasDefault) { throw new bad_request_error_1.BadRequestError(`The field "${property.name}" is mandatory for credentials of type "${type}"`); } } } await (0, validation_1.validateExternalSecretsPermissions)({ user, projectId, dataToSave: data }); this.validateOAuthCredentialUrls(type, data); } validateOAuthCredentialUrls(type, data) { const parentTypes = this.credentialTypes.getParentTypes(type) ?? []; const isOAuth2 = type === 'oAuth2Api' || parentTypes.includes('oAuth2Api'); const isOAuth1 = type === 'oAuth1Api' || parentTypes.includes('oAuth1Api'); if (isOAuth2) { const oauthUrlFields = ['authUrl', 'accessTokenUrl', 'serverUrl']; for (const field of oauthUrlFields) { const value = data[field]; if (typeof value === 'string' && value.trim() !== '' && !(0, n8n_workflow_1.isExpression)(value)) { (0, validate_oauth_url_1.validateOAuthUrl)(value); } } } if (isOAuth1) { const oauthUrlFields = ['authUrl', 'requestTokenUrl', 'accessTokenUrl']; for (const field of oauthUrlFields) { const value = data[field]; if (typeof value === 'string' && value.trim() !== '' && !(0, n8n_workflow_1.isExpression)(value)) { (0, validate_oauth_url_1.validateOAuthUrl)(value); } } } } async createManagedCredential(dto, user) { return await this.createCredential({ ...dto, isManaged: true }, user); } async createCredential(opts, user) { const targetProjectId = await this.resolveOwningProjectIdForNewCredential(user, opts.projectId); await this.checkCredentialData(opts.type, opts.data, user, targetProjectId); if (this.externalSecretsConfig.externalSecretsForProjects) { await (0, validation_1.validateAccessToReferencedSecretProviders)(targetProjectId, opts.data, this.externalSecretsProviderAccessCheckService, 'create'); } const encryptedCredential = await this.createEncryptedData({ id: null, name: opts.name, type: opts.type, data: opts.data, }); const isGlobal = opts.isGlobal; if (isGlobal === true) { const canShareGlobally = (0, permissions_1.hasGlobalScope)(user, 'credential:shareGlobally'); if (!canShareGlobally) { throw new forbidden_error_1.ForbiddenError('You do not have permission to create globally shared credentials'); } encryptedCredential.isGlobal = isGlobal; } const credentialEntity = this.credentialsRepository.create({ ...encryptedCredential, isManaged: opts.isManaged, isResolvable: opts.isResolvable ?? false, }); const { shared, ...credential } = await this.save(credentialEntity, encryptedCredential, user, targetProjectId, opts.data); const scopes = await this.getCredentialScopes(user, credential.id); return { ...credential, scopes }; } async prepareCredentialsForTest({ storedCredential, user, credentialsToTest, }) { const decryptedData = await this.decrypt(storedCredential, true); const mergedCredentials = credentialsToTest ? (0, n8n_workflow_1.deepCopy)(credentialsToTest) : { id: storedCredential.id, name: storedCredential.name, type: storedCredential.type, data: decryptedData, }; if (user && credentialsToTest) { await this.replaceCredentialContentsForSharee(user, storedCredential, decryptedData, mergedCredentials); if (mergedCredentials.data) { mergedCredentials.data = this.unredact(mergedCredentials.data, decryptedData, this.getCredentialTypeProperties(storedCredential.type)); } } return mergedCredentials; } }; exports.CredentialsService = CredentialsService; exports.CredentialsService = CredentialsService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [db_1.CredentialsRepository, credential_dependency_service_1.CredentialDependencyService, db_1.SharedCredentialsRepository, ownership_service_1.OwnershipService, backend_common_1.Logger, n8n_core_1.ErrorReporter, credentials_tester_service_1.CredentialsTester, external_hooks_1.ExternalHooks, credential_types_1.CredentialTypes, db_1.ProjectRepository, project_service_ee_1.ProjectService, role_service_1.RoleService, db_1.UserRepository, credentials_finder_service_1.CredentialsFinderService, credentials_helper_1.CredentialsHelper, external_secrets_config_1.ExternalSecretsConfig, secret_provider_access_check_service_ee_1.SecretsProviderAccessCheckService]) ], CredentialsService); //# sourceMappingURL=credentials.service.js.map