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