mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 12:35:46 +03:00
⚡ Do not save credential default values to DB #PROD-52
This commit is contained in:
parent
09a4b44875
commit
cfe6e72440
@ -5,16 +5,15 @@ import {
|
||||
|
||||
import {
|
||||
CredentialsOverwrites,
|
||||
ICredentialsTypeData,
|
||||
} from './';
|
||||
|
||||
class CredentialTypesClass implements ICredentialTypesInterface {
|
||||
|
||||
credentialTypes: {
|
||||
[key: string]: ICredentialType
|
||||
} = {};
|
||||
credentialTypes: ICredentialsTypeData = {};
|
||||
|
||||
|
||||
async init(credentialTypes: { [key: string]: ICredentialType }): Promise<void> {
|
||||
async init(credentialTypes: ICredentialsTypeData): Promise<void> {
|
||||
this.credentialTypes = credentialTypes;
|
||||
|
||||
// Load the credentials overwrites if any exist
|
||||
|
@ -5,10 +5,13 @@ import {
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsHelper,
|
||||
INodeParameters,
|
||||
NodeHelpers,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
CredentialsOverwrites,
|
||||
CredentialTypes,
|
||||
Db,
|
||||
ICredentialsDb,
|
||||
} from './';
|
||||
@ -42,15 +45,51 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
*
|
||||
* @param {string} name Name of the credentials to return data of
|
||||
* @param {string} type Type of the credentials to return data of
|
||||
* @param {boolean} [raw] Return the data as supplied without defaults or overwrites
|
||||
* @returns {ICredentialDataDecryptedObject}
|
||||
* @memberof CredentialsHelper
|
||||
*/
|
||||
getDecrypted(name: string, type: string): ICredentialDataDecryptedObject {
|
||||
getDecrypted(name: string, type: string, raw?: boolean): ICredentialDataDecryptedObject {
|
||||
const credentials = this.getCredentials(name, type);
|
||||
|
||||
const decryptedDataOriginal = credentials.getData(this.encryptionKey);
|
||||
|
||||
if (raw === true) {
|
||||
return decryptedDataOriginal;
|
||||
}
|
||||
|
||||
return this.applyDefaultsAndOverwrites(decryptedDataOriginal, type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies credential default data and overwrites
|
||||
*
|
||||
* @param {ICredentialDataDecryptedObject} decryptedDataOriginal The credential data to overwrite data on
|
||||
* @param {string} type Type of the credentials to overwrite data of
|
||||
* @returns {ICredentialDataDecryptedObject}
|
||||
* @memberof CredentialsHelper
|
||||
*/
|
||||
applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string): ICredentialDataDecryptedObject {
|
||||
const credentialTypes = CredentialTypes();
|
||||
const credentialType = credentialTypes.getByName(type);
|
||||
|
||||
if (credentialType === undefined) {
|
||||
throw new Error(`The credential type "${type}" is not known and can so not be decrypted.`);
|
||||
}
|
||||
|
||||
// Add the default credential values
|
||||
const decryptedData = NodeHelpers.getNodeParameters(credentialType.properties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject;
|
||||
|
||||
if (decryptedDataOriginal.oauthTokenData !== undefined) {
|
||||
// The OAuth data gets removed as it is not defined specifically as a parameter
|
||||
// on the credentials so add it back in case it was set
|
||||
decryptedData.oauthTokenData = decryptedDataOriginal.oauthTokenData;
|
||||
}
|
||||
|
||||
// Load and apply the credentials overwrites if any exist
|
||||
const credentialsOverwrites = CredentialsOverwrites();
|
||||
return credentialsOverwrites.applyOverwrite(credentials.type, credentials.getData(this.encryptionKey));
|
||||
return credentialsOverwrites.applyOverwrite(type, decryptedData);
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,7 +12,14 @@ class CredentialsOverwritesClass {
|
||||
|
||||
private overwriteData: ICredentialsOverwrite = {};
|
||||
|
||||
async init() {
|
||||
async init(overwriteData?: ICredentialsOverwrite) {
|
||||
if (overwriteData !== undefined) {
|
||||
// If data is already given it can directly be set instead of
|
||||
// loaded from environment
|
||||
this.overwriteData = overwriteData;
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await GenericHelpers.getConfigValue('credentials.overwrite') as string;
|
||||
|
||||
try {
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialsEncrypted,
|
||||
ICredentialType,
|
||||
IDataObject,
|
||||
IExecutionError,
|
||||
IRun,
|
||||
@ -36,6 +37,10 @@ export interface ICustomRequest extends Request {
|
||||
parsedUrl: Url | undefined;
|
||||
}
|
||||
|
||||
export interface ICredentialsTypeData {
|
||||
[key: string]: ICredentialType;
|
||||
}
|
||||
|
||||
export interface ICredentialsOverwrite {
|
||||
[key: string]: ICredentialDataDecryptedObject;
|
||||
}
|
||||
@ -350,7 +355,10 @@ export interface IWorkflowExecutionDataProcess {
|
||||
workflowData: IWorkflowBase;
|
||||
}
|
||||
|
||||
|
||||
export interface IWorkflowExecutionDataProcessWithExecution extends IWorkflowExecutionDataProcess {
|
||||
credentialsOverwrite: ICredentialsOverwrite;
|
||||
credentialsTypeData: ICredentialsTypeData;
|
||||
executionId: string;
|
||||
nodeTypeData: ITransferNodeTypes;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import * as csrf from 'csrf';
|
||||
import {
|
||||
ActiveExecutions,
|
||||
ActiveWorkflowRunner,
|
||||
CredentialsOverwrites,
|
||||
CredentialsHelper,
|
||||
CredentialTypes,
|
||||
Db,
|
||||
IActivationError,
|
||||
@ -51,7 +51,6 @@ import {
|
||||
WorkflowCredentials,
|
||||
WebhookHelpers,
|
||||
WorkflowExecuteAdditionalData,
|
||||
WorkflowHelpers,
|
||||
WorkflowRunner,
|
||||
GenericHelpers,
|
||||
} from './';
|
||||
@ -63,6 +62,7 @@ import {
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsEncrypted,
|
||||
ICredentialType,
|
||||
IDataObject,
|
||||
INodeCredentials,
|
||||
@ -70,6 +70,7 @@ import {
|
||||
INodeParameters,
|
||||
INodePropertyOptions,
|
||||
IRunData,
|
||||
IWorkflowCredentials,
|
||||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
@ -913,12 +914,15 @@ class App {
|
||||
throw new Error('No encryption key got found to decrypt the credentials!');
|
||||
}
|
||||
|
||||
const credentials = new Credentials(result.name, result.type, result.nodesAccess, result.data);
|
||||
const savedCredentialsData = credentials.getData(encryptionKey);
|
||||
|
||||
// Load the credentials overwrites if any exist
|
||||
const credentialsOverwrites = CredentialsOverwrites();
|
||||
const oauthCredentials = credentialsOverwrites.applyOverwrite(credentials.type, savedCredentialsData);
|
||||
// Decrypt the currently saved credentials
|
||||
const workflowCredentials: IWorkflowCredentials = {
|
||||
[result.type as string]: {
|
||||
[result.name as string]: result as ICredentialsEncrypted,
|
||||
},
|
||||
};
|
||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
||||
|
||||
const token = new csrf();
|
||||
// Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR
|
||||
@ -939,8 +943,11 @@ class App {
|
||||
state: stateEncodedStr,
|
||||
});
|
||||
|
||||
savedCredentialsData.csrfSecret = csrfSecret;
|
||||
credentials.setData(savedCredentialsData, encryptionKey);
|
||||
// Encrypt the data
|
||||
const credentials = new Credentials(result.name, result.type, result.nodesAccess);
|
||||
decryptedDataOriginal.csrfSecret = csrfSecret;
|
||||
|
||||
credentials.setData(decryptedDataOriginal, encryptionKey);
|
||||
const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb;
|
||||
|
||||
// Add special database related data
|
||||
@ -992,15 +999,18 @@ class App {
|
||||
return ResponseHelper.sendErrorResponse(res, errorResponse);
|
||||
}
|
||||
|
||||
const credentials = new Credentials(result.name, result.type, result.nodesAccess, result.data);
|
||||
const savedCredentialsData = credentials.getData(encryptionKey!);
|
||||
|
||||
// Load the credentials overwrites if any exist
|
||||
const credentialsOverwrites = CredentialsOverwrites();
|
||||
const oauthCredentials = credentialsOverwrites.applyOverwrite(credentials.type, savedCredentialsData);
|
||||
// Decrypt the currently saved credentials
|
||||
const workflowCredentials: IWorkflowCredentials = {
|
||||
[result.type as string]: {
|
||||
[result.name as string]: result as ICredentialsEncrypted,
|
||||
},
|
||||
};
|
||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
||||
|
||||
const token = new csrf();
|
||||
if (oauthCredentials.csrfSecret === undefined || !token.verify(oauthCredentials.csrfSecret as string, state.token)) {
|
||||
if (decryptedDataOriginal.csrfSecret === undefined || !token.verify(decryptedDataOriginal.csrfSecret as string, state.token)) {
|
||||
const errorResponse = new ResponseHelper.ResponseError('The OAuth2 callback state is invalid!', undefined, 404);
|
||||
return ResponseHelper.sendErrorResponse(res, errorResponse);
|
||||
}
|
||||
@ -1032,18 +1042,19 @@ class App {
|
||||
return ResponseHelper.sendErrorResponse(res, errorResponse);
|
||||
}
|
||||
|
||||
if (savedCredentialsData.oauthTokenData) {
|
||||
if (decryptedDataOriginal.oauthTokenData) {
|
||||
// Only overwrite supplied data as some providers do for example just return the
|
||||
// refresh_token on the very first request and not on subsequent ones.
|
||||
Object.assign(savedCredentialsData.oauthTokenData, oauthToken.data);
|
||||
Object.assign(decryptedDataOriginal.oauthTokenData, oauthToken.data);
|
||||
} else {
|
||||
// No data exists so simply set
|
||||
savedCredentialsData.oauthTokenData = oauthToken.data;
|
||||
decryptedDataOriginal.oauthTokenData = oauthToken.data;
|
||||
}
|
||||
|
||||
_.unset(savedCredentialsData, 'csrfSecret');
|
||||
_.unset(decryptedDataOriginal, 'csrfSecret');
|
||||
|
||||
credentials.setData(savedCredentialsData, encryptionKey);
|
||||
const credentials = new Credentials(result.name, result.type, result.nodesAccess);
|
||||
credentials.setData(decryptedDataOriginal, encryptionKey);
|
||||
const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb;
|
||||
// Add special database related data
|
||||
newCredentialsData.updatedAt = this.getCurrentDate();
|
||||
|
@ -1,5 +1,9 @@
|
||||
import {
|
||||
ActiveExecutions,
|
||||
CredentialsOverwrites,
|
||||
CredentialTypes,
|
||||
ICredentialsOverwrite,
|
||||
ICredentialsTypeData,
|
||||
IProcessMessageDataHook,
|
||||
ITransferNodeTypes,
|
||||
IWorkflowExecutionDataProcess,
|
||||
@ -31,12 +35,14 @@ import { fork } from 'child_process';
|
||||
|
||||
export class WorkflowRunner {
|
||||
activeExecutions: ActiveExecutions.ActiveExecutions;
|
||||
credentialsOverwrites: ICredentialsOverwrite;
|
||||
push: Push.Push;
|
||||
|
||||
|
||||
constructor() {
|
||||
this.push = Push.getInstance();
|
||||
this.activeExecutions = ActiveExecutions.getInstance();
|
||||
this.credentialsOverwrites = CredentialsOverwrites().getAll();
|
||||
}
|
||||
|
||||
|
||||
@ -192,8 +198,16 @@ export class WorkflowRunner {
|
||||
nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes);
|
||||
}
|
||||
|
||||
const credentialTypes = CredentialTypes();
|
||||
const credentialTypeData: ICredentialsTypeData = {};
|
||||
for (const credentialType of Object.keys(data.credentials)) {
|
||||
credentialTypeData[credentialType] = credentialTypes.getByName(credentialType);
|
||||
}
|
||||
|
||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId;
|
||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData;
|
||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsOverwrite = this.credentialsOverwrites;
|
||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsTypeData = credentialTypeData; // TODO: Still needs correct value
|
||||
|
||||
const workflowHooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId);
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
|
||||
import {
|
||||
CredentialsOverwrites,
|
||||
CredentialTypes,
|
||||
IWorkflowExecutionDataProcessWithExecution,
|
||||
NodeTypes,
|
||||
WorkflowExecuteAdditionalData,
|
||||
@ -58,6 +60,14 @@ export class WorkflowRunnerProcess {
|
||||
const nodeTypes = NodeTypes();
|
||||
await nodeTypes.init(nodeTypesData);
|
||||
|
||||
// Init credential types the workflow uses (is needed to apply default values to credentials)
|
||||
const credentialTypes = CredentialTypes();
|
||||
await credentialTypes.init(inputData.credentialsTypeData);
|
||||
|
||||
// Load the credentials overwrites if any exist
|
||||
const credentialsOverwrites = CredentialsOverwrites();
|
||||
await credentialsOverwrites.init();
|
||||
|
||||
this.workflow = new Workflow({ id: this.data.workflowData.id as string | undefined, name: this.data.workflowData.name, nodes: this.data.workflowData!.nodes, connections: this.data.workflowData!.connections, active: this.data.workflowData!.active, nodeTypes, staticData: this.data.workflowData!.staticData, settings: this.data.workflowData!.settings});
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials);
|
||||
additionalData.hooks = this.getProcessForwardHooks();
|
||||
|
@ -55,7 +55,7 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-for="parameter in credentialProperties" :key="parameter.name">
|
||||
<el-row v-if="displayCredentialParameter(parameter)" class="parameter-wrapper">
|
||||
<el-row class="parameter-wrapper">
|
||||
<el-col :span="6" class="parameter-name">
|
||||
{{parameter.displayName}}:
|
||||
<el-tooltip placement="top" class="parameter-info" v-if="parameter.description" effect="light">
|
||||
@ -128,6 +128,7 @@ import {
|
||||
INodeParameters,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
NodeHelpers,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import ParameterInput from '@/components/ParameterInput.vue';
|
||||
@ -199,6 +200,9 @@ export default mixins(
|
||||
},
|
||||
credentialProperties (): INodeProperties[] {
|
||||
return this.credentialTypeData.properties.filter((propertyData: INodeProperties) => {
|
||||
if (!this.displayCredentialParameter(propertyData)) {
|
||||
return false;
|
||||
}
|
||||
return !this.credentialTypeData.__overwrittenProperties || !this.credentialTypeData.__overwrittenProperties.includes(propertyData.name);
|
||||
});
|
||||
},
|
||||
@ -283,7 +287,8 @@ export default mixins(
|
||||
name: this.name,
|
||||
type: (this.credentialTypeData as ICredentialType).name,
|
||||
nodesAccess,
|
||||
data: this.propertyValue,
|
||||
// Save only the none default data
|
||||
data: NodeHelpers.getNodeParameters(this.credentialTypeData.properties as INodeProperties[], this.propertyValue as INodeParameters, false, false),
|
||||
} as ICredentialsDecrypted;
|
||||
|
||||
let result;
|
||||
@ -402,7 +407,8 @@ export default mixins(
|
||||
name: this.name,
|
||||
type: (this.credentialTypeData as ICredentialType).name,
|
||||
nodesAccess,
|
||||
data: this.propertyValue,
|
||||
// Save only the none default data
|
||||
data: NodeHelpers.getNodeParameters(this.credentialTypeData.properties as INodeProperties[], this.propertyValue as INodeParameters, false, false),
|
||||
} as ICredentialsDecrypted;
|
||||
|
||||
let result;
|
||||
|
Loading…
Reference in New Issue
Block a user