UNPKG

n8n

Version:

n8n Workflow Automation Tool

342 lines 15.1 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 backend_common_1 = require("@n8n/backend-common"); const constants_1 = require("@n8n/constants"); const db_1 = require("@n8n/db"); const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const not_found_error_1 = require("../../errors/response-errors/not-found.error"); const event_service_1 = require("../../events/event.service"); const publisher_service_1 = require("../../scaling/pubsub/publisher.service"); const external_secrets_providers_ee_1 = require("./external-secrets-providers.ee"); const external_secrets_config_1 = require("./external-secrets.config"); const provider_lifecycle_service_1 = require("./provider-lifecycle.service"); const provider_registry_service_1 = require("./provider-registry.service"); const retry_manager_service_1 = require("./retry-manager.service"); const secrets_cache_service_1 = require("./secrets-cache.service"); const settings_store_service_1 = require("./settings-store.service"); let ExternalSecretsManager = class ExternalSecretsManager { constructor(logger, config, providersFactory, eventService, publisher, settingsStore, providerRegistry, providerLifecycle, retryManager, secretsCache, secretsProviderConnectionRepository, cipher) { this.logger = logger; this.config = config; this.providersFactory = providersFactory; this.eventService = eventService; this.publisher = publisher; this.settingsStore = settingsStore; this.providerRegistry = providerRegistry; this.providerLifecycle = providerLifecycle; this.retryManager = retryManager; this.secretsCache = secretsCache; this.secretsProviderConnectionRepository = secretsProviderConnectionRepository; this.cipher = cipher; this.initialized = false; this.logger = this.logger.scoped('external-secrets'); } async init() { if (this.initialized) return; this.initializingPromise ??= (async () => { try { await this.reloadAllProviders(); this.startSecretsRefresh(); this.initialized = true; this.logger.debug('External secrets manager initialized'); } catch (error) { this.logger.error('External secrets manager failed to initialize', { error }); throw error; } finally { this.initializingPromise = undefined; } })(); await this.initializingPromise; } shutdown() { this.stopSecretsRefresh(); this.retryManager.cancelAll(); void this.providerRegistry.disconnectAll(); this.initialized = false; this.logger.debug('External secrets manager shut down'); } getProvider(provider) { return this.providerRegistry.get(provider); } getProviderProperties(providerType) { const ProviderClass = this.providersFactory.getProvider(providerType); if (!ProviderClass) { throw new not_found_error_1.NotFoundError(`Provider type "${providerType}" not found`); } return new ProviderClass().properties; } hasProvider(provider) { return this.providerRegistry.has(provider); } getProviderNames() { return this.providerRegistry.getNames(); } getProvidersWithSettings() { const allProviderClasses = this.providersFactory.getAllProviders(); const settings = this.getCachedSettings(); return Object.entries(allProviderClasses).map(([name, providerClass]) => ({ provider: this.providerRegistry.get(name) ?? new providerClass(), settings: settings[name] ?? {}, })); } getProviderWithSettings(provider) { const ProviderClass = this.providersFactory.getProvider(provider); const settings = this.getCachedSettings(); return { provider: this.providerRegistry.get(provider) ?? new ProviderClass(), settings: settings[provider] ?? {}, }; } async syncProviderConnection(providerKey) { await this.tearDownProviderConnection(providerKey); const connection = await this.secretsProviderConnectionRepository.findOne({ where: { providerKey }, }); if (connection?.isEnabled) { const settings = await this.decryptSettings(connection.encryptedSettings); await this.setupProvider(connection.type, { connected: true, connectedAt: null, settings }, providerKey); const provider = this.providerRegistry.get(providerKey); if (provider) { await this.secretsCache.refreshProvider(providerKey, provider); } } this.broadcastReload(); } async updateProvider(providerKey) { const providerInstance = this.providerRegistry.get(providerKey); if (!providerInstance) { throw new not_found_error_1.NotFoundError(`Provider "${providerKey}" not found`); } if (providerInstance.state !== 'connected') { throw new n8n_workflow_1.UnexpectedError(`Provider "${providerKey}" is not connected`); } await providerInstance.update(); this.broadcastReload(); this.logger.debug(`Updated provider ${providerKey}`); this.eventService.emit('external-secrets-provider-reloaded', { vaultType: providerKey, }); } getSecret(provider, name) { return this.secretsCache.getSecret(provider, name); } hasSecret(provider, name) { return this.secretsCache.hasSecret(provider, name); } getSecretNames(provider) { return this.secretsCache.getSecretNames(provider); } getAllSecretNames() { return this.secretsCache.getAllSecretNames(); } async setProviderSettings(provider, settings, userId) { const { isNewProvider } = await this.settingsStore.updateProvider(provider, { settings }); await this.reloadProvider(provider); this.broadcastReload(); void this.trackProviderSave(provider, isNewProvider, userId); } async setProviderConnected(provider, connected) { await this.settingsStore.updateProvider(provider, { connected }); if (connected) { await this.retryManager.runWithRetry(provider, async () => await this.connectProvider(provider)); } else { this.retryManager.cancelRetry(provider); const providerInstance = this.providerRegistry.get(provider); if (providerInstance) { await this.providerLifecycle.disconnect(providerInstance); } } this.broadcastReload(); } async testProviderSettings(provider, data) { const testSettings = { connected: true, connectedAt: new Date(), settings: data, }; const errorState = { success: false, testState: 'error', }; const result = await this.providerLifecycle.initialize(provider, testSettings); if (!result.success || !result.provider) { return errorState; } try { const connectResult = await this.providerLifecycle.connect(result.provider); if (!connectResult.success) { return { ...errorState, error: connectResult.error?.message, }; } const [success, error] = await result.provider.test(); if (!success) { return { success: false, testState: 'error', error }; } const currentSettings = await this.settingsStore.getProvider(provider); const testState = currentSettings?.connected ? 'connected' : 'tested'; return { success: true, testState }; } catch { return errorState; } finally { await result.provider?.disconnect(); } } async reloadAllProviders() { if (this.config.externalSecretsForProjects || this.config.externalSecretsMultipleConnections) { await this.reloadProvidersFromConnectionsRepo(); return; } this.logger.debug('Reloading all external secrets providers'); const newSettings = await this.settingsStore.reload(); for (const name of Object.keys(newSettings)) { await this.reloadProvider(name); } await this.secretsCache.refreshAll(); this.logger.debug('Reloaded all external secrets providers'); } async reloadProvidersFromConnectionsRepo() { this.logger.debug('Initializing external secrets with project-based providers'); const connections = await this.secretsProviderConnectionRepository.findAll(); for (const connection of connections) { await this.tearDownProviderConnection(connection.providerKey); if (!connection.isEnabled) continue; const settings = await this.decryptSettings(connection.encryptedSettings); const connectionSettings = { connected: true, connectedAt: null, settings, }; await this.setupProvider(connection.type, connectionSettings, connection.providerKey); } await this.secretsCache.refreshAll(); this.logger.debug('Reloaded external secrets providers'); } async setupProvider(providerType, config, providerKey) { const key = providerKey ?? providerType; const result = await this.providerLifecycle.initialize(providerType, config); if (!result.success || !result.provider) { this.logger.error(`Failed to initialize provider ${key}`, { error: result.error, }); return; } this.providerRegistry.add(key, result.provider); if (config.connected) { await this.retryManager.runWithRetry(key, async () => await this.connectProvider(key)); } } async connectProvider(name) { const provider = this.providerRegistry.get(name); if (!provider) { this.logger.warn(`Cannot connect provider ${name}: not found in registry`); throw new Error(`Provider ${name} not found in registry`); } return await this.providerLifecycle.connect(provider); } async reloadProvider(name) { const config = await this.settingsStore.getProvider(name); if (!config) { this.logger.warn(`Cannot reload provider ${name}: settings not found`); return; } await this.tearDownProviderConnection(name); await this.setupProvider(name, config); } async tearDownProviderConnection(providerKey) { this.retryManager.cancelRetry(providerKey); const existingProvider = this.providerRegistry.get(providerKey); if (existingProvider) { this.logger.debug(`Tearing down provider connection: ${providerKey}`); await this.providerLifecycle.disconnect(existingProvider); this.providerRegistry.remove(providerKey); } } async decryptSettings(encryptedData) { try { const decryptedData = await this.cipher.decryptV2(encryptedData); return (0, n8n_workflow_1.jsonParse)(decryptedData); } catch (e) { this.logger.error('Failed to decrypt external secrets settings', { error: 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 updateSecrets() { await this.secretsCache.refreshAll(); } startSecretsRefresh() { this.refreshInterval = setInterval(async () => await this.secretsCache.refreshAll(), this.config.updateInterval * constants_1.Time.seconds.toMilliseconds); this.logger.debug('Started secrets refresh interval'); } stopSecretsRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = undefined; } } broadcastReload() { void this.publisher.publishCommand({ command: 'reload-external-secrets-providers' }); } 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], }); } getCachedSettings() { return this.settingsStore.getCached(); } }; exports.ExternalSecretsManager = ExternalSecretsManager; __decorate([ (0, decorators_1.OnPubSubEvent)('reload-external-secrets-providers'), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], ExternalSecretsManager.prototype, "reloadAllProviders", null); exports.ExternalSecretsManager = ExternalSecretsManager = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, external_secrets_config_1.ExternalSecretsConfig, external_secrets_providers_ee_1.ExternalSecretsProviders, event_service_1.EventService, publisher_service_1.Publisher, settings_store_service_1.ExternalSecretsSettingsStore, provider_registry_service_1.ExternalSecretsProviderRegistry, provider_lifecycle_service_1.ExternalSecretsProviderLifecycle, retry_manager_service_1.ExternalSecretsRetryManager, secrets_cache_service_1.ExternalSecretsSecretsCache, db_1.SecretsProviderConnectionRepository, n8n_core_1.Cipher]) ], ExternalSecretsManager); //# sourceMappingURL=external-secrets-manager.ee.js.map