Skip to content

Commit d92f994

Browse files
authored
fix(editor): Resolve $vars and $secrets in expressions in credentials fields (#9289)
1 parent 6ab3781 commit d92f994

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

packages/editor-ui/src/components/ParameterInputWrapper.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,18 @@ export default defineComponent({
202202
? this.modelValue.value
203203
: this.modelValue;
204204
205-
if (!this.activeNode || !this.isValueExpression || typeof value !== 'string') {
205+
if (
206+
!this.isForCredential &&
207+
(!this.activeNode || !this.isValueExpression || typeof value !== 'string')
208+
) {
206209
return { ok: false, error: new Error() };
207210
}
208211
209212
try {
210-
let opts;
213+
let opts = { isForCredential: this.isForCredential };
211214
if (this.ndvStore.isInputParentOfActiveNode) {
212215
opts = {
216+
...opts,
213217
targetItem: this.targetItem ?? undefined,
214218
inputNodeName: this.ndvStore.ndvInputNodeName,
215219
inputRunIndex: this.ndvStore.ndvInputRunIndex,

packages/editor-ui/src/composables/useWorkflowHelpers.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,51 @@ export function resolveParameter(
8282
inputRunIndex?: number;
8383
inputBranchIndex?: number;
8484
additionalKeys?: IWorkflowDataProxyAdditionalKeys;
85+
isForCredential?: boolean;
8586
} = {},
8687
): IDataObject | null {
8788
let itemIndex = opts?.targetItem?.itemIndex || 0;
8889

90+
const workflow = getCurrentWorkflow();
91+
92+
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
93+
$execution: {
94+
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
95+
mode: 'test',
96+
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
97+
resumeFormUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
98+
},
99+
$vars: useEnvironmentsStore().variablesAsObject,
100+
101+
// deprecated
102+
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
103+
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
104+
105+
...opts.additionalKeys,
106+
};
107+
108+
if (opts.isForCredential) {
109+
// node-less expression resolution
110+
return workflow.expression.getParameterValue(
111+
parameter,
112+
null,
113+
0,
114+
itemIndex,
115+
'',
116+
[],
117+
'manual',
118+
additionalKeys,
119+
undefined,
120+
false,
121+
undefined,
122+
'',
123+
) as IDataObject;
124+
}
125+
89126
const inputName = NodeConnectionType.Main;
90127
const activeNode = useNDVStore().activeNode;
91128
let contextNode = activeNode;
92129

93-
const workflow = getCurrentWorkflow();
94-
95130
if (activeNode) {
96131
contextNode = workflow.getParentMainInputNode(activeNode);
97132
}
@@ -159,22 +194,6 @@ export function resolveParameter(
159194
_connectionInputData = [];
160195
}
161196

162-
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
163-
$execution: {
164-
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
165-
mode: 'test',
166-
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
167-
resumeFormUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
168-
},
169-
$vars: useEnvironmentsStore().variablesAsObject,
170-
171-
// deprecated
172-
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
173-
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
174-
175-
...opts.additionalKeys,
176-
};
177-
178197
if (activeNode?.type === HTTP_REQUEST_NODE_TYPE) {
179198
const EMPTY_RESPONSE = { statusCode: 200, headers: {}, body: {} };
180199
const EMPTY_REQUEST = { headers: {}, body: {}, qs: {} };
@@ -793,6 +812,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
793812
inputBranchIndex?: number;
794813
c?: number;
795814
additionalKeys?: IWorkflowDataProxyAdditionalKeys;
815+
isForCredential?: boolean;
796816
} = {},
797817
stringifyObject = true,
798818
) {

packages/editor-ui/src/views/CredentialsView.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,16 @@ import { defineComponent } from 'vue';
5050
import ResourcesListLayout from '@/components/layouts/ResourcesListLayout.vue';
5151
import CredentialCard from '@/components/CredentialCard.vue';
5252
import type { ICredentialType } from 'n8n-workflow';
53-
import { CREDENTIAL_SELECT_MODAL_KEY } from '@/constants';
53+
import { CREDENTIAL_SELECT_MODAL_KEY, EnterpriseEditionFeature } from '@/constants';
5454
import { mapStores } from 'pinia';
5555
import { useUIStore } from '@/stores/ui.store';
5656
import { useUsersStore } from '@/stores/users.store';
5757
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
5858
import { useCredentialsStore } from '@/stores/credentials.store';
5959
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
6060
import { useSourceControlStore } from '@/stores/sourceControl.store';
61+
import useEnvironmentsStore from '@/stores/environments.ee.store';
62+
import { useSettingsStore } from '@/stores/settings.store';
6163
6264
type IResourcesListLayoutInstance = InstanceType<typeof ResourcesListLayout>;
6365
@@ -123,11 +125,16 @@ export default defineComponent({
123125
});
124126
},
125127
async initialize() {
128+
const isVarsEnabled = useSettingsStore().isEnterpriseFeatureEnabled(
129+
EnterpriseEditionFeature.Variables,
130+
);
131+
126132
const loadPromises = [
127133
this.credentialsStore.fetchAllCredentials(),
128134
this.credentialsStore.fetchCredentialTypes(false),
129135
this.externalSecretsStore.fetchAllSecrets(),
130136
this.nodeTypesStore.loadNodeTypesIfNotLoaded(),
137+
isVarsEnabled ? useEnvironmentsStore().fetchAllVariables() : Promise.resolve(), // for expression resolution
131138
];
132139
133140
await Promise.all(loadPromises);

packages/workflow/src/WorkflowDataProxy.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ export class WorkflowDataProxy {
164164
const that = this;
165165
const node = this.workflow.nodes[nodeName];
166166

167-
return new Proxy(node.parameters, {
167+
// `node` is `undefined` only in expressions in credentials
168+
169+
return new Proxy(node?.parameters ?? {}, {
168170
has: () => true,
169171
ownKeys(target) {
170172
return Reflect.ownKeys(target);

0 commit comments

Comments
 (0)