refactor(core): Simplify ExternalSecretsProxy setup and move it to core (#16021)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2025-06-05 12:49:08 +02:00
committed by GitHub
parent 3e91f3253b
commit 2258a74518
20 changed files with 227 additions and 118 deletions

View File

@@ -4,6 +4,7 @@ import { ExecutionRepository } from '@n8n/db';
import { WorkflowRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { ExternalSecretsProxy } from 'n8n-core';
import type { IWorkflowBase } from 'n8n-workflow';
import type {
IExecuteWorkflowInfo,
@@ -23,7 +24,6 @@ import {
SubworkflowPolicyChecker,
} from '@/executions/pre-execution-checks';
import { ExternalHooks } from '@/external-hooks';
import { SecretsHelper } from '@/secrets-helpers.ee';
import { UrlService } from '@/services/url.service';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import { Telemetry } from '@/telemetry';
@@ -86,12 +86,12 @@ describe('WorkflowExecuteAdditionalData', () => {
const variablesService = mockInstance(VariablesService);
variablesService.getAllCached.mockResolvedValue([]);
const credentialsHelper = mockInstance(CredentialsHelper);
const secretsHelper = mockInstance(SecretsHelper);
const externalSecretsProxy = mockInstance(ExternalSecretsProxy);
const eventService = mockInstance(EventService);
mockInstance(ExternalHooks);
Container.set(VariablesService, variablesService);
Container.set(CredentialsHelper, credentialsHelper);
Container.set(SecretsHelper, secretsHelper);
Container.set(ExternalSecretsProxy, externalSecretsProxy);
const executionRepository = mockInstance(ExecutionRepository);
mockInstance(Telemetry);
const workflowRepository = mockInstance(WorkflowRepository);
@@ -306,7 +306,7 @@ describe('WorkflowExecuteAdditionalData', () => {
userId: undefined,
setExecutionStatus: expect.any(Function),
variables: mockVariables,
secretsHelpers: secretsHelper,
externalSecretsProxy,
startRunnerTask: expect.any(Function),
logAiEvent: expect.any(Function),
});

View File

@@ -11,6 +11,7 @@ import {
ObjectStoreService,
DataDeduplicationService,
ErrorReporter,
ExternalSecretsProxy,
} from 'n8n-core';
import { ensureError, sleep, UserError } from 'n8n-workflow';
@@ -278,6 +279,7 @@ export abstract class BaseCommand extends Command {
async initExternalSecrets() {
const secretsManager = Container.get(ExternalSecretsManager);
await secretsManager.init();
Container.get(ExternalSecretsProxy).setManager(secretsManager);
}
initWorkflowHistory() {

View File

@@ -6,7 +6,7 @@ import { Container } from '@n8n/di';
import Csrf from 'csrf';
import type { Response } from 'express';
import { captor, mock } from 'jest-mock-extended';
import { Cipher, type InstanceSettings } from 'n8n-core';
import { Cipher, type InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import nock from 'nock';
@@ -19,7 +19,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers.ee';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { mockInstance } from '@test/mocking';
@@ -28,7 +27,7 @@ jest.mock('@/workflow-execute-additional-data');
describe('OAuth1CredentialController', () => {
mockInstance(Logger);
mockInstance(ExternalHooks);
mockInstance(SecretsHelper);
mockInstance(ExternalSecretsProxy);
mockInstance(VariablesService, {
getAllCached: async () => [],
});

View File

@@ -6,7 +6,7 @@ import { Container } from '@n8n/di';
import Csrf from 'csrf';
import { type Response } from 'express';
import { captor, mock } from 'jest-mock-extended';
import { Cipher, type InstanceSettings } from 'n8n-core';
import { Cipher, type InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import nock from 'nock';
@@ -19,7 +19,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers.ee';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { mockInstance } from '@test/mocking';
@@ -27,7 +26,7 @@ jest.mock('@/workflow-execute-additional-data');
describe('OAuth2CredentialController', () => {
mockInstance(Logger);
mockInstance(SecretsHelper);
mockInstance(ExternalSecretsProxy);
mockInstance(VariablesService, {
getAllCached: async () => [],
});

View File

@@ -327,8 +327,6 @@ export class CredentialsHelper extends ICredentialsHelper {
return decryptedDataOriginal;
}
await additionalData?.secretsHelpers?.waitForInit();
return await this.applyDefaultsAndOverwrites(
additionalData,
decryptedDataOriginal,

View File

@@ -4,6 +4,7 @@ import { Container } from '@n8n/di';
import axios from 'axios';
import type { AxiosRequestConfig, Method } from 'axios';
import { Agent as HTTPSAgent } from 'https';
import { ExternalSecretsProxy } from 'n8n-core';
import { jsonParse, MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import type {
MessageEventBusDestinationOptions,
@@ -14,7 +15,6 @@ import type {
} from 'n8n-workflow';
import { CredentialsHelper } from '@/credentials-helper';
import { SecretsHelper } from '@/secrets-helpers.ee';
import { MessageEventBusDestination } from './message-event-bus-destination.ee';
import { eventMessageGenericDestinationTestEvent } from '../event-message-classes/event-message-generic';
@@ -103,7 +103,7 @@ export class MessageEventBusDestinationWebhook
if (foundCredential) {
const credentialsDecrypted = await this.credentialsHelper?.getDecrypted(
{
secretsHelpers: Container.get(SecretsHelper),
externalSecretsProxy: Container.get(ExternalSecretsProxy),
} as unknown as IWorkflowExecuteAdditionalData,
foundCredential[1],
foundCredential[0],

View File

@@ -352,10 +352,10 @@ describe('External Secrets Manager', () => {
expect(manager.getSecretNames('dummy')).toEqual(['test1', 'test2']);
});
test('should return undefined when provider does not exist', async () => {
test('should return an empty array when provider does not exist', async () => {
await manager.init();
expect(manager.getSecretNames('nonexistent')).toBeUndefined();
expect(manager.getSecretNames('nonexistent')).toBeEmptyArray();
});
});

View File

@@ -2,7 +2,7 @@ import { Logger } from '@n8n/backend-common';
import { SettingsRepository } from '@n8n/db';
import { OnPubSubEvent, OnShutdown } from '@n8n/decorators';
import { Service } from '@n8n/di';
import { Cipher } from 'n8n-core';
import { Cipher, type IExternalSecretsManager } from 'n8n-core';
import { jsonParse, type IDataObject, ensureError, UnexpectedError } from 'n8n-workflow';
import { EventService } from '@/events/event.service';
@@ -19,7 +19,7 @@ import { ExternalSecretsConfig } from './external-secrets.config';
import type { ExternalSecretsSettings, SecretsProvider, SecretsProviderSettings } from './types';
@Service()
export class ExternalSecretsManager {
export class ExternalSecretsManager implements IExternalSecretsManager {
private providers: Record<string, SecretsProvider> = {};
private initializingPromise?: Promise<void>;
@@ -211,7 +211,7 @@ export class ExternalSecretsManager {
return provider in this.providers;
}
getProviderNames(): string[] | undefined {
getProviderNames(): string[] {
return Object.keys(this.providers);
}
@@ -223,8 +223,8 @@ export class ExternalSecretsManager {
return this.getProvider(provider)?.hasSecret(name) ?? false;
}
getSecretNames(provider: string): string[] | undefined {
return this.getProvider(provider)?.getSecretNames();
getSecretNames(provider: string): string[] {
return this.getProvider(provider)?.getSecretNames() ?? [];
}
getAllSecretNames(): Record<string, string[]> {

View File

@@ -3,6 +3,7 @@ import type { IExecutionResponse } from '@n8n/db';
import type { ExecutionRepository } from '@n8n/db';
import { mock } from 'jest-mock-extended';
import type { WorkflowExecute as ActualWorkflowExecute } from 'n8n-core';
import { ExternalSecretsProxy } from 'n8n-core';
import { mockInstance } from 'n8n-core/test/utils';
import type { IPinData, ITaskData, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import { Workflow, type IRunExecutionData, type WorkflowExecuteMode } from 'n8n-workflow';
@@ -11,7 +12,6 @@ import { CredentialsHelper } from '@/credentials-helper';
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
import { ExternalHooks } from '@/external-hooks';
import type { ManualExecutionService } from '@/manual-execution.service';
import { SecretsHelper } from '@/secrets-helpers.ee';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';
@@ -23,7 +23,7 @@ mockInstance(VariablesService, {
getAllCached: jest.fn().mockResolvedValue([]),
});
mockInstance(CredentialsHelper);
mockInstance(SecretsHelper);
mockInstance(ExternalSecretsProxy);
mockInstance(WorkflowStaticDataService);
mockInstance(WorkflowStatisticsService);
mockInstance(ExternalHooks);

View File

@@ -1,42 +0,0 @@
import { Service } from '@n8n/di';
import type { SecretsHelpersBase } from 'n8n-workflow';
import { ExternalSecretsManager } from './external-secrets.ee/external-secrets-manager.ee';
@Service()
export class SecretsHelper implements SecretsHelpersBase {
constructor(private service: ExternalSecretsManager) {}
async update() {
if (!this.service.initialized) {
await this.service.init();
}
await this.service.updateSecrets();
}
async waitForInit() {
if (!this.service.initialized) {
await this.service.init();
}
}
getSecret(provider: string, name: string) {
return this.service.getSecret(provider, name);
}
hasSecret(provider: string, name: string): boolean {
return this.service.hasSecret(provider, name);
}
hasProvider(provider: string): boolean {
return this.service.hasProvider(provider);
}
listProviders(): string[] {
return this.service.getProviderNames() ?? [];
}
listSecrets(provider: string): string[] {
return this.service.getSecretNames(provider) ?? [];
}
}

View File

@@ -7,7 +7,7 @@ import { Logger } from '@n8n/backend-common';
import { GlobalConfig } from '@n8n/config';
import { ExecutionRepository, WorkflowRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { WorkflowExecute } from 'n8n-core';
import { ExternalSecretsProxy, WorkflowExecute } from 'n8n-core';
import { UnexpectedError, Workflow } from 'n8n-workflow';
import type {
IDataObject,
@@ -46,7 +46,6 @@ import {
import type { UpdateExecutionPayload } from '@/interfaces';
import { NodeTypes } from '@/node-types';
import { Push } from '@/push';
import { SecretsHelper } from '@/secrets-helpers.ee';
import { UrlService } from '@/services/url.service';
import { TaskRequester } from '@/task-runners/task-managers/task-requester';
import { findSubworkflowStart } from '@/utils';
@@ -388,7 +387,7 @@ export async function getBase(
userId,
setExecutionStatus,
variables,
secretsHelpers: Container.get(SecretsHelper),
externalSecretsProxy: Container.get(ExternalSecretsProxy),
async startRunnerTask(
additionalData: IWorkflowExecuteAdditionalData,
jobType: string,

View File

@@ -2,7 +2,7 @@ import type { WebhookEntity } from '@n8n/db';
import { WorkflowRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { InstanceSettings } from 'n8n-core';
import { InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
import { NodeApiError, Workflow } from 'n8n-workflow';
@@ -19,7 +19,6 @@ import { ExecutionService } from '@/executions/execution.service';
import { ExternalHooks } from '@/external-hooks';
import { NodeTypes } from '@/node-types';
import { Push } from '@/push';
import { SecretsHelper } from '@/secrets-helpers.ee';
import * as WebhookHelpers from '@/webhooks/webhook-helpers';
import { WebhookService } from '@/webhooks/webhook.service';
import * as AdditionalData from '@/workflow-execute-additional-data';
@@ -33,7 +32,7 @@ import { mockInstance } from '../shared/mocking';
mockInstance(ActiveExecutions);
mockInstance(Push);
mockInstance(SecretsHelper);
mockInstance(ExternalSecretsProxy);
mockInstance(ExecutionService);
mockInstance(WorkflowService);

View File

@@ -13,7 +13,7 @@ import type {
import type {
ExecutionLifecycleHookName,
ExecutionLifecyleHookHandlers,
ExecutionLifecycleHookHandlers,
} from '../execution-lifecycle-hooks';
import { ExecutionLifecycleHooks } from '../execution-lifecycle-hooks';
@@ -46,12 +46,14 @@ describe('ExecutionLifecycleHooks', () => {
describe('addHandler()', () => {
const hooksHandlers =
mock<{
[K in keyof ExecutionLifecyleHookHandlers]: ExecutionLifecyleHookHandlers[K][number];
[K in keyof ExecutionLifecycleHookHandlers]: ExecutionLifecycleHookHandlers[K][number];
}>();
const testCases: Array<{
hook: ExecutionLifecycleHookName;
args: Parameters<ExecutionLifecyleHookHandlers[keyof ExecutionLifecyleHookHandlers][number]>;
args: Parameters<
ExecutionLifecycleHookHandlers[keyof ExecutionLifecycleHookHandlers][number]
>;
}> = [
{ hook: 'nodeExecuteBefore', args: ['testNode', mock<ITaskStartedData>()] },
{

View File

@@ -0,0 +1,121 @@
import { mock } from 'jest-mock-extended';
import { ExternalSecretsProxy, type IExternalSecretsManager } from '../external-secrets-proxy';
describe('ExternalSecretsProxy', () => {
let proxy: ExternalSecretsProxy;
const manager = mock<IExternalSecretsManager>();
beforeEach(() => {
jest.resetAllMocks();
proxy = new ExternalSecretsProxy();
});
describe('update', () => {
it('should update secrets when manager is set', async () => {
manager.updateSecrets.mockResolvedValue();
proxy.setManager(manager);
await proxy.update();
expect(manager.updateSecrets).toHaveBeenCalledTimes(1);
});
it('should not throw when updating without a manager', async () => {
await expect(proxy.update()).resolves.not.toThrow();
});
});
describe('getSecret', () => {
it('should get secret from manager', () => {
const secretValue = { key: 'value' };
manager.getSecret.mockReturnValue(secretValue);
proxy.setManager(manager);
const result = proxy.getSecret('aws', 'api-key');
expect(manager.getSecret).toHaveBeenCalledWith('aws', 'api-key');
expect(result).toBe(secretValue);
});
it('should return undefined when getting secret without a manager', () => {
const result = proxy.getSecret('aws', 'api-key');
expect(result).toBeUndefined();
});
});
describe('hasSecret', () => {
it('should check if secret exists', () => {
manager.hasSecret.mockReturnValue(true);
proxy.setManager(manager);
const result = proxy.hasSecret('aws', 'api-key');
expect(manager.hasSecret).toHaveBeenCalledWith('aws', 'api-key');
expect(result).toBe(true);
});
it('should return false when checking secret without a manager', () => {
const result = proxy.hasSecret('aws', 'api-key');
expect(result).toBe(false);
});
});
describe('hasProvider', () => {
it('should check if provider exists', () => {
manager.hasProvider.mockReturnValue(true);
proxy.setManager(manager);
const result = proxy.hasProvider('aws');
expect(manager.hasProvider).toHaveBeenCalledWith('aws');
expect(result).toBe(true);
});
it('should return false when checking provider without a manager', () => {
const result = proxy.hasProvider('aws');
expect(result).toBe(false);
});
});
describe('listProviders', () => {
it('should list providers', () => {
const providers = ['aws', 'gcp', 'azure'];
manager.getProviderNames.mockReturnValue(providers);
proxy.setManager(manager);
const result = proxy.listProviders();
expect(manager.getProviderNames).toHaveBeenCalledTimes(1);
expect(result).toEqual(providers);
});
it('should return empty array when listing providers without a manager', () => {
const result = proxy.listProviders();
expect(result).toEqual([]);
});
});
describe('listSecrets', () => {
it('should list secrets for a provider', () => {
const secrets = ['api-key', 'api-secret', 'token'];
manager.getSecretNames.mockReturnValue(secrets);
proxy.setManager(manager);
const result = proxy.listSecrets('aws');
expect(manager.getSecretNames).toHaveBeenCalledWith('aws');
expect(result).toEqual(secrets);
});
it('should return empty array when listing secrets without a manager', () => {
const result = proxy.listSecrets('aws');
expect(result).toEqual([]);
});
});
});

View File

@@ -11,7 +11,7 @@ import type {
WorkflowExecuteMode,
} from 'n8n-workflow';
export type ExecutionLifecyleHookHandlers = {
export type ExecutionLifecycleHookHandlers = {
nodeExecuteBefore: Array<
(
this: ExecutionLifecycleHooks,
@@ -56,7 +56,7 @@ s */
>;
};
export type ExecutionLifecycleHookName = keyof ExecutionLifecyleHookHandlers;
export type ExecutionLifecycleHookName = keyof ExecutionLifecycleHookHandlers;
/**
* Contains hooks that trigger at specific events in an execution's lifecycle. Every hook has an array of callbacks to run.
@@ -77,7 +77,7 @@ export type ExecutionLifecycleHookName = keyof ExecutionLifecyleHookHandlers;
* ```
*/
export class ExecutionLifecycleHooks {
readonly handlers: ExecutionLifecyleHookHandlers = {
readonly handlers: ExecutionLifecycleHookHandlers = {
nodeExecuteAfter: [],
nodeExecuteBefore: [],
nodeFetchedData: [],
@@ -92,18 +92,18 @@ export class ExecutionLifecycleHooks {
readonly workflowData: IWorkflowBase,
) {}
addHandler<Hook extends keyof ExecutionLifecyleHookHandlers>(
addHandler<Hook extends keyof ExecutionLifecycleHookHandlers>(
hookName: Hook,
...handlers: Array<ExecutionLifecyleHookHandlers[Hook][number]>
...handlers: Array<ExecutionLifecycleHookHandlers[Hook][number]>
): void {
// @ts-expect-error FIX THIS
this.handlers[hookName].push(...handlers);
}
async runHook<
Hook extends keyof ExecutionLifecyleHookHandlers,
Hook extends keyof ExecutionLifecycleHookHandlers,
Params extends unknown[] = Parameters<
Exclude<ExecutionLifecyleHookHandlers[Hook], undefined>[number]
Exclude<ExecutionLifecycleHookHandlers[Hook], undefined>[number]
>,
>(hookName: Hook, parameters: Params) {
const hooks = this.handlers[hookName];
@@ -116,9 +116,3 @@ export class ExecutionLifecycleHooks {
}
}
}
declare module 'n8n-workflow' {
interface IWorkflowExecuteAdditionalData {
hooks?: ExecutionLifecycleHooks;
}
}

View File

@@ -0,0 +1,43 @@
import { Service } from '@n8n/di';
export interface IExternalSecretsManager {
updateSecrets(): Promise<void>;
hasSecret(provider: string, name: string): boolean;
getSecret(provider: string, name: string): unknown;
getSecretNames(provider: string): string[];
hasProvider(provider: string): boolean;
getProviderNames(): string[];
}
@Service()
export class ExternalSecretsProxy {
private manager?: IExternalSecretsManager;
setManager(manager: IExternalSecretsManager) {
this.manager = manager;
}
async update() {
await this.manager?.updateSecrets();
}
getSecret(provider: string, name: string) {
return this.manager?.getSecret(provider, name);
}
hasSecret(provider: string, name: string): boolean {
return !!this.manager && this.manager.hasSecret(provider, name);
}
hasProvider(provider: string): boolean {
return !!this.manager && this.manager.hasProvider(provider);
}
listProviders(): string[] {
return this.manager?.getProviderNames() ?? [];
}
listSecrets(provider: string): string[] {
return this.manager?.getSecretNames(provider) ?? [];
}
}

View File

@@ -1,3 +1,13 @@
import type { ExecutionLifecycleHooks } from './execution-lifecycle-hooks';
import type { ExternalSecretsProxy } from './external-secrets-proxy';
declare module 'n8n-workflow' {
interface IWorkflowExecuteAdditionalData {
hooks?: ExecutionLifecycleHooks;
externalSecretsProxy: ExternalSecretsProxy;
}
}
export * from './active-workflows';
export * from './interfaces';
export * from './routing-node';
@@ -6,3 +16,4 @@ export * from './partial-execution-utils';
export * from './node-execution-context/utils/execution-metadata';
export * from './workflow-execute';
export { ExecutionLifecycleHooks } from './execution-lifecycle-hooks';
export { ExternalSecretsProxy, type IExternalSecretsManager } from './external-secrets-proxy';

View File

@@ -1,24 +1,20 @@
import { mock } from 'jest-mock-extended';
import { LoggerProxy } from 'n8n-workflow';
import type {
IDataObject,
IRunExecutionData,
IWorkflowExecuteAdditionalData,
SecretsHelpersBase,
} from 'n8n-workflow';
import type { IDataObject, IRunExecutionData, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import { PLACEHOLDER_EMPTY_EXECUTION_ID } from '@/constants';
import type { ExternalSecretsProxy } from '@/execution-engine/external-secrets-proxy';
import { getAdditionalKeys } from '../get-additional-keys';
describe('getAdditionalKeys', () => {
const secretsHelpers = mock<SecretsHelpersBase>();
const externalSecretsProxy = mock<ExternalSecretsProxy>();
const additionalData = mock<IWorkflowExecuteAdditionalData>({
executionId: '123',
webhookWaitingBaseUrl: 'https://webhook.test',
formWaitingBaseUrl: 'https://form.test',
variables: { testVar: 'value' },
secretsHelpers,
externalSecretsProxy,
});
const runExecutionData = mock<IRunExecutionData>({
@@ -30,11 +26,11 @@ describe('getAdditionalKeys', () => {
beforeAll(() => {
LoggerProxy.init(mock());
secretsHelpers.hasProvider.mockReturnValue(true);
secretsHelpers.hasSecret.mockReturnValue(true);
secretsHelpers.getSecret.mockReturnValue('secret-value');
secretsHelpers.listSecrets.mockReturnValue(['secret1']);
secretsHelpers.listProviders.mockReturnValue(['provider1']);
externalSecretsProxy.hasProvider.mockReturnValue(true);
externalSecretsProxy.hasSecret.mockReturnValue(true);
externalSecretsProxy.getSecret.mockReturnValue('secret-value');
externalSecretsProxy.listSecrets.mockReturnValue(['secret1']);
externalSecretsProxy.listProviders.mockReturnValue(['provider1']);
});
it('should use placeholder execution ID when none provided', () => {

View File

@@ -23,7 +23,7 @@ function buildSecretsValueProxy(value: IDataObject): unknown {
}
export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData): IDataObject {
const secretsHelpers = additionalData.secretsHelpers;
const { externalSecretsProxy } = additionalData;
return new Proxy(
{},
{
@@ -31,7 +31,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
if (typeof providerName !== 'string') {
return {};
}
if (secretsHelpers.hasProvider(providerName)) {
if (externalSecretsProxy.hasProvider(providerName)) {
return new Proxy(
{},
{
@@ -39,13 +39,13 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
if (typeof secretName !== 'string') {
return;
}
if (!secretsHelpers.hasSecret(providerName, secretName)) {
if (!externalSecretsProxy.hasSecret(providerName, secretName)) {
throw new ExpressionError('Could not load secrets', {
description:
'The credential in use tries to use secret from an external store that could not be found',
});
}
const retValue = secretsHelpers.getSecret(providerName, secretName);
const retValue = externalSecretsProxy.getSecret(providerName, secretName);
if (typeof retValue === 'object' && retValue !== null) {
return buildSecretsValueProxy(retValue as IDataObject);
}
@@ -55,7 +55,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
return false;
},
ownKeys() {
return secretsHelpers.listSecrets(providerName);
return externalSecretsProxy.listSecrets(providerName);
},
},
);
@@ -69,7 +69,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
return false;
},
ownKeys() {
return secretsHelpers.listProviders();
return externalSecretsProxy.listProviders();
},
},
);

View File

@@ -2374,7 +2374,6 @@ export interface IWorkflowExecuteAdditionalData {
executionTimeoutTimestamp?: number;
userId?: string;
variables: IDataObject;
secretsHelpers: SecretsHelpersBase;
logAiEvent: (eventName: AiEvent, payload: AiEventPayload) => void;
parentCallbackManager?: CallbackManager;
startRunnerTask<T, E = unknown>(
@@ -2870,17 +2869,6 @@ export interface ICheckProcessedContextData {
export type N8nAIProviderType = 'openai' | 'unknown';
export interface SecretsHelpersBase {
update(): Promise<void>;
waitForInit(): Promise<void>;
getSecret(provider: string, name: string): unknown;
hasSecret(provider: string, name: string): boolean;
hasProvider(provider: string): boolean;
listProviders(): string[];
listSecrets(provider: string): string[];
}
export type Functionality = 'regular' | 'configuration-node' | 'pairedItem';
export type CallbackManager = CallbackManagerLC;