mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
fix: Apply credential overwrites recursively (#5072)
This ensures that overwrites defined for a parent credential type also applies to all credentials extending it.
This commit is contained in:
parent
f1184ccab5
commit
5d746c4a83
|
@ -84,6 +84,7 @@
|
||||||
"@types/lodash.set": "^4.3.6",
|
"@types/lodash.set": "^4.3.6",
|
||||||
"@types/lodash.split": "^4.4.7",
|
"@types/lodash.split": "^4.4.7",
|
||||||
"@types/lodash.unionby": "^4.8.7",
|
"@types/lodash.unionby": "^4.8.7",
|
||||||
|
"@types/lodash.uniq": "^4.5.7",
|
||||||
"@types/lodash.uniqby": "^4.7.7",
|
"@types/lodash.uniqby": "^4.7.7",
|
||||||
"@types/lodash.unset": "^4.5.7",
|
"@types/lodash.unset": "^4.5.7",
|
||||||
"@types/parseurl": "^1.3.1",
|
"@types/parseurl": "^1.3.1",
|
||||||
|
@ -156,6 +157,7 @@
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"lodash.split": "^4.4.2",
|
"lodash.split": "^4.4.2",
|
||||||
"lodash.unionby": "^4.8.0",
|
"lodash.unionby": "^4.8.0",
|
||||||
|
"lodash.uniq": "^4.5.0",
|
||||||
"lodash.uniqby": "^4.7.0",
|
"lodash.uniqby": "^4.7.0",
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
"luxon": "^3.1.0",
|
"luxon": "^3.1.0",
|
||||||
|
|
|
@ -8,7 +8,9 @@ import type {
|
||||||
import { RESPONSE_ERROR_MESSAGES } from './constants';
|
import { RESPONSE_ERROR_MESSAGES } from './constants';
|
||||||
|
|
||||||
class CredentialTypesClass implements ICredentialTypes {
|
class CredentialTypesClass implements ICredentialTypes {
|
||||||
constructor(private nodesAndCredentials: INodesAndCredentials) {}
|
constructor(private nodesAndCredentials: INodesAndCredentials) {
|
||||||
|
nodesAndCredentials.credentialTypes = this;
|
||||||
|
}
|
||||||
|
|
||||||
recognizes(type: string) {
|
recognizes(type: string) {
|
||||||
return type in this.knownCredentials || type in this.loadedCredentials;
|
return type in this.knownCredentials || type in this.loadedCredentials;
|
||||||
|
@ -22,6 +24,22 @@ class CredentialTypesClass implements ICredentialTypes {
|
||||||
return this.knownCredentials[type]?.nodesToTestWith ?? [];
|
return this.knownCredentials[type]?.nodesToTestWith ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all parent types of the given credential type
|
||||||
|
*/
|
||||||
|
getParentTypes(typeName: string): string[] {
|
||||||
|
const credentialType = this.getByName(typeName);
|
||||||
|
if (credentialType?.extends === undefined) return [];
|
||||||
|
|
||||||
|
const types: string[] = [];
|
||||||
|
credentialType.extends.forEach((type: string) => {
|
||||||
|
types.push(type);
|
||||||
|
types.push(...this.getParentTypes(type));
|
||||||
|
});
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
private getCredential(type: string): LoadedClass<ICredentialType> {
|
private getCredential(type: string): LoadedClass<ICredentialType> {
|
||||||
const loadedCredentials = this.loadedCredentials;
|
const loadedCredentials = this.loadedCredentials;
|
||||||
if (type in loadedCredentials) {
|
if (type in loadedCredentials) {
|
||||||
|
|
|
@ -247,18 +247,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
* Returns all parent types of the given credential type
|
* Returns all parent types of the given credential type
|
||||||
*/
|
*/
|
||||||
getParentTypes(typeName: string): string[] {
|
getParentTypes(typeName: string): string[] {
|
||||||
const credentialType = this.credentialTypes.getByName(typeName);
|
return this.credentialTypes.getParentTypes(typeName);
|
||||||
|
|
||||||
if (credentialType === undefined || credentialType.extends === undefined) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let types: string[] = [];
|
|
||||||
credentialType.extends.forEach((type: string) => {
|
|
||||||
types = [...types, type, ...this.getParentTypes(type)];
|
|
||||||
});
|
|
||||||
|
|
||||||
return types;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -86,8 +86,13 @@ class CredentialsOverwritesClass {
|
||||||
return overwrites;
|
return overwrites;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get(type: string): ICredentialDataDecryptedObject | undefined {
|
private get(name: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return this.overwriteData[type];
|
const parentTypes = this.credentialTypes.getParentTypes(name);
|
||||||
|
return [name, ...parentTypes]
|
||||||
|
.reverse()
|
||||||
|
.map((type) => this.overwriteData[type])
|
||||||
|
.filter((type) => !!type)
|
||||||
|
.reduce((acc, current) => Object.assign(acc, current), {});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll(): ICredentialsOverwrite {
|
getAll(): ICredentialsOverwrite {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import uniq from 'lodash.uniq';
|
||||||
import {
|
import {
|
||||||
CUSTOM_EXTENSION_ENV,
|
CUSTOM_EXTENSION_ENV,
|
||||||
UserSettings,
|
UserSettings,
|
||||||
|
@ -8,6 +9,7 @@ import {
|
||||||
Types,
|
Types,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
import type {
|
import type {
|
||||||
|
ICredentialTypes,
|
||||||
ILogger,
|
ILogger,
|
||||||
INodesAndCredentials,
|
INodesAndCredentials,
|
||||||
KnownNodesAndCredentials,
|
KnownNodesAndCredentials,
|
||||||
|
@ -46,6 +48,8 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||||
|
|
||||||
includeNodes = config.getEnv('nodes.include');
|
includeNodes = config.getEnv('nodes.include');
|
||||||
|
|
||||||
|
credentialTypes: ICredentialTypes;
|
||||||
|
|
||||||
logger: ILogger;
|
logger: ILogger;
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -68,8 +72,21 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||||
async generateTypesForFrontend() {
|
async generateTypesForFrontend() {
|
||||||
const credentialsOverwrites = CredentialsOverwrites().getAll();
|
const credentialsOverwrites = CredentialsOverwrites().getAll();
|
||||||
for (const credential of this.types.credentials) {
|
for (const credential of this.types.credentials) {
|
||||||
|
const overwrittenProperties = [];
|
||||||
|
this.credentialTypes
|
||||||
|
.getParentTypes(credential.name)
|
||||||
|
.reverse()
|
||||||
|
.map((name) => credentialsOverwrites[name])
|
||||||
|
.forEach((overwrite) => {
|
||||||
|
if (overwrite) overwrittenProperties.push(...Object.keys(overwrite));
|
||||||
|
});
|
||||||
|
|
||||||
if (credential.name in credentialsOverwrites) {
|
if (credential.name in credentialsOverwrites) {
|
||||||
credential.__overwrittenProperties = Object.keys(credentialsOverwrites[credential.name]);
|
overwrittenProperties.push(...Object.keys(credentialsOverwrites[credential.name]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overwrittenProperties.length) {
|
||||||
|
credential.__overwrittenProperties = uniq(overwrittenProperties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import set from 'lodash.set';
|
||||||
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
ICredentialTypes,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
INode,
|
INode,
|
||||||
|
@ -71,6 +72,7 @@ import { eventBusRouter } from '@/eventbus/eventBusRoutes';
|
||||||
const loadNodesAndCredentials: INodesAndCredentials = {
|
const loadNodesAndCredentials: INodesAndCredentials = {
|
||||||
loaded: { nodes: {}, credentials: {} },
|
loaded: { nodes: {}, credentials: {} },
|
||||||
known: { nodes: {}, credentials: {} },
|
known: { nodes: {}, credentials: {} },
|
||||||
|
credentialTypes: {} as ICredentialTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockNodeTypes = NodeTypes(loadNodesAndCredentials);
|
const mockNodeTypes = NodeTypes(loadNodesAndCredentials);
|
||||||
|
@ -160,7 +162,7 @@ export async function initTestServer({
|
||||||
* Pre-requisite: Mock the telemetry module before calling.
|
* Pre-requisite: Mock the telemetry module before calling.
|
||||||
*/
|
*/
|
||||||
export function initTestTelemetry() {
|
export function initTestTelemetry() {
|
||||||
void InternalHooksManager.init('test-instance-id', 'test-version', mockNodeTypes);
|
void InternalHooksManager.init('test-instance-id', mockNodeTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,4 +43,5 @@ const mockNodesAndCredentials = (): INodesAndCredentials => ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
known: { nodes: {}, credentials: {} },
|
known: { nodes: {}, credentials: {} },
|
||||||
|
credentialTypes: {} as ICredentialTypes,
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
IAuthenticateGeneric,
|
IAuthenticateGeneric,
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
ICredentialTypes,
|
||||||
IHttpRequestOptions,
|
IHttpRequestOptions,
|
||||||
INode,
|
INode,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
|
@ -16,6 +17,7 @@ const TEST_ENCRYPTION_KEY = 'test';
|
||||||
const mockNodesAndCredentials: INodesAndCredentials = {
|
const mockNodesAndCredentials: INodesAndCredentials = {
|
||||||
loaded: { nodes: {}, credentials: {} },
|
loaded: { nodes: {}, credentials: {} },
|
||||||
known: { nodes: {}, credentials: {} },
|
known: { nodes: {}, credentials: {} },
|
||||||
|
credentialTypes: {} as ICredentialTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('CredentialsHelper', () => {
|
describe('CredentialsHelper', () => {
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { INodeTypeData, INodeTypes, SubworkflowOperationError, Workflow } from 'n8n-workflow';
|
import {
|
||||||
|
ICredentialTypes,
|
||||||
|
INodeTypeData,
|
||||||
|
INodeTypes,
|
||||||
|
SubworkflowOperationError,
|
||||||
|
Workflow,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
|
@ -35,6 +41,7 @@ beforeAll(async () => {
|
||||||
credentials: {},
|
credentials: {},
|
||||||
},
|
},
|
||||||
known: { nodes: {}, credentials: {} },
|
known: { nodes: {}, credentials: {} },
|
||||||
|
credentialTypes: {} as ICredentialTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
credentialOwnerRole = await testDb.getCredentialOwnerRole();
|
credentialOwnerRole = await testDb.getCredentialOwnerRole();
|
||||||
|
|
|
@ -336,6 +336,7 @@ export interface ICredentialTypes {
|
||||||
recognizes(credentialType: string): boolean;
|
recognizes(credentialType: string): boolean;
|
||||||
getByName(credentialType: string): ICredentialType;
|
getByName(credentialType: string): ICredentialType;
|
||||||
getNodeTypesToTestWith(type: string): string[];
|
getNodeTypesToTestWith(type: string): string[];
|
||||||
|
getParentTypes(typeName: string): string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// The way the credentials get saved in the database (data encrypted)
|
// The way the credentials get saved in the database (data encrypted)
|
||||||
|
@ -1492,6 +1493,7 @@ export type LoadedNodesAndCredentials = {
|
||||||
export interface INodesAndCredentials {
|
export interface INodesAndCredentials {
|
||||||
known: KnownNodesAndCredentials;
|
known: KnownNodesAndCredentials;
|
||||||
loaded: LoadedNodesAndCredentials;
|
loaded: LoadedNodesAndCredentials;
|
||||||
|
credentialTypes: ICredentialTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRun {
|
export interface IRun {
|
||||||
|
|
|
@ -122,6 +122,7 @@ importers:
|
||||||
'@types/lodash.set': ^4.3.6
|
'@types/lodash.set': ^4.3.6
|
||||||
'@types/lodash.split': ^4.4.7
|
'@types/lodash.split': ^4.4.7
|
||||||
'@types/lodash.unionby': ^4.8.7
|
'@types/lodash.unionby': ^4.8.7
|
||||||
|
'@types/lodash.uniq': ^4.5.7
|
||||||
'@types/lodash.uniqby': ^4.7.7
|
'@types/lodash.uniqby': ^4.7.7
|
||||||
'@types/lodash.unset': ^4.5.7
|
'@types/lodash.unset': ^4.5.7
|
||||||
'@types/parseurl': ^1.3.1
|
'@types/parseurl': ^1.3.1
|
||||||
|
@ -179,6 +180,7 @@ importers:
|
||||||
lodash.set: ^4.3.2
|
lodash.set: ^4.3.2
|
||||||
lodash.split: ^4.4.2
|
lodash.split: ^4.4.2
|
||||||
lodash.unionby: ^4.8.0
|
lodash.unionby: ^4.8.0
|
||||||
|
lodash.uniq: ^4.5.0
|
||||||
lodash.uniqby: ^4.7.0
|
lodash.uniqby: ^4.7.0
|
||||||
lodash.unset: ^4.5.2
|
lodash.unset: ^4.5.2
|
||||||
luxon: ^3.1.0
|
luxon: ^3.1.0
|
||||||
|
@ -271,6 +273,7 @@ importers:
|
||||||
lodash.set: 4.3.2
|
lodash.set: 4.3.2
|
||||||
lodash.split: 4.4.2
|
lodash.split: 4.4.2
|
||||||
lodash.unionby: 4.8.0
|
lodash.unionby: 4.8.0
|
||||||
|
lodash.uniq: 4.5.0
|
||||||
lodash.uniqby: 4.7.0
|
lodash.uniqby: 4.7.0
|
||||||
lodash.unset: 4.5.2
|
lodash.unset: 4.5.2
|
||||||
luxon: 3.1.1
|
luxon: 3.1.1
|
||||||
|
@ -332,6 +335,7 @@ importers:
|
||||||
'@types/lodash.set': 4.3.7
|
'@types/lodash.set': 4.3.7
|
||||||
'@types/lodash.split': 4.4.7
|
'@types/lodash.split': 4.4.7
|
||||||
'@types/lodash.unionby': 4.8.7
|
'@types/lodash.unionby': 4.8.7
|
||||||
|
'@types/lodash.uniq': 4.5.7
|
||||||
'@types/lodash.uniqby': 4.7.7
|
'@types/lodash.uniqby': 4.7.7
|
||||||
'@types/lodash.unset': 4.5.7
|
'@types/lodash.unset': 4.5.7
|
||||||
'@types/parseurl': 1.3.1
|
'@types/parseurl': 1.3.1
|
||||||
|
@ -5929,6 +5933,12 @@ packages:
|
||||||
'@types/lodash': 4.14.186
|
'@types/lodash': 4.14.186
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/lodash.uniq/4.5.7:
|
||||||
|
resolution: {integrity: sha512-qg7DeAbdZMi6DGvCxThlJycykLLhETrJrQZ6F2KaZ+o0sNK1qRHz46lgNA+nHHjwrmA2a91DyiZTp3ey3m1rEw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.14.186
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/lodash.uniqby/4.7.7:
|
/@types/lodash.uniqby/4.7.7:
|
||||||
resolution: {integrity: sha512-sv2g6vkCIvEUsK5/Vq17haoZaisfj2EWW8mP7QWlnKi6dByoNmeuHDDXHR7sabuDqwO4gvU7ModIL22MmnOocg==}
|
resolution: {integrity: sha512-sv2g6vkCIvEUsK5/Vq17haoZaisfj2EWW8mP7QWlnKi6dByoNmeuHDDXHR7sabuDqwO4gvU7ModIL22MmnOocg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue