mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
⚡ Abstract OAuth signing and make credentials extendable
This commit is contained in:
parent
740cb8a6fc
commit
8228b8505f
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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',
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 = [
|
||||||
|
];
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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[];
|
||||||
|
|
Loading…
Reference in a new issue