UNPKG

n8n

Version:

n8n Workflow Automation Tool

270 lines • 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsSecretsManager = void 0; const backend_common_1 = require("@n8n/backend-common"); const backend_network_1 = require("@n8n/backend-network"); const di_1 = require("@n8n/di"); const constants_1 = require("../constants"); const secrets_provider_errors_1 = require("../errors/secrets-provider-errors"); const unknown_auth_type_error_1 = require("../errors/unknown-auth-type.error"); const types_1 = require("../types"); class AwsSecretsManager extends types_1.SecretsProvider { constructor(logger = di_1.Container.get(backend_common_1.Logger), outboundHttp = di_1.Container.get(backend_network_1.OutboundHttp)) { super(); this.logger = logger; this.outboundHttp = outboundHttp; this.name = 'awsSecretsManager'; this.displayName = 'AWS Secrets Manager'; this.properties = [ constants_1.DOCS_HELP_NOTICE, { displayName: 'Region', name: 'region', type: 'string', default: '', required: true, placeholder: 'e.g. eu-west-3', noDataExpression: true, }, { displayName: 'Authentication Method', name: 'authMethod', type: 'options', options: [ { name: 'IAM User', value: 'iamUser', description: 'Credentials for IAM user having <code>secretsmanager:ListSecrets</code> and <code>secretsmanager:BatchGetSecretValue</code> permissions. <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html" target="_blank">Learn more</a>', }, { name: 'Auto Detect', value: 'autoDetect', description: 'Use automatic credential detection to authenticate AWS calls for external secrets<a href="https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html#credchain" target="_blank">Learn more</a>.', }, ], default: 'iamUser', required: true, noDataExpression: true, }, { displayName: 'Access Key ID', name: 'accessKeyId', type: 'string', default: '', required: true, placeholder: 'e.g. ACHXUQMBAQEVTE2RKMWP', noDataExpression: true, displayOptions: { show: { authMethod: ['iamUser'], }, }, }, { displayName: 'Secret Access Key', name: 'secretAccessKey', type: 'string', default: '', required: true, placeholder: 'e.g. cbmjrH/xNAjPwlQR3i/1HRSDD+esQX/Lan3gcmBc', typeOptions: { password: true }, noDataExpression: true, displayOptions: { show: { authMethod: ['iamUser'], }, }, }, ]; this.cachedSecrets = {}; this.logger = this.logger.scoped('external-secrets'); } async init(context) { const { region, authMethod } = context.settings; this.settings = { region, authMethod }; try { this.assertAuthType(context); const clientConfig = { region }; if (authMethod === 'iamUser') { const { accessKeyId, secretAccessKey } = context.settings; clientConfig.credentials = { accessKeyId, secretAccessKey }; } clientConfig.requestHandler = this.outboundHttp .transport({ ssrf: 'disabled', }) .getNodeAgent(); const { SecretsManager } = await import('@aws-sdk/client-secrets-manager'); this.client = new SecretsManager(clientConfig); this.logger.debug('AWS Secrets Manager provider initialized'); } catch (error) { this.logOperationFailure('Failed to initialize AWS Secrets Manager provider', { operation: 'initialize', error, }); throw error; } } async test() { try { await this.verifyConnection(); return [true]; } catch (e) { const error = e instanceof Error ? e : new Error(`${e}`); this.logOperationFailure('AWS Secrets Manager provider test failed', { operation: 'test', error, context: this.awsErrorContext(error), }); return [false, error.message]; } } async doConnect() { try { await this.verifyConnection(); this.logger.debug('AWS Secrets Manager provider connected'); } catch (error) { this.logOperationFailure('Failed to connect AWS Secrets Manager provider', { operation: 'connect', error, context: this.awsErrorContext(error), }); throw error; } } async disconnect() { return; } async update() { try { const secrets = await this.fetchAllSecrets(); const supportedSecrets = secrets; this.cachedSecrets = Object.fromEntries(supportedSecrets.map((s) => [s.secretName, s.secretValue])); this.logger.debug('AWS Secrets Manager provider secrets updated'); } catch (error) { this.logOperationFailure('Failed to update AWS Secrets Manager provider secrets', { operation: 'update', error, context: this.awsErrorContext(error), }); throw error; } } getSecret(name) { return this.cachedSecrets[name]; } hasSecret(name) { return name in this.cachedSecrets; } getSecretNames() { return Object.keys(this.cachedSecrets); } assertAuthType(context) { const { authMethod } = context.settings; if (authMethod === 'iamUser' || authMethod === 'autoDetect') return; throw new unknown_auth_type_error_1.UnknownAuthTypeError(authMethod); } async fetchAllSecretsNames() { const names = []; let nextToken; do { const response = await this.client.listSecrets({ NextToken: nextToken, }); if (response.SecretList) { names.push(...response.SecretList.filter((s) => s.Name).map((s) => s.Name)); } nextToken = response.NextToken; } while (nextToken); return names; } async fetchAllSecrets() { const secrets = []; const secretNames = await this.fetchAllSecretsNames(); const batches = this.batch(secretNames); for (const batch of batches) { const response = await this.client.batchGetSecretValue({ SecretIdList: batch, }); if (response.SecretValues) { for (const secret of response.SecretValues) { if (secret.Name && secret.SecretString) { secrets.push({ secretName: secret.Name, secretValue: secret.SecretString, }); } } } } return secrets; } batch(arr, size = 20) { return Array.from({ length: Math.ceil(arr.length / size) }, (_, index) => arr.slice(index * size, (index + 1) * size)); } async verifyConnection() { await this.client.listSecrets({ MaxResults: 1 }); } isRecord(value) { return typeof value === 'object' && value !== null; } getAwsErrorCode(error) { if (this.isRecord(error)) { if ('Code' in error && typeof error.Code === 'string') { return error.Code; } if ('code' in error) { const { code } = error; if (typeof code === 'string' || typeof code === 'number') { return code; } } } if (error instanceof Error) { return error.name; } if (this.isRecord(error) && typeof error.name === 'string') { return error.name; } return undefined; } awsErrorContext(error) { const context = {}; const errorCode = this.getAwsErrorCode(error); if (errorCode !== undefined) { context.errorCode = errorCode; } if (this.isRecord(error) && '$metadata' in error) { const metadata = error.$metadata; if (this.isRecord(metadata) && typeof metadata.httpStatusCode === 'number') { context.statusCode = metadata.httpStatusCode; } } return context; } logOperationFailure(message, params) { const context = { ...params.context }; if (this.settings?.region) { context.region = this.settings.region; } if (this.settings?.authMethod) { context.authMethod = this.settings.authMethod; } (0, secrets_provider_errors_1.logSecretsProviderOperationFailure)({ logger: this.logger, message, providerName: this.name, providerDisplayName: this.displayName, operation: params.operation, error: params.error, context, }); } } exports.AwsSecretsManager = AwsSecretsManager; //# sourceMappingURL=aws-secrets-manager.js.map