diff --git a/packages/core/src/execution-engine/node-execution-context/__tests__/node-execution-context.test.ts b/packages/core/src/execution-engine/node-execution-context/__tests__/node-execution-context.test.ts index 7cddaeec313..54a3c4462de 100644 --- a/packages/core/src/execution-engine/node-execution-context/__tests__/node-execution-context.test.ts +++ b/packages/core/src/execution-engine/node-execution-context/__tests__/node-execution-context.test.ts @@ -364,6 +364,38 @@ describe('NodeExecutionContext', () => { }); }); + describe('_getRunlessCredentials', () => { + it('should pass placeholder execute data that only carries the node', async () => { + const credentialDetails = { id: 'cred-runless', name: 'Runless Cred' }; + const runlessNode = mock({ type: 'n8n-nodes-base.httpRequest' }); + runlessNode.credentials = { someCred: credentialDetails }; + + const mockCredentialsHelper = { + getDecrypted: vi.fn().mockResolvedValue({ token: 'ok' }), + getCredentialsProperties: vi.fn(), + isCredentialUsableByNode: vi.fn().mockReturnValue(true), + }; + + const ctx = new TestContext( + workflow, + runlessNode, + mock({ credentialsHelper: mockCredentialsHelper }), + mode, + ); + + await expect(ctx['_getRunlessCredentials']('someCred')).resolves.toEqual({ token: 'ok' }); + expect(mockCredentialsHelper.getDecrypted).toHaveBeenCalledWith( + expect.anything(), + credentialDetails, + 'someCred', + mode, + { data: {}, node: runlessNode, source: null }, + false, + undefined, + ); + }); + }); + describe('prepareOutputData', () => { it('should return the input array wrapped in another array', async () => { const outputData = [mock(), mock()]; diff --git a/packages/core/src/execution-engine/node-execution-context/hook-context.ts b/packages/core/src/execution-engine/node-execution-context/hook-context.ts index 6b769559a6f..1ba81c069fa 100644 --- a/packages/core/src/execution-engine/node-execution-context/hook-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/hook-context.ts @@ -1,7 +1,6 @@ import { UnexpectedError } from 'n8n-workflow'; import type { ICredentialDataDecryptedObject, - IExecuteData, INode, IHookFunctions, IWorkflowExecuteAdditionalData, @@ -37,12 +36,7 @@ export class HookContext extends NodeExecutionContext implements IHookFunctions } async getCredentials(type: string) { - // No real task run backs a webhook-registration hook, so this only exists to - // surface `node` to the credentials helper (e.g. for policy checks) — `data`/ - // `source` are unused. - const executeData: IExecuteData = { data: {}, node: this.node, source: null }; - - return await this._getCredentials(type, executeData); + return await this._getRunlessCredentials(type); } getNodeWebhookUrl(name: WebhookType): string | undefined { diff --git a/packages/core/src/execution-engine/node-execution-context/load-options-context.ts b/packages/core/src/execution-engine/node-execution-context/load-options-context.ts index 9a33534f1f3..f5a739b0f3b 100644 --- a/packages/core/src/execution-engine/node-execution-context/load-options-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/load-options-context.ts @@ -1,7 +1,6 @@ import get from 'lodash/get'; import type { ICredentialDataDecryptedObject, - IExecuteData, IGetNodeParameterOptions, INode, ILoadOptionsFunctions, @@ -45,12 +44,7 @@ export class LoadOptionsContext extends NodeExecutionContext implements ILoadOpt } async getCredentials(type: string) { - // No real task run backs design-time parameter loading, so this only exists to - // surface `node` to the credentials helper (e.g. for policy checks) — `data`/`source` - // are unused. - const executeData: IExecuteData = { data: {}, node: this.node, source: null }; - - return await this._getCredentials(type, executeData); + return await this._getRunlessCredentials(type); } getCurrentNodeParameter( diff --git a/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts b/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts index a5d98e35469..c0f5544075a 100644 --- a/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts @@ -470,6 +470,18 @@ export abstract class NodeExecutionContext implements Omit( + type: string, + ) { + return await this._getCredentials(type, { data: {}, node: this.node, source: null }); + } + @Memoized protected get additionalKeys() { return getAdditionalKeys(this.additionalData, this.mode, this.runExecutionData); diff --git a/packages/core/src/execution-engine/node-execution-context/poll-context.ts b/packages/core/src/execution-engine/node-execution-context/poll-context.ts index c87f77d25ed..74dc0c0dc82 100644 --- a/packages/core/src/execution-engine/node-execution-context/poll-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/poll-context.ts @@ -3,7 +3,6 @@ import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; import type { ICredentialDataDecryptedObject, IDataObject, - IExecuteData, INode, IPollFunctions, IWorkflowExecuteAdditionalData, @@ -73,10 +72,6 @@ export class PollContext extends NodeExecutionContext implements IPollFunctions } async getCredentials(type: string) { - // No real task run backs a poll, so this only exists to surface `node` to - // the credentials helper (e.g. for policy checks) — `data`/`source` are unused. - const executeData: IExecuteData = { data: {}, node: this.node, source: null }; - - return await this._getCredentials(type, executeData); + return await this._getRunlessCredentials(type); } } diff --git a/packages/core/src/execution-engine/node-execution-context/trigger-context.ts b/packages/core/src/execution-engine/node-execution-context/trigger-context.ts index d6cd0783c8e..22c98a79508 100644 --- a/packages/core/src/execution-engine/node-execution-context/trigger-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/trigger-context.ts @@ -1,7 +1,6 @@ import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; import type { ICredentialDataDecryptedObject, - IExecuteData, INode, ITriggerFunctions, IWorkflowExecuteAdditionalData, @@ -66,10 +65,6 @@ export class TriggerContext extends NodeExecutionContext implements ITriggerFunc } async getCredentials(type: string) { - // No real task run backs a trigger, so this only exists to surface `node` to - // the credentials helper (e.g. for policy checks) — `data`/`source` are unused. - const executeData: IExecuteData = { data: {}, node: this.node, source: null }; - - return await this._getCredentials(type, executeData); + return await this._getRunlessCredentials(type); } } diff --git a/packages/core/src/execution-engine/node-execution-context/webhook-context.ts b/packages/core/src/execution-engine/node-execution-context/webhook-context.ts index 3f9f28d27b0..901a9458de9 100644 --- a/packages/core/src/execution-engine/node-execution-context/webhook-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/webhook-context.ts @@ -100,11 +100,7 @@ export class WebhookContext extends NodeExecutionContext implements IWebhookFunc } async getCredentials(type: string) { - // No real task run backs a webhook call, so this only exists to surface `node` - // to the credentials helper (e.g. for policy checks) — `data`/`source` are unused. - const executeData: IExecuteData = { data: {}, node: this.node, source: null }; - - return await this._getCredentials(type, executeData); + return await this._getRunlessCredentials(type); } getBodyData() {