From 86721fc4969b5ecbc950a19e7174bfdee9672b9c Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 26 Jun 2022 15:55:51 -0700 Subject: [PATCH] feat(core) Simplify authentication type (#3578) * :zap: Add generic auth type * :zap: Remove queryAuth * :zap: Remove bearer * :zap: Remove headerAuth * :zap: Remove basicAuth * :zap: Adjust tests * :zap: Small improvements * :shirt: Fix lint issue --- packages/cli/src/CredentialsHelper.ts | 88 +++------ .../cli/test/unit/CredentialsHelper.test.ts | 177 ++++++------------ .../credentials/AsanaApi.credentials.ts | 14 +- .../credentials/CalApi.credentials.ts | 13 +- .../credentials/GithubApi.credentials.ts | 13 +- .../credentials/HttpHeaderAuth.credentials.ts | 14 +- .../credentials/Magento2Api.credentials.ts | 14 +- .../MailjetEmailApi.credentials.ts | 20 +- .../credentials/MailjetSmsApi.credentials.ts | 13 +- .../credentials/PipedriveApi.credentials.ts | 14 +- .../ServiceNowBasicApi.credentials.ts | 15 +- .../credentials/SlackApi.credentials.ts | 13 +- .../credentials/TodoistApi.credentials.ts | 12 +- .../credentials/TwakeCloudApi.credentials.ts | 13 +- packages/workflow/src/Interfaces.ts | 55 ++---- 15 files changed, 207 insertions(+), 281 deletions(-) diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index b551ec73a3..95d1c70e38 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -98,73 +98,41 @@ export class CredentialsHelper extends ICredentialsHelper { if (typeof credentialType.authenticate === 'object') { // Predefined authentication method + let keyResolved: string; + let valueResolved: string; const { authenticate } = credentialType; if (requestOptions.headers === undefined) { requestOptions.headers = {}; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (authenticate.type === 'bearer') { - const tokenPropertyName: string = - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.tokenPropertyName ?? 'accessToken'; - requestOptions.headers.Authorization = `Bearer ${ - credentials[tokenPropertyName] as string - }`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - } else if (authenticate.type === 'basicAuth') { - const userPropertyName: string = - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.userPropertyName ?? 'user'; - const passwordPropertyName: string = - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.passwordPropertyName ?? 'password'; + if (authenticate.type === 'generic') { + Object.entries(authenticate.properties).forEach(([outerKey, outerValue]) => { + Object.entries(outerValue).forEach(([key, value]) => { + keyResolved = this.resolveValue( + key, + { $credentials: credentials }, + workflow, + node, + defaultTimezone, + ); - requestOptions.auth = { - username: credentials[userPropertyName] as string, - password: credentials[passwordPropertyName] as string, - }; - } else if (authenticate.type === 'headerAuth') { - const key = this.resolveValue( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.name, - { $credentials: credentials }, - workflow, - node, - defaultTimezone, - ); + valueResolved = this.resolveValue( + value as string, + { $credentials: credentials }, + workflow, + node, + defaultTimezone, + ); - const value = this.resolveValue( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.value, - { $credentials: credentials }, - workflow, - node, - defaultTimezone, - ); - requestOptions.headers[key] = value; - } else if (authenticate.type === 'queryAuth') { - const key = this.resolveValue( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.key, - { $credentials: credentials }, - workflow, - node, - defaultTimezone, - ); - - const value = this.resolveValue( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - authenticate.properties.value, - { $credentials: credentials }, - workflow, - node, - defaultTimezone, - ); - if (!requestOptions.qs) { - requestOptions.qs = {}; - } - requestOptions.qs[key] = value; + // @ts-ignore + if (!requestOptions[outerKey]) { + // @ts-ignore + requestOptions[outerKey] = {}; + } + // @ts-ignore + requestOptions[outerKey][keyResolved] = valueResolved; + }); + }); } } } diff --git a/packages/cli/test/unit/CredentialsHelper.test.ts b/packages/cli/test/unit/CredentialsHelper.test.ts index b8de4e0529..afc4a1e2e4 100644 --- a/packages/cli/test/unit/CredentialsHelper.test.ts +++ b/packages/cli/test/unit/CredentialsHelper.test.ts @@ -1,10 +1,7 @@ import { CredentialsHelper, CredentialTypes } from '../../src'; import * as Helpers from './Helpers'; import { - IAuthenticateBasicAuth, - IAuthenticateBearer, - IAuthenticateHeaderAuth, - IAuthenticateQueryAuth, + IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialType, ICredentialTypeData, @@ -27,7 +24,7 @@ describe('CredentialsHelper', () => { output: IHttpRequestOptions; }> = [ { - description: 'built-in basicAuth, default property names', + description: 'basicAuth, default property names', input: { credentials: { user: 'user1', @@ -51,10 +48,15 @@ describe('CredentialsHelper', () => { }, ]; - authenticate = { - type: 'basicAuth', - properties: {}, - } as IAuthenticateBasicAuth; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + auth: { + username: '={{$credentials.user}}', + password: '={{$credentials.password}}', + }, + }, + }; })(), }, output: { @@ -65,48 +67,67 @@ describe('CredentialsHelper', () => { }, }, { - description: 'built-in basicAuth, custom property names', + description: 'headerAuth', input: { credentials: { - customUser: 'user2', - customPassword: 'password2', + accessToken: 'test', }, credentialType: new (class TestApi implements ICredentialType { name = 'testApi'; displayName = 'Test API'; properties: INodeProperties[] = [ { - displayName: 'User', - name: 'user', - type: 'string', - default: '', - }, - { - displayName: 'Password', - name: 'password', + displayName: 'Access Token', + name: 'accessToken', type: 'string', default: '', }, ]; - authenticate = { - type: 'basicAuth', + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - userPropertyName: 'customUser', - passwordPropertyName: 'customPassword', + headers: { + Authorization: '=Bearer {{$credentials.accessToken}}', + }, }, - } as IAuthenticateBasicAuth; + }; })(), }, - output: { - url: '', - headers: {}, - auth: { username: 'user2', password: 'password2' }, - qs: {}, - }, + output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} }, }, { - description: 'built-in headerAuth', + description: 'headerAuth, key and value expressions', + input: { + credentials: { + accessToken: 'test', + }, + credentialType: new (class TestApi implements ICredentialType { + name = 'testApi'; + displayName = 'Test API'; + properties: INodeProperties[] = [ + { + displayName: 'Access Token', + name: 'accessToken', + type: 'string', + default: '', + }, + ]; + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + '={{$credentials.accessToken}}': '=Bearer {{$credentials.accessToken}}', + }, + }, + }; + })(), + }, + output: { url: '', headers: { test: 'Bearer test' }, qs: {} }, + }, + { + description: 'queryAuth', input: { credentials: { accessToken: 'test', @@ -124,95 +145,13 @@ describe('CredentialsHelper', () => { ]; authenticate = { - type: 'headerAuth', + type: 'generic', properties: { - name: 'Authorization', - value: '=Bearer {{$credentials.accessToken}}', + qs: { + accessToken: '={{$credentials.accessToken}}', + }, }, - } as IAuthenticateHeaderAuth; - })(), - }, - output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} }, - }, - { - description: 'built-in bearer, default property name', - input: { - credentials: { - accessToken: 'test', - }, - credentialType: new (class TestApi implements ICredentialType { - name = 'testApi'; - displayName = 'Test API'; - properties: INodeProperties[] = [ - { - displayName: 'Access Token', - name: 'accessToken', - type: 'string', - default: '', - }, - ]; - - authenticate = { - type: 'bearer', - properties: {}, - } as IAuthenticateBearer; - })(), - }, - output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} }, - }, - { - description: 'built-in bearer, custom property name', - input: { - credentials: { - myToken: 'test', - }, - credentialType: new (class TestApi implements ICredentialType { - name = 'testApi'; - displayName = 'Test API'; - properties: INodeProperties[] = [ - { - displayName: 'My Token', - name: 'myToken', - type: 'string', - default: '', - }, - ]; - - authenticate = { - type: 'bearer', - properties: { - tokenPropertyName: 'myToken', - }, - } as IAuthenticateBearer; - })(), - }, - output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} }, - }, - { - description: 'built-in queryAuth', - input: { - credentials: { - accessToken: 'test', - }, - credentialType: new (class TestApi implements ICredentialType { - name = 'testApi'; - displayName = 'Test API'; - properties: INodeProperties[] = [ - { - displayName: 'Access Token', - name: 'accessToken', - type: 'string', - default: '', - }, - ]; - - authenticate = { - type: 'queryAuth', - properties: { - key: 'accessToken', - value: '={{$credentials.accessToken}}', - }, - } as IAuthenticateQueryAuth; + } as IAuthenticateGeneric; })(), }, output: { url: '', headers: {}, qs: { accessToken: 'test' } }, diff --git a/packages/nodes-base/credentials/AsanaApi.credentials.ts b/packages/nodes-base/credentials/AsanaApi.credentials.ts index 81805f4166..35710c8dfb 100644 --- a/packages/nodes-base/credentials/AsanaApi.credentials.ts +++ b/packages/nodes-base/credentials/AsanaApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBearer, + IAuthenticateGeneric, ICredentialType, INodeProperties, } from 'n8n-workflow'; @@ -17,9 +17,13 @@ export class AsanaApi implements ICredentialType { }, ]; - authenticate = { - type: 'bearer', - properties: {}, - } as IAuthenticateBearer; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + Authorization: '=Bearer {{$credentials.accessToken}}', + }, + }, + }; } diff --git a/packages/nodes-base/credentials/CalApi.credentials.ts b/packages/nodes-base/credentials/CalApi.credentials.ts index 04bf23171c..9e091e7db4 100644 --- a/packages/nodes-base/credentials/CalApi.credentials.ts +++ b/packages/nodes-base/credentials/CalApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateQueryAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -24,13 +24,14 @@ export class CalApi implements ICredentialType { }, ]; - authenticate = { - type: 'queryAuth', + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - key: 'apiKey', - value: '={{$credentials.apiKey}}', + qs: { + apiKey: '={{$credentials.apiKey}}', + }, }, - } as IAuthenticateQueryAuth; + }; test: ICredentialTestRequest = { request: { diff --git a/packages/nodes-base/credentials/GithubApi.credentials.ts b/packages/nodes-base/credentials/GithubApi.credentials.ts index e54c02dd37..44dd48027a 100644 --- a/packages/nodes-base/credentials/GithubApi.credentials.ts +++ b/packages/nodes-base/credentials/GithubApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateHeaderAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -30,13 +30,16 @@ export class GithubApi implements ICredentialType { default: '', }, ]; - authenticate: IAuthenticateHeaderAuth = { - type: 'headerAuth', + + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - name: 'Authorization', - value: '=token {{$credentials?.accessToken}}', + headers: { + Authorization: '=token {{$credentials?.accessToken}}', + }, }, }; + test: ICredentialTestRequest = { request: { baseURL: '={{$credentials?.server}}', diff --git a/packages/nodes-base/credentials/HttpHeaderAuth.credentials.ts b/packages/nodes-base/credentials/HttpHeaderAuth.credentials.ts index 87f7829731..a3341010f8 100644 --- a/packages/nodes-base/credentials/HttpHeaderAuth.credentials.ts +++ b/packages/nodes-base/credentials/HttpHeaderAuth.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateHeaderAuth, + IAuthenticateGeneric, ICredentialType, INodeProperties, } from 'n8n-workflow'; @@ -26,11 +26,13 @@ export class HttpHeaderAuth implements ICredentialType { default: '', }, ]; - authenticate = { - type: 'headerAuth', + + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - name: '={{credentials.name}}', - value: '={{credentials.value}}', + headers: { + '={{$credentials.name}}': '={{$credentials.value}}', + }, }, - } as IAuthenticateHeaderAuth; + }; } diff --git a/packages/nodes-base/credentials/Magento2Api.credentials.ts b/packages/nodes-base/credentials/Magento2Api.credentials.ts index 0bd4f0d728..203599e45c 100644 --- a/packages/nodes-base/credentials/Magento2Api.credentials.ts +++ b/packages/nodes-base/credentials/Magento2Api.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBearer, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -30,8 +30,12 @@ export class Magento2Api implements ICredentialType { }, }; - authenticate = { - type: 'bearer', - properties: {}, - } as IAuthenticateBearer; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + Authorization: '=Bearer {{$credentials.accessToken}}', + }, + }, + }; } diff --git a/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts b/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts index 0c29f0bd18..6f81a45460 100644 --- a/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts +++ b/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBasicAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -30,13 +30,17 @@ export class MailjetEmailApi implements ICredentialType { description: 'Whether to allow to run the API call in a Sandbox mode, where all validations of the payload will be done without delivering the message', }, ]; - authenticate: IAuthenticateBasicAuth = { - type: 'basicAuth', - properties: { - userPropertyName: 'apiKey', - passwordPropertyName: 'secretKey', - }, - }; + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + auth: { + username: '={{$credentials.apiKey}}', + password: '={{$credentials.secretKey}}', + }, + }, +}; + test: ICredentialTestRequest = { request: { baseURL: `https://api.mailjet.com`, diff --git a/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts b/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts index 9ad4cf94ae..196069371a 100644 --- a/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts +++ b/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateHeaderAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -17,13 +17,16 @@ export class MailjetSmsApi implements ICredentialType { default: '', }, ]; - authenticate: IAuthenticateHeaderAuth = { - type: 'headerAuth', + + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - name: 'Authorization', - value: '=Bearer {{$credentials.token}}', + headers: { + Authorization: '=Bearer {{$credentials.token}}', + }, }, }; + test: ICredentialTestRequest = { request: { baseURL: `https://api.mailjet.com`, diff --git a/packages/nodes-base/credentials/PipedriveApi.credentials.ts b/packages/nodes-base/credentials/PipedriveApi.credentials.ts index 96c3477947..5f07eb1f79 100644 --- a/packages/nodes-base/credentials/PipedriveApi.credentials.ts +++ b/packages/nodes-base/credentials/PipedriveApi.credentials.ts @@ -1,6 +1,5 @@ import { - IAuthenticateQueryAuth, - ICredentialTestRequest, + IAuthenticateGeneric, ICredentialType, INodeProperties, } from 'n8n-workflow'; @@ -19,11 +18,12 @@ export class PipedriveApi implements ICredentialType { }, ]; - authenticate = { - type: 'queryAuth', + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - key: 'api_token', - value: '={{$credentials.apiToken}}', + qs: { + api_token: '={{$credentials.apiToken}}', + }, }, - } as IAuthenticateQueryAuth; + }; } diff --git a/packages/nodes-base/credentials/ServiceNowBasicApi.credentials.ts b/packages/nodes-base/credentials/ServiceNowBasicApi.credentials.ts index 09c01fe27a..64a036b7d8 100644 --- a/packages/nodes-base/credentials/ServiceNowBasicApi.credentials.ts +++ b/packages/nodes-base/credentials/ServiceNowBasicApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBasicAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -40,10 +40,17 @@ export class ServiceNowBasicApi implements ICredentialType { required: true, }, ]; - authenticate: IAuthenticateBasicAuth = { - type: 'basicAuth', - properties: {}, + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + auth: { + username: '={{$credentials.user}}', + password: '={{$credentials.password}}', + }, + }, }; + test: ICredentialTestRequest = { request: { baseURL: '=https://{{$credentials?.subdomain}}.service-now.com', diff --git a/packages/nodes-base/credentials/SlackApi.credentials.ts b/packages/nodes-base/credentials/SlackApi.credentials.ts index ba5e451cad..3fbfa98ad3 100644 --- a/packages/nodes-base/credentials/SlackApi.credentials.ts +++ b/packages/nodes-base/credentials/SlackApi.credentials.ts @@ -1,6 +1,5 @@ import { - IAuthenticateBearer, - IAuthenticateQueryAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -19,12 +18,16 @@ export class SlackApi implements ICredentialType { required: true, }, ]; - authenticate: IAuthenticateBearer = { - type: 'bearer', + + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - tokenPropertyName: 'accessToken', + headers: { + Authorization: '=Bearer {{$credentials.accessToken}}', + }, }, }; + test: ICredentialTestRequest = { request: { baseURL: 'https://slack.com', diff --git a/packages/nodes-base/credentials/TodoistApi.credentials.ts b/packages/nodes-base/credentials/TodoistApi.credentials.ts index 386fc86882..314919805a 100644 --- a/packages/nodes-base/credentials/TodoistApi.credentials.ts +++ b/packages/nodes-base/credentials/TodoistApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBearer, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -18,12 +18,14 @@ export class TodoistApi implements ICredentialType { ]; - authenticate = { - type: 'bearer', + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - tokenPropertyName: 'apiKey', + headers: { + Authorization: '=Bearer {{$credentials.apiKey}}', + }, }, - } as IAuthenticateBearer; + }; test: ICredentialTestRequest = { request: { diff --git a/packages/nodes-base/credentials/TwakeCloudApi.credentials.ts b/packages/nodes-base/credentials/TwakeCloudApi.credentials.ts index 93abdec472..760432b41e 100644 --- a/packages/nodes-base/credentials/TwakeCloudApi.credentials.ts +++ b/packages/nodes-base/credentials/TwakeCloudApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateBearer, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -17,12 +17,15 @@ export class TwakeCloudApi implements ICredentialType { default: '', }, ]; - authenticate = { - type: 'bearer', + + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - tokenPropertyName: 'workspaceKey', + headers: { + Authorization: '=Bearer {{$credentials.workspaceKey}}', + }, }, - } as IAuthenticateBearer; + }; test: ICredentialTestRequest = { request: { diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 7642ef83d4..0ab491b4a1 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -150,6 +150,16 @@ export interface IRequestOptionsSimplified { qs: IDataObject; } +export interface IRequestOptionsSimplifiedAuth { + auth?: { + username: string; + password: string; + }; + body?: IDataObject; + headers?: IDataObject; + qs?: IDataObject; +} + export abstract class ICredentialsHelper { encryptionKey: string; @@ -191,40 +201,16 @@ export abstract class ICredentialsHelper { export interface IAuthenticateBase { type: string; - properties: { - [key: string]: string; - }; + properties: + | { + [key: string]: string; + } + | IRequestOptionsSimplifiedAuth; } -export interface IAuthenticateBasicAuth extends IAuthenticateBase { - type: 'basicAuth'; - properties: { - userPropertyName?: string; - passwordPropertyName?: string; - }; -} - -export interface IAuthenticateBearer extends IAuthenticateBase { - type: 'bearer'; - properties: { - tokenPropertyName?: string; - }; -} - -export interface IAuthenticateHeaderAuth extends IAuthenticateBase { - type: 'headerAuth'; - properties: { - name: string; - value: string; - }; -} - -export interface IAuthenticateQueryAuth extends IAuthenticateBase { - type: 'queryAuth'; - properties: { - key: string; - value: string; - }; +export interface IAuthenticateGeneric extends IAuthenticateBase { + type: 'generic'; + properties: IRequestOptionsSimplifiedAuth; } export type IAuthenticate = @@ -232,10 +218,7 @@ export type IAuthenticate = credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions, ) => Promise) - | IAuthenticateBasicAuth - | IAuthenticateBearer - | IAuthenticateHeaderAuth - | IAuthenticateQueryAuth; + | IAuthenticateGeneric; export interface IAuthenticateRuleBase { type: string;