fix(editor): Resolve $vars and $secrets in expressions in credentials fields (#9289)

This commit is contained in:
Iván Ovejero 2024-05-03 14:39:31 +02:00 committed by GitHub
parent 6ab3781570
commit d92f994913
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 22 deletions

View file

@ -202,14 +202,18 @@ export default defineComponent({
? this.modelValue.value ? this.modelValue.value
: this.modelValue; : this.modelValue;
if (!this.activeNode || !this.isValueExpression || typeof value !== 'string') { if (
!this.isForCredential &&
(!this.activeNode || !this.isValueExpression || typeof value !== 'string')
) {
return { ok: false, error: new Error() }; return { ok: false, error: new Error() };
} }
try { try {
let opts; let opts = { isForCredential: this.isForCredential };
if (this.ndvStore.isInputParentOfActiveNode) { if (this.ndvStore.isInputParentOfActiveNode) {
opts = { opts = {
...opts,
targetItem: this.targetItem ?? undefined, targetItem: this.targetItem ?? undefined,
inputNodeName: this.ndvStore.ndvInputNodeName, inputNodeName: this.ndvStore.ndvInputNodeName,
inputRunIndex: this.ndvStore.ndvInputRunIndex, inputRunIndex: this.ndvStore.ndvInputRunIndex,

View file

@ -82,16 +82,51 @@ export function resolveParameter(
inputRunIndex?: number; inputRunIndex?: number;
inputBranchIndex?: number; inputBranchIndex?: number;
additionalKeys?: IWorkflowDataProxyAdditionalKeys; additionalKeys?: IWorkflowDataProxyAdditionalKeys;
isForCredential?: boolean;
} = {}, } = {},
): IDataObject | null { ): IDataObject | null {
let itemIndex = opts?.targetItem?.itemIndex || 0; let itemIndex = opts?.targetItem?.itemIndex || 0;
const workflow = getCurrentWorkflow();
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: 'test',
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
resumeFormUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
},
$vars: useEnvironmentsStore().variablesAsObject,
// deprecated
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
...opts.additionalKeys,
};
if (opts.isForCredential) {
// node-less expression resolution
return workflow.expression.getParameterValue(
parameter,
null,
0,
itemIndex,
'',
[],
'manual',
additionalKeys,
undefined,
false,
undefined,
'',
) as IDataObject;
}
const inputName = NodeConnectionType.Main; const inputName = NodeConnectionType.Main;
const activeNode = useNDVStore().activeNode; const activeNode = useNDVStore().activeNode;
let contextNode = activeNode; let contextNode = activeNode;
const workflow = getCurrentWorkflow();
if (activeNode) { if (activeNode) {
contextNode = workflow.getParentMainInputNode(activeNode); contextNode = workflow.getParentMainInputNode(activeNode);
} }
@ -159,22 +194,6 @@ export function resolveParameter(
_connectionInputData = []; _connectionInputData = [];
} }
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: 'test',
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
resumeFormUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
},
$vars: useEnvironmentsStore().variablesAsObject,
// deprecated
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
...opts.additionalKeys,
};
if (activeNode?.type === HTTP_REQUEST_NODE_TYPE) { if (activeNode?.type === HTTP_REQUEST_NODE_TYPE) {
const EMPTY_RESPONSE = { statusCode: 200, headers: {}, body: {} }; const EMPTY_RESPONSE = { statusCode: 200, headers: {}, body: {} };
const EMPTY_REQUEST = { headers: {}, body: {}, qs: {} }; const EMPTY_REQUEST = { headers: {}, body: {}, qs: {} };
@ -793,6 +812,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
inputBranchIndex?: number; inputBranchIndex?: number;
c?: number; c?: number;
additionalKeys?: IWorkflowDataProxyAdditionalKeys; additionalKeys?: IWorkflowDataProxyAdditionalKeys;
isForCredential?: boolean;
} = {}, } = {},
stringifyObject = true, stringifyObject = true,
) { ) {

View file

@ -50,7 +50,7 @@ import { defineComponent } from 'vue';
import ResourcesListLayout from '@/components/layouts/ResourcesListLayout.vue'; import ResourcesListLayout from '@/components/layouts/ResourcesListLayout.vue';
import CredentialCard from '@/components/CredentialCard.vue'; import CredentialCard from '@/components/CredentialCard.vue';
import type { ICredentialType } from 'n8n-workflow'; import type { ICredentialType } from 'n8n-workflow';
import { CREDENTIAL_SELECT_MODAL_KEY } from '@/constants'; import { CREDENTIAL_SELECT_MODAL_KEY, EnterpriseEditionFeature } from '@/constants';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useUsersStore } from '@/stores/users.store'; import { useUsersStore } from '@/stores/users.store';
@ -58,6 +58,8 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useCredentialsStore } from '@/stores/credentials.store'; import { useCredentialsStore } from '@/stores/credentials.store';
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store'; import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
import { useSourceControlStore } from '@/stores/sourceControl.store'; import { useSourceControlStore } from '@/stores/sourceControl.store';
import useEnvironmentsStore from '@/stores/environments.ee.store';
import { useSettingsStore } from '@/stores/settings.store';
type IResourcesListLayoutInstance = InstanceType<typeof ResourcesListLayout>; type IResourcesListLayoutInstance = InstanceType<typeof ResourcesListLayout>;
@ -123,11 +125,16 @@ export default defineComponent({
}); });
}, },
async initialize() { async initialize() {
const isVarsEnabled = useSettingsStore().isEnterpriseFeatureEnabled(
EnterpriseEditionFeature.Variables,
);
const loadPromises = [ const loadPromises = [
this.credentialsStore.fetchAllCredentials(), this.credentialsStore.fetchAllCredentials(),
this.credentialsStore.fetchCredentialTypes(false), this.credentialsStore.fetchCredentialTypes(false),
this.externalSecretsStore.fetchAllSecrets(), this.externalSecretsStore.fetchAllSecrets(),
this.nodeTypesStore.loadNodeTypesIfNotLoaded(), this.nodeTypesStore.loadNodeTypesIfNotLoaded(),
isVarsEnabled ? useEnvironmentsStore().fetchAllVariables() : Promise.resolve(), // for expression resolution
]; ];
await Promise.all(loadPromises); await Promise.all(loadPromises);

View file

@ -164,7 +164,9 @@ export class WorkflowDataProxy {
const that = this; const that = this;
const node = this.workflow.nodes[nodeName]; const node = this.workflow.nodes[nodeName];
return new Proxy(node.parameters, { // `node` is `undefined` only in expressions in credentials
return new Proxy(node?.parameters ?? {}, {
has: () => true, has: () => true,
ownKeys(target) { ownKeys(target) {
return Reflect.ownKeys(target); return Reflect.ownKeys(target);