Abstract OAuth signing and make credentials extendable

This commit is contained in:
Jan Oberhauser 2020-01-13 20:46:58 -06:00
parent 740cb8a6fc
commit 8228b8505f
13 changed files with 283 additions and 15 deletions

View file

@ -70,7 +70,7 @@ export interface ICredentialsBase {
updatedAt: Date; updatedAt: Date;
} }
export interface ICredentialsDb extends ICredentialsBase, ICredentialsEncrypted{ export interface ICredentialsDb extends ICredentialsBase, ICredentialsEncrypted {
id: number | string | ObjectID; id: number | string | ObjectID;
} }

View file

@ -758,11 +758,7 @@ class App {
const findQuery = {} as FindManyOptions; const findQuery = {} as FindManyOptions;
// Make sure the variable has an expected value // Make sure the variable has an expected value
if (req.query.includeData === 'true') { req.query.includeData = (req.query.includeData === 'true' || req.query.includeData === true);
req.query.includeData = true;
} else {
req.query.includeData = false;
}
if (req.query.includeData !== true) { if (req.query.includeData !== true) {
// Return only the fields we need // Return only the fields we need

View file

@ -1,5 +1,6 @@
import { import {
Db, Db,
ICredentialsDb,
IExecutionDb, IExecutionDb,
IExecutionFlattedDb, IExecutionFlattedDb,
IPushDataExecutionFinished, IPushDataExecutionFinished,
@ -13,11 +14,13 @@ import {
} from './'; } from './';
import { import {
Credentials,
UserSettings, UserSettings,
WorkflowExecute, WorkflowExecute,
} from 'n8n-core'; } from 'n8n-core';
import { import {
ICredentialDataDecryptedObject,
IDataObject, IDataObject,
IExecuteData, IExecuteData,
IExecuteWorkflowInfo, IExecuteWorkflowInfo,
@ -369,6 +372,40 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
} }
/**
* Updates credentials with new data
*
* @export
* @param {string} name Name of the credentials to update
* @param {string} type Type of the credentials to update
* @param {ICredentialDataDecryptedObject} data The new credential data
* @param {string} encryptionKey The encryption key to use
* @returns {Promise<void>}
*/
export async function updateCredentials(name: string, type: string, data: ICredentialDataDecryptedObject, encryptionKey: string): Promise<void> {
const foundCredentials = await Db.collections.Credentials!.find({ name, type });
if (!foundCredentials.length) {
throw new Error(`Could not find credentials for type "${type}" with name "${name}".`);
}
const credentialsDb = foundCredentials[0];
// Encrypt the data
const credentials = new Credentials(credentialsDb.name, credentialsDb.type, credentialsDb.nodesAccess);
credentials.setData(data, encryptionKey);
const newCredentialsData = credentials.getDataToSave() as ICredentialsDb;
// Add special database related data
newCredentialsData.updatedAt = this.getCurrentDate();
// TODO: also add user automatically depending on who is logged in, if anybody is logged in
// Save the credentials in DB
await Db.collections.Credentials!.save(newCredentialsData);
}
/** /**
* Returns the base additional data without webhooks * Returns the base additional data without webhooks
* *
@ -395,6 +432,7 @@ export async function getBase(credentials: IWorkflowCredentials, currentNodePara
executeWorkflow, executeWorkflow,
restApiUrl: urlBaseWebhook + config.get('endpoints.rest') as string, restApiUrl: urlBaseWebhook + config.get('endpoints.rest') as string,
timezone, timezone,
updateCredentials,
webhookBaseUrl, webhookBaseUrl,
webhookTestBaseUrl, webhookTestBaseUrl,
currentNodeParameters, currentNodeParameters,

View file

@ -39,6 +39,7 @@
"typescript": "~3.7.4" "typescript": "~3.7.4"
}, },
"dependencies": { "dependencies": {
"client-oauth2": "^4.2.5",
"cron": "^1.7.2", "cron": "^1.7.2",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",

View file

@ -1,4 +1,5 @@
import { import {
IAllExecuteFunctions,
IBinaryData, IBinaryData,
ICredentialType, ICredentialType,
IDataObject, IDataObject,
@ -18,6 +19,7 @@ import {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { OptionsWithUri } from 'request';
import * as requestPromise from 'request-promise-native'; import * as requestPromise from 'request-promise-native';
interface Constructable<T> { interface Constructable<T> {
@ -35,6 +37,7 @@ export interface IExecuteFunctions extends IExecuteFunctionsBase {
helpers: { helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>; prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[]; returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}; };
} }
@ -44,6 +47,7 @@ export interface IExecuteSingleFunctions extends IExecuteSingleFunctionsBase {
helpers: { helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>; prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
}; };
} }
@ -52,15 +56,22 @@ export interface IPollFunctions extends IPollFunctionsBase {
helpers: { helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>; prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[]; returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}; };
} }
export interface IResponseError extends Error {
statusCode?: number;
}
export interface ITriggerFunctions extends ITriggerFunctionsBase { export interface ITriggerFunctions extends ITriggerFunctionsBase {
helpers: { helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>; prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[]; returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}; };
} }
@ -84,6 +95,7 @@ export interface IUserSettings {
export interface ILoadOptionsFunctions extends ILoadOptionsFunctionsBase { export interface ILoadOptionsFunctions extends ILoadOptionsFunctionsBase {
helpers: { helpers: {
request?: requestPromise.RequestPromiseAPI, request?: requestPromise.RequestPromiseAPI,
requestOAuth?: (this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions) => Promise<any>, // tslint:disable-line:no-any
}; };
} }
@ -91,6 +103,7 @@ export interface ILoadOptionsFunctions extends ILoadOptionsFunctionsBase {
export interface IHookFunctions extends IHookFunctionsBase { export interface IHookFunctions extends IHookFunctionsBase {
helpers: { helpers: {
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
}; };
} }
@ -99,6 +112,7 @@ export interface IWebhookFunctions extends IWebhookFunctionsBase {
helpers: { helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>; prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
request: requestPromise.RequestPromiseAPI, request: requestPromise.RequestPromiseAPI,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any>, // tslint:disable-line:no-any
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[]; returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}; };
} }

View file

@ -2,11 +2,13 @@ import {
Credentials, Credentials,
IHookFunctions, IHookFunctions,
ILoadOptionsFunctions, ILoadOptionsFunctions,
IResponseError,
IWorkflowSettings, IWorkflowSettings,
BINARY_ENCODING, BINARY_ENCODING,
} from './'; } from './';
import { import {
IAllExecuteFunctions,
IBinaryData, IBinaryData,
IContextObject, IContextObject,
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
@ -34,9 +36,11 @@ import {
WorkflowExecuteMode, WorkflowExecuteMode,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { get } from 'lodash'; import * as clientOAuth2 from 'client-oauth2';
import { get, unset } from 'lodash';
import * as express from "express"; import * as express from "express";
import * as path from 'path'; import * as path from 'path';
import { OptionsWithUri } from 'request';
import * as requestPromise from 'request-promise-native'; import * as requestPromise from 'request-promise-native';
import { Magic, MAGIC_MIME_TYPE } from 'mmmagic'; import { Magic, MAGIC_MIME_TYPE } from 'mmmagic';
@ -101,6 +105,70 @@ export async function prepareBinaryData(binaryData: Buffer, filePath?: string, m
/**
* Makes a request using OAuth data for authentication
*
* @export
* @param {IAllExecuteFunctions} this
* @param {string} credentialsType
* @param {(OptionsWithUri | requestPromise.RequestPromiseOptions)} requestOptions
* @param {INode} node
* @param {IWorkflowExecuteAdditionalData} additionalData
* @returns
*/
export function requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, node: INode, additionalData: IWorkflowExecuteAdditionalData) {
const credentials = this.getCredentials(credentialsType) as ICredentialDataDecryptedObject;
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
if (credentials.oauthTokenData === undefined) {
throw new Error('OAuth credentials not connected!');
}
const oAuthClient = new clientOAuth2({});
const oauthTokenData = JSON.parse(credentials.oauthTokenData as string);
const token = oAuthClient.createToken(oauthTokenData);
// Signs the request by adding authorization headers or query parameters depending
// on the token-type used.
const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject);
return this.helpers.request!(newRequestOptions)
.catch(async (error: IResponseError) => {
// TODO: Check if also other codes are possible
if (error.statusCode === 401) {
// TODO: Whole refresh process is not tested yet
// Token is probably not valid anymore. So try refresh it.
const newToken = await token.refresh();
const newCredentialsData = newToken.data;
unset(newCredentialsData, 'csrfSecret');
unset(newCredentialsData, 'oauthTokenData');
// Find the name of the credentials
if (!node.credentials || !node.credentials[credentialsType]) {
throw new Error(`The node "${node.name}" does not have credentials of type "${credentialsType}"!`);
}
const name = node.credentials[credentialsType];
// Save the refreshed token
await additionalData.updateCredentials(name, credentialsType, newCredentialsData, additionalData.encryptionKey);
// Make the request again with the new token
const newRequestOptions = newToken.sign(requestOptions as clientOAuth2.RequestObject);
return this.helpers.request!(newRequestOptions);
}
// Unknown error so simply throw it
throw error;
});
}
/** /**
* Takes generic input data and brings it into the json format n8n uses. * Takes generic input data and brings it into the json format n8n uses.
* *
@ -355,6 +423,9 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio
helpers: { helpers: {
prepareBinaryData, prepareBinaryData,
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
returnJsonArray, returnJsonArray,
}, },
}; };
@ -406,6 +477,9 @@ export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, addi
helpers: { helpers: {
prepareBinaryData, prepareBinaryData,
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
returnJsonArray, returnJsonArray,
}, },
}; };
@ -484,6 +558,9 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
helpers: { helpers: {
prepareBinaryData, prepareBinaryData,
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
returnJsonArray, returnJsonArray,
}, },
}; };
@ -563,6 +640,9 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
helpers: { helpers: {
prepareBinaryData, prepareBinaryData,
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
}, },
}; };
})(workflow, runExecutionData, connectionInputData, inputData, node, itemIndex); })(workflow, runExecutionData, connectionInputData, inputData, node, itemIndex);
@ -610,6 +690,9 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio
}, },
helpers: { helpers: {
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
}, },
}; };
return that; return that;
@ -665,6 +748,9 @@ export function getExecuteHookFunctions(workflow: Workflow, node: INode, additio
}, },
helpers: { helpers: {
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
}, },
}; };
return that; return that;
@ -747,6 +833,9 @@ export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, addi
helpers: { helpers: {
prepareBinaryData, prepareBinaryData,
request: requestPromise, request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> {
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
},
returnJsonArray, returnJsonArray,
}, },
}; };

View file

@ -1,6 +1,7 @@
import { set } from 'lodash'; import { set } from 'lodash';
import { import {
ICredentialDataDecryptedObject,
IExecuteWorkflowInfo, IExecuteWorkflowInfo,
INodeExecutionData, INodeExecutionData,
INodeParameters, INodeParameters,
@ -280,6 +281,7 @@ export function WorkflowExecuteAdditionalData(waitPromise: IDeferredPromise<IRun
restApiUrl: '', restApiUrl: '',
encryptionKey: 'test', encryptionKey: 'test',
timezone: 'America/New_York', timezone: 'America/New_York',
updateCredentials: async (name: string, type: string, data: ICredentialDataDecryptedObject, encryptionKey: string): Promise<void> => {},
webhookBaseUrl: 'webhook', webhookBaseUrl: 'webhook',
webhookTestBaseUrl: 'webhook-test', webhookTestBaseUrl: 'webhook-test',
}; };

View file

@ -169,13 +169,21 @@ export default mixins(
}, },
methods: { methods: {
getCredentialTypeData (name: string): ICredentialType | null { getCredentialTypeData (name: string): ICredentialType | null {
for (const credentialData of this.credentialTypes) { let credentialData = this.$store.getters.credentialType(name);
if (credentialData.name === name) {
return credentialData; if (credentialData === null || credentialData.extends === undefined) {
} return credentialData;
} }
return null; // Credentials extends another one. So get the properties of the one it
// extends and add them.
credentialData = JSON.parse(JSON.stringify(credentialData));
for (const credentialTypeName of credentialData.extends) {
const data = this.$store.getters.credentialType(credentialTypeName);
credentialData.properties.push.apply(credentialData.properties, data.properties);
}
return credentialData;
}, },
credentialsCreated (data: ICredentialsDecryptedResponse): void { credentialsCreated (data: ICredentialsDecryptedResponse): void {
this.$emit('credentialsCreated', data); this.$emit('credentialsCreated', data);

View file

@ -0,0 +1,15 @@
import {
ICredentialType,
} from 'n8n-workflow';
export class GithubOAuth2Api implements ICredentialType {
name = 'githubOAuth2Api';
// name = 'oAuth2Api/githubOAuth2Api';
extends = [
'oAuth2Api',
];
displayName = 'Github OAuth2 API';
properties = [
];
}

View file

@ -67,6 +67,17 @@ export class HttpRequest implements INodeType {
}, },
}, },
}, },
{
name: 'oAuth2Api',
required: true,
displayOptions: {
show: {
authentication: [
'oAuth2',
],
},
},
},
], ],
properties: [ properties: [
{ {
@ -86,6 +97,10 @@ export class HttpRequest implements INodeType {
name: 'Header Auth', name: 'Header Auth',
value: 'headerAuth' value: 'headerAuth'
}, },
{
name: 'OAuth2',
value: 'oAuth2'
},
{ {
name: 'None', name: 'None',
value: 'none' value: 'none'
@ -504,6 +519,7 @@ export class HttpRequest implements INodeType {
const httpBasicAuth = this.getCredentials('httpBasicAuth'); const httpBasicAuth = this.getCredentials('httpBasicAuth');
const httpDigestAuth = this.getCredentials('httpDigestAuth'); const httpDigestAuth = this.getCredentials('httpDigestAuth');
const httpHeaderAuth = this.getCredentials('httpHeaderAuth'); const httpHeaderAuth = this.getCredentials('httpHeaderAuth');
const oAuth2Api = this.getCredentials('oAuth2Api');
let requestOptions: OptionsWithUri; let requestOptions: OptionsWithUri;
let setUiParameter: IDataObject; let setUiParameter: IDataObject;
@ -658,7 +674,13 @@ export class HttpRequest implements INodeType {
} }
// Now that the options are all set make the actual http request // Now that the options are all set make the actual http request
const response = await this.helpers.request(requestOptions); let response;
if (oAuth2Api !== undefined) {
response = await this.helpers.requestOAuth.call(this, 'oAuth2Api', requestOptions);
} else {
response = await this.helpers.request(requestOptions);
}
if (responseFormat === 'file') { if (responseFormat === 'file') {
const dataPropertyName = this.getNodeParameter('dataPropertyName', 0) as string; const dataPropertyName = this.getNodeParameter('dataPropertyName', 0) as string;

View file

@ -1,3 +1,5 @@
import { OptionsWithUri } from 'request';
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
INodeExecutionData, INodeExecutionData,
@ -36,11 +38,71 @@ export class OAuth implements INodeType {
value: 'get', value: 'get',
description: 'Returns the OAuth token data.', description: 'Returns the OAuth token data.',
}, },
{
name: 'Request',
value: 'request',
description: 'Make an OAuth signed requ.',
},
], ],
default: 'get', default: 'get',
description: 'The operation to perform.', description: 'The operation to perform.',
}, },
{
displayName: 'Request Method',
name: 'requestMethod',
type: 'options',
displayOptions: {
show: {
operation: [
'request',
],
},
},
options: [
{
name: 'DELETE',
value: 'DELETE'
},
{
name: 'GET',
value: 'GET'
},
{
name: 'HEAD',
value: 'HEAD'
},
{
name: 'PATCH',
value: 'PATCH'
},
{
name: 'POST',
value: 'POST'
},
{
name: 'PUT',
value: 'PUT'
},
],
default: 'GET',
description: 'The request method to use.',
},
{
displayName: 'URL',
name: 'url',
type: 'string',
displayOptions: {
show: {
operation: [
'request',
],
},
},
default: '',
placeholder: 'http://example.com/index.html',
description: 'The URL to make the request to.',
required: true,
},
] ]
}; };
@ -62,6 +124,22 @@ export class OAuth implements INodeType {
// without knowledge of the node. // without knowledge of the node.
return [this.helpers.returnJsonArray(JSON.parse(credentials.oauthTokenData as string))]; return [this.helpers.returnJsonArray(JSON.parse(credentials.oauthTokenData as string))];
} else if (operation === 'request') {
const url = this.getNodeParameter('url', 0) as string;
const requestMethod = this.getNodeParameter('requestMethod', 0) as string;
// Authorization Code Grant
const requestOptions: OptionsWithUri = {
headers: {
'User-Agent': 'some-user',
},
method: requestMethod,
uri: url,
json: true,
};
const responseData = await this.helpers.requestOAuth.call(this, 'oAuth2Api', requestOptions);
return [this.helpers.returnJsonArray(responseData)];
} else { } else {
throw new Error('Unknown operation'); throw new Error('Unknown operation');
} }

View file

@ -40,6 +40,7 @@
"dist/credentials/FileMaker.credentials.js", "dist/credentials/FileMaker.credentials.js",
"dist/credentials/FlowApi.credentials.js", "dist/credentials/FlowApi.credentials.js",
"dist/credentials/GithubApi.credentials.js", "dist/credentials/GithubApi.credentials.js",
"dist/credentials/GithubOAuth2Api.credentials.js",
"dist/credentials/GitlabApi.credentials.js", "dist/credentials/GitlabApi.credentials.js",
"dist/credentials/GoogleApi.credentials.js", "dist/credentials/GoogleApi.credentials.js",
"dist/credentials/HttpBasicAuth.credentials.js", "dist/credentials/HttpBasicAuth.credentials.js",

View file

@ -2,6 +2,8 @@ import { Workflow } from './Workflow';
import { WorkflowHooks } from './WorkflowHooks'; import { WorkflowHooks } from './WorkflowHooks';
import * as express from 'express'; import * as express from 'express';
export type IAllExecuteFunctions = IExecuteFunctions | IExecuteSingleFunctions | IHookFunctions | ILoadOptionsFunctions | IPollFunctions | ITriggerFunctions | IWebhookFunctions;
export interface IBinaryData { export interface IBinaryData {
[key: string]: string | undefined; [key: string]: string | undefined;
data: string; data: string;
@ -58,6 +60,7 @@ export interface ICredentialsEncrypted {
export interface ICredentialType { export interface ICredentialType {
name: string; name: string;
displayName: string; displayName: string;
extends?: string[];
properties: INodeProperties[]; properties: INodeProperties[];
} }
@ -78,7 +81,7 @@ export interface ICredentialData {
} }
// The encrypted credentials which the nodes can access // The encrypted credentials which the nodes can access
export type CredentialInformation = string | number | boolean; export type CredentialInformation = string | number | boolean | IDataObject;
// The encrypted credentials which the nodes can access // The encrypted credentials which the nodes can access
@ -649,6 +652,7 @@ export interface IWorkflowExecuteAdditionalData {
httpRequest?: express.Request; httpRequest?: express.Request;
restApiUrl: string; restApiUrl: string;
timezone: string; timezone: string;
updateCredentials: (name: string, type: string, data: ICredentialDataDecryptedObject, encryptionKey: string) => Promise<void>;
webhookBaseUrl: string; webhookBaseUrl: string;
webhookTestBaseUrl: string; webhookTestBaseUrl: string;
currentNodeParameters? : INodeParameters[]; currentNodeParameters? : INodeParameters[];