UNPKG

n8n

Version:

n8n Workflow Automation Tool

340 lines 13.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.ExternalSecretsManager = void 0; const di_1 = require("@n8n/di"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const settings_repository_1 = require("../databases/repositories/settings.repository"); const event_service_1 = require("../events/event.service"); const license_1 = require("../license"); const publisher_service_1 = require("../scaling/pubsub/publisher.service"); const constants_1 = require("./constants"); const external_secrets_helper_ee_1 = require("./external-secrets-helper.ee"); const external_secrets_providers_ee_1 = require("./external-secrets-providers.ee"); let ExternalSecretsManager = class ExternalSecretsManager { constructor(logger, settingsRepo, license, secretsProviders, cipher, eventService, publisher) { this.logger = logger; this.settingsRepo = settingsRepo; this.license = license; this.secretsProviders = secretsProviders; this.cipher = cipher; this.eventService = eventService; this.publisher = publisher; this.providers = {}; this.cachedSettings = {}; this.initialized = false; this.initRetryTimeouts = {}; this.logger = this.logger.scoped('external-secrets'); } async init() { if (!this.initialized) { if (!this.initializingPromise) { this.initializingPromise = new Promise(async (resolve) => { await this.internalInit(); this.initialized = true; resolve(); this.initializingPromise = undefined; this.updateInterval = setInterval(async () => await this.updateSecrets(), (0, external_secrets_helper_ee_1.updateIntervalTime)()); }); } return await this.initializingPromise; } this.logger.debug('External secrets manager initialized'); } shutdown() { clearInterval(this.updateInterval); Object.values(this.providers).forEach((p) => { void p.disconnect().catch(() => { }); }); Object.values(this.initRetryTimeouts).forEach((v) => clearTimeout(v)); this.logger.debug('External secrets manager shut down'); } async reloadAllProviders(backoff) { this.logger.debug('Reloading all external secrets providers'); const providers = this.getProviderNames(); if (!providers) { return; } for (const provider of providers) { await this.reloadProvider(provider, backoff); } this.logger.debug('External secrets managed reloaded all providers'); } broadcastReloadExternalSecretsProviders() { void this.publisher.publishCommand({ command: 'reload-external-secrets-providers' }); } decryptSecretsSettings(value) { const decryptedData = this.cipher.decrypt(value); try { return (0, n8n_workflow_1.jsonParse)(decryptedData); } catch (e) { throw new n8n_workflow_1.UnexpectedError('External Secrets Settings could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.'); } } async getDecryptedSettings(settingsRepo) { const encryptedSettings = await settingsRepo.getEncryptedSecretsProviderSettings(); if (encryptedSettings === null) { return null; } return this.decryptSecretsSettings(encryptedSettings); } async internalInit() { const settings = await this.getDecryptedSettings(this.settingsRepo); if (!settings) { return; } const providers = (await Promise.allSettled(Object.entries(settings).map(async ([name, providerSettings]) => await this.initProvider(name, providerSettings)))).map((i) => (i.status === 'rejected' ? null : i.value)); this.providers = Object.fromEntries(providers.filter((p) => p !== null).map((s) => [s.name, s])); this.cachedSettings = settings; await this.updateSecrets(); } async initProvider(name, providerSettings, currentBackoff = constants_1.EXTERNAL_SECRETS_INITIAL_BACKOFF) { const providerClass = this.secretsProviders.getProvider(name); if (!providerClass) { return null; } const provider = new providerClass(); try { await provider.init(providerSettings); } catch (e) { this.logger.error(`Error initializing secrets provider ${provider.displayName} (${provider.name}).`); this.retryInitWithBackoff(name, currentBackoff); return provider; } try { if (providerSettings.connected) { await provider.connect(); } } catch (e) { try { await provider.disconnect(); } catch { } this.logger.error(`Error initializing secrets provider ${provider.displayName} (${provider.name}).`); this.retryInitWithBackoff(name, currentBackoff); return provider; } return provider; } retryInitWithBackoff(name, currentBackoff) { if (name in this.initRetryTimeouts) { clearTimeout(this.initRetryTimeouts[name]); delete this.initRetryTimeouts[name]; } this.initRetryTimeouts[name] = setTimeout(() => { delete this.initRetryTimeouts[name]; if (this.providers[name] && this.providers[name].state !== 'error') { return; } void this.reloadProvider(name, Math.min(currentBackoff * 2, constants_1.EXTERNAL_SECRETS_MAX_BACKOFF)); }, currentBackoff); } async updateSecrets() { if (!this.license.isExternalSecretsEnabled()) { return; } await Promise.allSettled(Object.entries(this.providers).map(async ([k, p]) => { try { if (this.cachedSettings[k].connected && p.state === 'connected') { await p.update(); } } catch { this.logger.error(`Error updating secrets provider ${p.displayName} (${p.name}).`); } })); this.logger.debug('External secrets manager updated secrets'); } getProvider(provider) { return this.providers[provider]; } hasProvider(provider) { return provider in this.providers; } getProviderNames() { return Object.keys(this.providers); } getSecret(provider, name) { return this.getProvider(provider)?.getSecret(name); } hasSecret(provider, name) { return this.getProvider(provider)?.hasSecret(name) ?? false; } getSecretNames(provider) { return this.getProvider(provider)?.getSecretNames(); } getAllSecretNames() { return Object.fromEntries(Object.keys(this.providers).map((provider) => [ provider, this.getSecretNames(provider) ?? [], ])); } getProvidersWithSettings() { return Object.entries(this.secretsProviders.getAllProviders()).map(([k, c]) => ({ provider: this.getProvider(k) ?? new c(), settings: this.cachedSettings[k] ?? {}, })); } getProviderWithSettings(provider) { const providerConstructor = this.secretsProviders.getProvider(provider); if (!providerConstructor) { return undefined; } return { provider: this.getProvider(provider) ?? new providerConstructor(), settings: this.cachedSettings[provider] ?? {}, }; } async reloadProvider(provider, backoff = constants_1.EXTERNAL_SECRETS_INITIAL_BACKOFF) { if (provider in this.providers) { await this.providers[provider].disconnect(); delete this.providers[provider]; } const newProvider = await this.initProvider(provider, this.cachedSettings[provider], backoff); if (newProvider) { this.providers[provider] = newProvider; } this.logger.debug(`External secrets manager reloaded provider ${provider}`); } async setProviderSettings(provider, data, userId) { let isNewProvider = false; let settings = await this.getDecryptedSettings(this.settingsRepo); if (!settings) { settings = {}; } if (!(provider in settings)) { isNewProvider = true; } settings[provider] = { connected: settings[provider]?.connected ?? false, connectedAt: settings[provider]?.connectedAt ?? new Date(), settings: data, }; await this.saveAndSetSettings(settings, this.settingsRepo); this.cachedSettings = settings; await this.reloadProvider(provider); this.broadcastReloadExternalSecretsProviders(); void this.trackProviderSave(provider, isNewProvider, userId); } async setProviderConnected(provider, connected) { let settings = await this.getDecryptedSettings(this.settingsRepo); if (!settings) { settings = {}; } settings[provider] = { connected, connectedAt: connected ? new Date() : (settings[provider]?.connectedAt ?? null), settings: settings[provider]?.settings ?? {}, }; await this.saveAndSetSettings(settings, this.settingsRepo); this.cachedSettings = settings; await this.reloadProvider(provider); await this.updateSecrets(); this.broadcastReloadExternalSecretsProviders(); } async trackProviderSave(vaultType, isNew, userId) { let testResult; try { testResult = await this.getProvider(vaultType)?.test(); } catch { } this.eventService.emit('external-secrets-provider-settings-saved', { userId, vaultType, isNew, isValid: testResult?.[0] ?? false, errorMessage: testResult?.[1], }); } encryptSecretsSettings(settings) { return this.cipher.encrypt(settings); } async saveAndSetSettings(settings, settingsRepo) { const encryptedSettings = this.encryptSecretsSettings(settings); await settingsRepo.saveEncryptedSecretsProviderSettings(encryptedSettings); } async testProviderSettings(provider, data) { let testProvider = null; try { testProvider = await this.initProvider(provider, { connected: true, connectedAt: new Date(), settings: data, }); if (!testProvider) { return { success: false, testState: 'error', }; } const [success, error] = await testProvider.test(); let testState = 'error'; if (success && this.cachedSettings[provider]?.connected) { testState = 'connected'; } else if (success) { testState = 'tested'; } return { success, testState, error, }; } catch { return { success: false, testState: 'error', }; } finally { if (testProvider) { await testProvider.disconnect(); } } } async updateProvider(provider) { if (!this.license.isExternalSecretsEnabled()) { return false; } if (!this.providers[provider] || this.providers[provider].state !== 'connected') { return false; } try { await this.providers[provider].update(); this.broadcastReloadExternalSecretsProviders(); this.logger.debug(`External secrets manager updated provider ${provider}`); return true; } catch (error) { this.logger.debug(`External secrets manager failed to update provider ${provider}`, { error: (0, n8n_workflow_1.ensureError)(error), }); return false; } } }; exports.ExternalSecretsManager = ExternalSecretsManager; exports.ExternalSecretsManager = ExternalSecretsManager = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [n8n_core_1.Logger, settings_repository_1.SettingsRepository, license_1.License, external_secrets_providers_ee_1.ExternalSecretsProviders, n8n_core_1.Cipher, event_service_1.EventService, publisher_service_1.Publisher]) ], ExternalSecretsManager); //# sourceMappingURL=external-secrets-manager.ee.js.map