From 5c8775e5536de158263328d6b7a0fd6ae1cd3d83 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 12:31:36 +0200 Subject: [PATCH 01/15] Move oauth2 endpoints to oauth2 controller --- packages/cli/src/Server.ts | 3 + packages/cli/src/api/oauth2Credential.api.ts | 150 +++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 packages/cli/src/api/oauth2Credential.api.ts diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 822e9f93c2346..813575b3de5a2 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -159,6 +159,7 @@ import { ExecutionEntity } from './databases/entities/ExecutionEntity'; import { SharedWorkflow } from './databases/entities/SharedWorkflow'; import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants'; import { credentialsController } from './api/credentials.api'; +import { oauth2CredentialController } from './api/oauth2Credential.api'; import { getInstanceBaseUrl, isEmailSetUp, @@ -1956,6 +1957,8 @@ class App { // OAuth2-Credential/Auth // ---------------------------------------- + this.app.use(`/${this.restEndpoint}/oauth2-credential`, oauth2CredentialController); + // Authorize OAuth Data this.app.get( `/${this.restEndpoint}/oauth2-credential/auth`, diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts new file mode 100644 index 0000000000000..109f9558fa1a5 --- /dev/null +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -0,0 +1,150 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import express from 'express'; +import { Credentials, UserSettings } from 'n8n-core'; +import _ from 'lodash'; +import { + LoggerProxy, + WorkflowExecuteMode, + INodeCredentialsDetails, + ICredentialsEncrypted, +} from 'n8n-workflow'; +import ClientOAuth2 from 'client-oauth2'; +import querystring from 'querystring'; +import Csrf from 'csrf'; + +import { externalHooks } from '../Server'; + +import { Db, ICredentialsDb, ResponseHelper, WebhookHelpers } from '..'; +import { RESPONSE_ERROR_MESSAGES } from '../constants'; +import { CredentialsHelper, getCredentialForUser } from '../CredentialsHelper'; +import { getLogger } from '../Logger'; +import { OAuthRequest } from '../requests'; +import config from '../../config'; + +export const oauth2CredentialController = express.Router(); + +/** + * Initialize Logger if needed + */ +oauth2CredentialController.use((req, res, next) => { + try { + LoggerProxy.getInstance(); + } catch (error) { + LoggerProxy.init(getLogger()); + } + next(); +}); + +/** + * GET /oauth2-credential/auth + */ +oauth2CredentialController.get( + '/auth', + ResponseHelper.send(async (req: OAuthRequest.OAuth1Credential.Auth): Promise => { + const { id: credentialId } = req.query; + + if (!credentialId) { + throw new ResponseHelper.ResponseError('Required credential ID is missing', undefined, 400); + } + + const credential = await getCredentialForUser(credentialId, req.user); + + if (!credential) { + LoggerProxy.error('Failed to authorize OAuth2 due to lack of permissions', { + userId: req.user.id, + credentialId, + }); + throw new ResponseHelper.ResponseError(RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, undefined, 404); + } + + let encryptionKey: string; + try { + encryptionKey = await UserSettings.getEncryptionKey(); + } catch (error) { + throw new ResponseHelper.ResponseError(error.message, undefined, 500); + } + + const mode: WorkflowExecuteMode = 'internal'; + const timezone = config.getEnv('generic.timezone'); + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + (credential as ICredentialsEncrypted).type, + mode, + timezone, + true, + ); + + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + (credential as ICredentialsEncrypted).type, + mode, + timezone, + ); + + const token = new Csrf(); + // Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR + const csrfSecret = token.secretSync(); + const state = { + token: token.create(csrfSecret), + cid: req.query.id, + }; + const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); + const restEndpoint = config.getEnv('endpoints.rest'); + + const oAuthOptions: ClientOAuth2.Options = { + clientId: _.get(oauthCredentials, 'clientId') as string, + clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, + accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, + redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, + scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), + state: stateEncodedStr, + }; + + await externalHooks.run('oauth2.authenticate', [oAuthOptions]); + + const oAuthObj = new ClientOAuth2(oAuthOptions); + + // Encrypt the data + const credentials = new Credentials( + credential as INodeCredentialsDetails, + (credential as ICredentialsEncrypted).type, + (credential as ICredentialsEncrypted).nodesAccess, + ); + decryptedDataOriginal.csrfSecret = csrfSecret; + + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; + + // Add special database related data + newCredentialsData.updatedAt = new Date(); + + // Update the credentials in DB + await Db.collections.Credentials.update(req.query.id, newCredentialsData); + + const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string; + let returnUri = oAuthObj.code.getUri(); + + // if scope uses comma, change it as the library always return then with spaces + if ((_.get(oauthCredentials, 'scope') as string).includes(',')) { + const data = querystring.parse(returnUri.split('?')[1]); + data.scope = _.get(oauthCredentials, 'scope') as string; + returnUri = `${_.get(oauthCredentials, 'authUrl', '') as string}?${querystring.stringify( + data, + )}`; + } + + if (authQueryParameters) { + returnUri += `&${authQueryParameters}`; + } + + LoggerProxy.verbose('OAuth2 authentication successful for new credential', { + userId: req.user.id, + credentialId, + }); + return returnUri; + }), +); From b754bfd8855c6c4098cc1eaa6333458b74af15c5 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 14:51:38 +0200 Subject: [PATCH 02/15] Remove old oauth2-credential auth endpoint from server.ts --- packages/cli/src/Server.ts | 117 ------------------- packages/cli/src/api/oauth2Credential.api.ts | 2 + 2 files changed, 2 insertions(+), 117 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 813575b3de5a2..71e8b052a62ac 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1959,123 +1959,6 @@ class App { this.app.use(`/${this.restEndpoint}/oauth2-credential`, oauth2CredentialController); - // Authorize OAuth Data - this.app.get( - `/${this.restEndpoint}/oauth2-credential/auth`, - ResponseHelper.send(async (req: OAuthRequest.OAuth2Credential.Auth): Promise => { - const { id: credentialId } = req.query; - - if (!credentialId) { - throw new ResponseHelper.ResponseError( - 'Required credential ID is missing', - undefined, - 400, - ); - } - - const credential = await getCredentialForUser(credentialId, req.user); - - if (!credential) { - LoggerProxy.error('Failed to authorize OAuth2 due to lack of permissions', { - userId: req.user.id, - credentialId, - }); - throw new ResponseHelper.ResponseError( - RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, - undefined, - 404, - ); - } - - let encryptionKey: string; - try { - encryptionKey = await UserSettings.getEncryptionKey(); - } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); - } - - const mode: WorkflowExecuteMode = 'internal'; - const timezone = config.getEnv('generic.timezone'); - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - credential as INodeCredentialsDetails, - credential.type, - mode, - timezone, - true, - ); - - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - credential.type, - mode, - timezone, - ); - - const token = new csrf(); - // Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR - const csrfSecret = token.secretSync(); - const state = { - token: token.create(csrfSecret), - cid: req.query.id, - }; - const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); - - const oAuthOptions: clientOAuth2.Options = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), - state: stateEncodedStr, - }; - - await this.externalHooks.run('oauth2.authenticate', [oAuthOptions]); - - const oAuthObj = new clientOAuth2(oAuthOptions); - - // Encrypt the data - const credentials = new Credentials( - credential as INodeCredentialsDetails, - credential.type, - credential.nodesAccess, - ); - decryptedDataOriginal.csrfSecret = csrfSecret; - - credentials.setData(decryptedDataOriginal, encryptionKey); - const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); - - // Update the credentials in DB - await Db.collections.Credentials.update(req.query.id as string, newCredentialsData); - - const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string; - let returnUri = oAuthObj.code.getUri(); - - // if scope uses comma, change it as the library always return then with spaces - if ((_.get(oauthCredentials, 'scope') as string).includes(',')) { - const data = querystring.parse(returnUri.split('?')[1]); - data.scope = _.get(oauthCredentials, 'scope') as string; - returnUri = `${_.get(oauthCredentials, 'authUrl', '')}?${querystring.stringify(data)}`; - } - - if (authQueryParameters) { - returnUri += `&${authQueryParameters}`; - } - - LoggerProxy.verbose('OAuth2 authentication successful for new credential', { - userId: req.user.id, - credentialId, - }); - return returnUri; - }), - ); - // ---------------------------------------- // OAuth2-Credential/Callback // ---------------------------------------- diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 109f9558fa1a5..91a4e9094dca4 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -39,6 +39,8 @@ oauth2CredentialController.use((req, res, next) => { /** * GET /oauth2-credential/auth + * + * Authorize OAuth Data */ oauth2CredentialController.get( '/auth', From 148d0088e7e083912ebf6ecbf746d815f9b7e448 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 14:59:19 +0200 Subject: [PATCH 03/15] Move OAuth2 callback endpoint to controller --- packages/cli/src/Server.ts | 179 +----------------- packages/cli/src/api/oauth2Credential.api.ts | 187 ++++++++++++++++++- 2 files changed, 186 insertions(+), 180 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 71e8b052a62ac..2cbe514b97690 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1954,188 +1954,11 @@ class App { ); // ---------------------------------------- - // OAuth2-Credential/Auth + // OAuth2-Credential // ---------------------------------------- this.app.use(`/${this.restEndpoint}/oauth2-credential`, oauth2CredentialController); - // ---------------------------------------- - // OAuth2-Credential/Callback - // ---------------------------------------- - - // Verify and store app code. Generate access tokens and store for respective credential. - this.app.get( - `/${this.restEndpoint}/oauth2-credential/callback`, - async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { - try { - // realmId it's currently just use for the quickbook OAuth2 flow - const { code, state: stateEncoded } = req.query; - - if (!code || !stateEncoded) { - const errorResponse = new ResponseHelper.ResponseError( - `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( - req.query, - )}`, - undefined, - 503, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let state; - try { - state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()); - } catch (error) { - const errorResponse = new ResponseHelper.ResponseError( - 'Invalid state format returned', - undefined, - 503, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - const credential = await getCredentialWithoutUser(state.cid); - - if (!credential) { - LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let encryptionKey: string; - try { - encryptionKey = await UserSettings.getEncryptionKey(); - } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); - } - - const mode: WorkflowExecuteMode = 'internal'; - const timezone = config.getEnv('generic.timezone'); - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - credential as INodeCredentialsDetails, - credential.type, - mode, - timezone, - true, - ); - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - credential.type, - mode, - timezone, - ); - - const token = new csrf(); - if ( - decryptedDataOriginal.csrfSecret === undefined || - !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) - ) { - LoggerProxy.debug('OAuth2 callback state is invalid', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - 'The OAuth2 callback state is invalid!', - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let options = {}; - - const oAuth2Parameters = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), - }; - - if ((_.get(oauthCredentials, 'authentication', 'header') as string) === 'body') { - options = { - body: { - client_id: _.get(oauthCredentials, 'clientId') as string, - client_secret: _.get(oauthCredentials, 'clientSecret', '') as string, - }, - }; - delete oAuth2Parameters.clientSecret; - } - - await this.externalHooks.run('oauth2.callback', [oAuth2Parameters]); - - const oAuthObj = new clientOAuth2(oAuth2Parameters); - - const queryParameters = req.originalUrl.split('?').splice(1, 1).join(''); - - const oauthToken = await oAuthObj.code.getToken( - `${oAuth2Parameters.redirectUri}?${queryParameters}`, - options, - ); - - if (Object.keys(req.query).length > 2) { - _.set(oauthToken.data, 'callbackQueryString', _.omit(req.query, 'state', 'code')); - } - - if (oauthToken === undefined) { - LoggerProxy.error('OAuth2 callback failed: unable to get access tokens', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - 'Unable to get access tokens!', - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - 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(decryptedDataOriginal.oauthTokenData, oauthToken.data); - } else { - // No data exists so simply set - decryptedDataOriginal.oauthTokenData = oauthToken.data; - } - - _.unset(decryptedDataOriginal, 'csrfSecret'); - - const credentials = new Credentials( - credential as INodeCredentialsDetails, - credential.type, - credential.nodesAccess, - ); - credentials.setData(decryptedDataOriginal, encryptionKey); - const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); - // Save the credentials in DB - await Db.collections.Credentials.update(state.cid, newCredentialsData); - LoggerProxy.verbose('OAuth2 callback successful for new credential', { - userId: req.user?.id, - credentialId: state.cid, - }); - - res.sendFile(pathResolve(__dirname, '../../templates/oauth-callback.html')); - } catch (error) { - // Error response - return ResponseHelper.sendErrorResponse(res, error); - } - }, - ); - // ---------------------------------------- // Executions // ---------------------------------------- diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 91a4e9094dca4..b39297350902a 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -13,12 +13,17 @@ import { import ClientOAuth2 from 'client-oauth2'; import querystring from 'querystring'; import Csrf from 'csrf'; +import { resolve as pathResolve } from 'path'; import { externalHooks } from '../Server'; import { Db, ICredentialsDb, ResponseHelper, WebhookHelpers } from '..'; import { RESPONSE_ERROR_MESSAGES } from '../constants'; -import { CredentialsHelper, getCredentialForUser } from '../CredentialsHelper'; +import { + CredentialsHelper, + getCredentialForUser, + getCredentialWithoutUser, +} from '../CredentialsHelper'; import { getLogger } from '../Logger'; import { OAuthRequest } from '../requests'; import config from '../../config'; @@ -37,6 +42,8 @@ oauth2CredentialController.use((req, res, next) => { next(); }); +const restEndpoint = config.getEnv('endpoints.rest'); + /** * GET /oauth2-credential/auth * @@ -94,7 +101,6 @@ oauth2CredentialController.get( cid: req.query.id, }; const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); - const restEndpoint = config.getEnv('endpoints.rest'); const oAuthOptions: ClientOAuth2.Options = { clientId: _.get(oauthCredentials, 'clientId') as string, @@ -150,3 +156,180 @@ oauth2CredentialController.get( return returnUri; }), ); + +/** + * GET /oauth2-credential/callback + * + * Verify and store app code. Generate access tokens and store for respective credential. + */ + +oauth2CredentialController.get( + `/${restEndpoint}/oauth2-credential/callback`, + // eslint-disable-next-line consistent-return + async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { + try { + // realmId it's currently just use for the quickbook OAuth2 flow + const { code, state: stateEncoded } = req.query; + + if (!code || !stateEncoded) { + const errorResponse = new ResponseHelper.ResponseError( + `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( + req.query, + )}`, + undefined, + 503, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let state; + try { + state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()); + } catch (error) { + const errorResponse = new ResponseHelper.ResponseError( + 'Invalid state format returned', + undefined, + 503, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + const credential = await getCredentialWithoutUser(state.cid); + + if (!credential) { + LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let encryptionKey: string; + try { + encryptionKey = await UserSettings.getEncryptionKey(); + } catch (error) { + throw new ResponseHelper.ResponseError(error.message, undefined, 500); + } + + const mode: WorkflowExecuteMode = 'internal'; + const timezone = config.getEnv('generic.timezone'); + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + (credential as ICredentialsEncrypted).type, + mode, + timezone, + true, + ); + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + (credential as ICredentialsEncrypted).type, + mode, + timezone, + ); + + const token = new Csrf(); + if ( + decryptedDataOriginal.csrfSecret === undefined || + !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) + ) { + LoggerProxy.debug('OAuth2 callback state is invalid', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + 'The OAuth2 callback state is invalid!', + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let options = {}; + + const oAuth2Parameters = { + clientId: _.get(oauthCredentials, 'clientId') as string, + clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, + accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, + redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, + scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), + }; + + if ((_.get(oauthCredentials, 'authentication', 'header') as string) === 'body') { + options = { + body: { + client_id: _.get(oauthCredentials, 'clientId') as string, + client_secret: _.get(oauthCredentials, 'clientSecret', '') as string, + }, + }; + delete oAuth2Parameters.clientSecret; + } + + await externalHooks.run('oauth2.callback', [oAuth2Parameters]); + + const oAuthObj = new ClientOAuth2(oAuth2Parameters); + + const queryParameters = req.originalUrl.split('?').splice(1, 1).join(''); + + const oauthToken = await oAuthObj.code.getToken( + `${oAuth2Parameters.redirectUri}?${queryParameters}`, + options, + ); + + if (Object.keys(req.query).length > 2) { + _.set(oauthToken.data, 'callbackQueryString', _.omit(req.query, 'state', 'code')); + } + + if (oauthToken === undefined) { + LoggerProxy.error('OAuth2 callback failed: unable to get access tokens', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + 'Unable to get access tokens!', + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + 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(decryptedDataOriginal.oauthTokenData, oauthToken.data); + } else { + // No data exists so simply set + decryptedDataOriginal.oauthTokenData = oauthToken.data; + } + + _.unset(decryptedDataOriginal, 'csrfSecret'); + + const credentials = new Credentials( + credential as INodeCredentialsDetails, + (credential as ICredentialsEncrypted).type, + (credential as ICredentialsEncrypted).nodesAccess, + ); + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; + // Add special database related data + newCredentialsData.updatedAt = new Date(); + // Save the credentials in DB + await Db.collections.Credentials.update(state.cid, newCredentialsData); + LoggerProxy.verbose('OAuth2 callback successful for new credential', { + userId: req.user?.id, + credentialId: state.cid, + }); + + res.sendFile(pathResolve(__dirname, '../../templates/oauth-callback.html')); + } catch (error) { + // Error response + return ResponseHelper.sendErrorResponse(res, error); + } + }, +); From bca671e6be3efbd552961daf5a25c2dd5f05273d Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 15:29:45 +0200 Subject: [PATCH 04/15] Fix tests and eslint issues --- packages/cli/src/api/oauth2Credential.api.ts | 27 +++++++++---------- .../integration/passwordReset.api.test.ts | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index b39297350902a..755b497f24dcf 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -1,21 +1,19 @@ /* eslint-disable import/no-cycle */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import ClientOAuth2 from 'client-oauth2'; +import Csrf from 'csrf'; import express from 'express'; -import { Credentials, UserSettings } from 'n8n-core'; import _ from 'lodash'; +import { Credentials, UserSettings } from 'n8n-core'; import { LoggerProxy, WorkflowExecuteMode, INodeCredentialsDetails, ICredentialsEncrypted, } from 'n8n-workflow'; -import ClientOAuth2 from 'client-oauth2'; -import querystring from 'querystring'; -import Csrf from 'csrf'; import { resolve as pathResolve } from 'path'; - -import { externalHooks } from '../Server'; +import querystring from 'querystring'; import { Db, ICredentialsDb, ResponseHelper, WebhookHelpers } from '..'; import { RESPONSE_ERROR_MESSAGES } from '../constants'; @@ -26,6 +24,7 @@ import { } from '../CredentialsHelper'; import { getLogger } from '../Logger'; import { OAuthRequest } from '../requests'; +import { externalHooks } from '../Server'; import config from '../../config'; export const oauth2CredentialController = express.Router(); @@ -80,7 +79,7 @@ oauth2CredentialController.get( const credentialsHelper = new CredentialsHelper(encryptionKey); const decryptedDataOriginal = await credentialsHelper.getDecrypted( credential as INodeCredentialsDetails, - (credential as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).type, mode, timezone, true, @@ -88,7 +87,7 @@ oauth2CredentialController.get( const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( decryptedDataOriginal, - (credential as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).type, mode, timezone, ); @@ -119,8 +118,8 @@ oauth2CredentialController.get( // Encrypt the data const credentials = new Credentials( credential as INodeCredentialsDetails, - (credential as ICredentialsEncrypted).type, - (credential as ICredentialsEncrypted).nodesAccess, + (credential as unknown as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).nodesAccess, ); decryptedDataOriginal.csrfSecret = csrfSecret; @@ -221,14 +220,14 @@ oauth2CredentialController.get( const credentialsHelper = new CredentialsHelper(encryptionKey); const decryptedDataOriginal = await credentialsHelper.getDecrypted( credential as INodeCredentialsDetails, - (credential as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).type, mode, timezone, true, ); const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( decryptedDataOriginal, - (credential as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).type, mode, timezone, ); @@ -312,8 +311,8 @@ oauth2CredentialController.get( const credentials = new Credentials( credential as INodeCredentialsDetails, - (credential as ICredentialsEncrypted).type, - (credential as ICredentialsEncrypted).nodesAccess, + (credential as unknown as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).nodesAccess, ); credentials.setData(decryptedDataOriginal, encryptionKey); const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; diff --git a/packages/cli/test/integration/passwordReset.api.test.ts b/packages/cli/test/integration/passwordReset.api.test.ts index 4b6a4256c5bc2..66bcae6626e06 100644 --- a/packages/cli/test/integration/passwordReset.api.test.ts +++ b/packages/cli/test/integration/passwordReset.api.test.ts @@ -44,7 +44,7 @@ beforeEach(async () => { config.set('userManagement.isInstanceOwnerSetUp', true); config.set('userManagement.emails.mode', ''); -}); +}, 10000); afterAll(async () => { await testDb.terminate(testDbName); From 9f859c3e3c4b9b4b39067e6fdfbd941fa300409f Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 16:15:07 +0200 Subject: [PATCH 05/15] Fix typo --- packages/cli/src/api/oauth2Credential.api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 755b497f24dcf..599e1a0d5b15a 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -163,7 +163,7 @@ oauth2CredentialController.get( */ oauth2CredentialController.get( - `/${restEndpoint}/oauth2-credential/callback`, + '/callback', // eslint-disable-next-line consistent-return async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { try { @@ -325,7 +325,7 @@ oauth2CredentialController.get( credentialId: state.cid, }); - res.sendFile(pathResolve(__dirname, '../../templates/oauth-callback.html')); + res.sendFile(pathResolve(__dirname, '../../../templates/oauth-callback.html')); } catch (error) { // Error response return ResponseHelper.sendErrorResponse(res, error); From 04775a2371c8672379e4ba9ff7bd51b59eb96fa4 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 21:07:40 +0200 Subject: [PATCH 06/15] fix lint issues --- package-lock.json | 2 +- packages/cli/package.json | 14 +++++-- packages/cli/src/api/oauth2Credential.api.ts | 40 ++++++++++---------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc42d8b3f198e..6d4df837d599e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110093,4 +110093,4 @@ "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" } } -} +} \ No newline at end of file diff --git a/packages/cli/package.json b/packages/cli/package.json index a2ddaab8058ee..db6ea93561a9f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -70,8 +70,12 @@ "@types/express": "^4.17.6", "@types/jest": "^27.4.0", "@types/localtunnel": "^1.9.0", - "@types/lodash": "^4.14.182", "@types/node": "^16.11.22", + "@types/lodash.get": "^4.4.6", + "@types/lodash.merge": "^4.6.6", + "@types/lodash.omit": "^4.5.7", + "@types/lodash.set": "^4.3.6", + "@types/lodash.split": "^4.4.7", "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", "@types/passport-jwt": "^3.0.6", @@ -129,7 +133,11 @@ "jsonwebtoken": "^8.5.1", "jwks-rsa": "~1.12.1", "localtunnel": "^2.0.0", - "lodash": "^4.17.21", + "lodash.get": "^4.4.2", + "lodash.merge": "^4.6.2", + "lodash.omit": "^4.5.0", + "lodash.set": "^4.3.2", + "lodash.split": "^4.4.2", "mysql2": "~2.3.0", "n8n-core": "~0.124.0", "n8n-editor-ui": "~0.150.0", @@ -157,4 +165,4 @@ "winston": "^3.3.3", "yamljs": "^0.3.0" } -} +} \ No newline at end of file diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 599e1a0d5b15a..a1541b2a07b0e 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -4,7 +4,7 @@ import ClientOAuth2 from 'client-oauth2'; import Csrf from 'csrf'; import express from 'express'; -import _ from 'lodash'; +import { get, omit, set, split, unset } from 'lodash'; import { Credentials, UserSettings } from 'n8n-core'; import { LoggerProxy, @@ -102,12 +102,12 @@ oauth2CredentialController.get( const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); const oAuthOptions: ClientOAuth2.Options = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, + clientId: get(oauthCredentials, 'clientId') as string, + clientSecret: get(oauthCredentials, 'clientSecret', '') as string, + accessTokenUri: get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: get(oauthCredentials, 'authUrl', '') as string, redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), + scopes: split(get(oauthCredentials, 'scope', 'openid,') as string, ','), state: stateEncodedStr, }; @@ -132,14 +132,14 @@ oauth2CredentialController.get( // Update the credentials in DB await Db.collections.Credentials.update(req.query.id, newCredentialsData); - const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string; + const authQueryParameters = get(oauthCredentials, 'authQueryParameters', '') as string; let returnUri = oAuthObj.code.getUri(); // if scope uses comma, change it as the library always return then with spaces - if ((_.get(oauthCredentials, 'scope') as string).includes(',')) { + if ((get(oauthCredentials, 'scope') as string).includes(',')) { const data = querystring.parse(returnUri.split('?')[1]); - data.scope = _.get(oauthCredentials, 'scope') as string; - returnUri = `${_.get(oauthCredentials, 'authUrl', '') as string}?${querystring.stringify( + data.scope = get(oauthCredentials, 'scope') as string; + returnUri = `${get(oauthCredentials, 'authUrl', '') as string}?${querystring.stringify( data, )}`; } @@ -252,19 +252,19 @@ oauth2CredentialController.get( let options = {}; const oAuth2Parameters = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, + clientId: get(oauthCredentials, 'clientId') as string, + clientSecret: get(oauthCredentials, 'clientSecret', '') as string | undefined, + accessTokenUri: get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: get(oauthCredentials, 'authUrl', '') as string, redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), + scopes: split(get(oauthCredentials, 'scope', 'openid,') as string, ','), }; - if ((_.get(oauthCredentials, 'authentication', 'header') as string) === 'body') { + if ((get(oauthCredentials, 'authentication', 'header') as string) === 'body') { options = { body: { - client_id: _.get(oauthCredentials, 'clientId') as string, - client_secret: _.get(oauthCredentials, 'clientSecret', '') as string, + client_id: get(oauthCredentials, 'clientId') as string, + client_secret: get(oauthCredentials, 'clientSecret', '') as string, }, }; delete oAuth2Parameters.clientSecret; @@ -282,7 +282,7 @@ oauth2CredentialController.get( ); if (Object.keys(req.query).length > 2) { - _.set(oauthToken.data, 'callbackQueryString', _.omit(req.query, 'state', 'code')); + set(oauthToken.data, 'callbackQueryString', omit(req.query, 'state', 'code')); } if (oauthToken === undefined) { @@ -307,7 +307,7 @@ oauth2CredentialController.get( decryptedDataOriginal.oauthTokenData = oauthToken.data; } - _.unset(decryptedDataOriginal, 'csrfSecret'); + unset(decryptedDataOriginal, 'csrfSecret'); const credentials = new Credentials( credential as INodeCredentialsDetails, From cd770064341f9ec7ce97d7f67f098f496e525e86 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 21:35:06 +0200 Subject: [PATCH 07/15] update package-lock --- package-lock.json | 210 +++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 107 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d4df837d599e..a650350b3f461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,9 @@ "@types/lodash.camelcase": "^4.3.6", "@types/lodash.get": "^4.4.6", "@types/lodash.merge": "^4.6.6", + "@types/lodash.omit": "^4.5.7", "@types/lodash.set": "^4.3.6", + "@types/lodash.split": "^4.4.7", "@types/lossless-json": "^1.0.0", "@types/luxon": "^2.0.9", "@types/mailparser": "^2.7.3", @@ -184,7 +186,9 @@ "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "lodash.merge": "^4.6.2", + "lodash.omit": "^4.5.0", "lodash.set": "^4.3.2", + "lodash.split": "^4.4.2", "lodash.unset": "^4.5.2", "lossless-json": "^1.0.4", "luxon": "^2.3.0", @@ -15060,6 +15064,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.omit": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz", + "integrity": "sha512-6q6cNg0tQ6oTWjSM+BcYMBhan54P/gLqBldG4AuXd3nKr0oeVekWNS4VrNEu3BhCSDXtGapi7zjhnna0s03KpA==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.set": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz", @@ -15068,6 +15080,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.split": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.split/-/lodash.split-4.4.7.tgz", + "integrity": "sha512-4L/89eW9ZFOkJscM/u0nRwWaN5jHK/esQ71FHuVgePAtBD9YRdYS4cM1HRyIxN3xAoRRrw1Ohf2HQjSk0zwuqA==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", @@ -15245,9 +15265,9 @@ } }, "node_modules/@types/passport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz", - "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz", + "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==", "dependencies": { "@types/express": "*" } @@ -15544,9 +15564,9 @@ } }, "node_modules/@types/uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==", "dependencies": { "source-map": "^0.6.1" } @@ -22878,20 +22898,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -31182,20 +31188,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/express/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -37298,9 +37290,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/iso-639-1": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", - "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.14.tgz", + "integrity": "sha512-nekI+mmtSpYySPXIXJtWhv/s+06nAU9wQzq4QPu3YSEMmjnkOoippPY+MEdqDP0Pie8/LsOFEuPbUHslLanDag==", "engines": { "node": ">=6.0" } @@ -43451,6 +43443,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -43471,6 +43468,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "node_modules/lodash.split": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.split/-/lodash.split-4.4.2.tgz", + "integrity": "sha512-kn1IDX0aHfg0FsnPIyxCHTamZXt3YK3aExRH1LW8YhzP6+sCldTm8+E4aIg+nSmM6R4eqdWGrXWtfYI961bwIw==" + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -44311,9 +44313,9 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.8" } @@ -55035,6 +55037,11 @@ "node": ">=12.3.0" } }, + "node_modules/tedious/node_modules/@types/node": { + "version": "12.20.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.54.tgz", + "integrity": "sha512-CFMnEPkSXWALI73t1oIWyb8QOmVrp6RruAqIx349sd+1ImaFwzlKcz55mwrx/yLyOyz1gkq/UKuNOigt27PXqg==" + }, "node_modules/tedious/node_modules/bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", @@ -55536,9 +55543,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz", - "integrity": "sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", + "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -61186,9 +61193,9 @@ } }, "node_modules/xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.12.tgz", + "integrity": "sha512-8pXgz5BUUfKMrb81tmcbvLNA97ab4d6HdoBHYF5XYHa8oarc2s64hF+oqI4FhBHVBWvEM1wHGy+vqt8kZhCaNw==", "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" @@ -73620,6 +73627,14 @@ "@types/lodash": "*" } }, + "@types/lodash.omit": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz", + "integrity": "sha512-6q6cNg0tQ6oTWjSM+BcYMBhan54P/gLqBldG4AuXd3nKr0oeVekWNS4VrNEu3BhCSDXtGapi7zjhnna0s03KpA==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lodash.set": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz", @@ -73628,6 +73643,14 @@ "@types/lodash": "*" } }, + "@types/lodash.split": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.split/-/lodash.split-4.4.7.tgz", + "integrity": "sha512-4L/89eW9ZFOkJscM/u0nRwWaN5jHK/esQ71FHuVgePAtBD9YRdYS4cM1HRyIxN3xAoRRrw1Ohf2HQjSk0zwuqA==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", @@ -73801,9 +73824,9 @@ } }, "@types/passport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz", - "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz", + "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==", "requires": { "@types/express": "*" } @@ -74099,9 +74122,9 @@ } }, "@types/uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==", "requires": { "source-map": "^0.6.1" } @@ -79876,14 +79899,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } } } }, @@ -86284,40 +86299,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } - } - } - }, - "express-openapi-validator": { - "version": "4.13.8", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-4.13.8.tgz", - "integrity": "sha512-89/sdkq+BKBuIyykaMl/vR9grFc3WFUPTjFo0THHbu+5g+q8rA7fKeoMfz+h84yOQIBcztmJ5ZJdk5uhEls31A==", - "requires": { - "@types/multer": "^1.4.7", - "ajv": "^6.12.6", - "content-type": "^1.0.4", - "json-schema-ref-parser": "^9.0.9", - "lodash.clonedeep": "^4.5.0", - "lodash.get": "^4.4.2", - "lodash.uniq": "^4.5.0", - "lodash.zipobject": "^4.1.3", - "media-typer": "^1.1.0", - "multer": "^1.4.5-lts.1", - "ono": "^7.1.3", - "path-to-regexp": "^6.2.0" - }, - "dependencies": { - "path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" } } }, @@ -91034,9 +91015,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "iso-639-1": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", - "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==" + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.14.tgz", + "integrity": "sha512-nekI+mmtSpYySPXIXJtWhv/s+06nAU9wQzq4QPu3YSEMmjnkOoippPY+MEdqDP0Pie8/LsOFEuPbUHslLanDag==" }, "isobject": { "version": "3.0.1", @@ -95881,6 +95862,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -95901,6 +95887,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "lodash.split": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.split/-/lodash.split-4.4.2.tgz", + "integrity": "sha512-kn1IDX0aHfg0FsnPIyxCHTamZXt3YK3aExRH1LW8YhzP6+sCldTm8+E4aIg+nSmM6R4eqdWGrXWtfYI961bwIw==" + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -96609,9 +96600,9 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memfs": { "version": "3.4.7", @@ -105145,6 +105136,11 @@ "sprintf-js": "^1.1.2" }, "dependencies": { + "@types/node": { + "version": "12.20.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.54.tgz", + "integrity": "sha512-CFMnEPkSXWALI73t1oIWyb8QOmVrp6RruAqIx349sd+1ImaFwzlKcz55mwrx/yLyOyz1gkq/UKuNOigt27PXqg==" + }, "bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", @@ -105508,9 +105504,9 @@ } }, "terser": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz", - "integrity": "sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", + "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -109817,9 +109813,9 @@ "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==" }, "xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.12.tgz", + "integrity": "sha512-8pXgz5BUUfKMrb81tmcbvLNA97ab4d6HdoBHYF5XYHa8oarc2s64hF+oqI4FhBHVBWvEM1wHGy+vqt8kZhCaNw==", "requires": { "commander": "^2.20.3", "cssfilter": "0.0.10" @@ -110093,4 +110089,4 @@ "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" } } -} \ No newline at end of file +} From 58c9c428d3335a46651d1c8b077e8360de57b0f8 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 22:08:01 +0200 Subject: [PATCH 08/15] Import lodash methods individually --- package-lock.json | 17 +++++++++++++++++ packages/cli/package.json | 4 +++- packages/cli/src/api/oauth2Credential.api.ts | 6 +++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a650350b3f461..1046c7324a9a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "@types/lodash.omit": "^4.5.7", "@types/lodash.set": "^4.3.6", "@types/lodash.split": "^4.4.7", + "@types/lodash.unset": "^4.5.7", "@types/lossless-json": "^1.0.0", "@types/luxon": "^2.0.9", "@types/mailparser": "^2.7.3", @@ -15088,6 +15089,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.unset": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.unset/-/lodash.unset-4.5.7.tgz", + "integrity": "sha512-/i371dATnLQ4tazwcX/n+rGk3M6RnMbA3lJKrKFjELicPExmZ1LcKtGfHBECuPS2TTl3yDuaFmWtmfACVuBBAQ==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", @@ -73651,6 +73660,14 @@ "@types/lodash": "*" } }, + "@types/lodash.unset": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.unset/-/lodash.unset-4.5.7.tgz", + "integrity": "sha512-/i371dATnLQ4tazwcX/n+rGk3M6RnMbA3lJKrKFjELicPExmZ1LcKtGfHBECuPS2TTl3yDuaFmWtmfACVuBBAQ==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", diff --git a/packages/cli/package.json b/packages/cli/package.json index db6ea93561a9f..507b0649ecc4d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -70,12 +70,13 @@ "@types/express": "^4.17.6", "@types/jest": "^27.4.0", "@types/localtunnel": "^1.9.0", - "@types/node": "^16.11.22", "@types/lodash.get": "^4.4.6", "@types/lodash.merge": "^4.6.6", "@types/lodash.omit": "^4.5.7", "@types/lodash.set": "^4.3.6", "@types/lodash.split": "^4.4.7", + "@types/lodash.unset": "^4.5.7", + "@types/node": "^16.11.22", "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", "@types/passport-jwt": "^3.0.6", @@ -138,6 +139,7 @@ "lodash.omit": "^4.5.0", "lodash.set": "^4.3.2", "lodash.split": "^4.4.2", + "lodash.unset": "^4.5.2", "mysql2": "~2.3.0", "n8n-core": "~0.124.0", "n8n-editor-ui": "~0.150.0", diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index a1541b2a07b0e..9265d8c53584e 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -4,7 +4,11 @@ import ClientOAuth2 from 'client-oauth2'; import Csrf from 'csrf'; import express from 'express'; -import { get, omit, set, split, unset } from 'lodash'; +import get from 'lodash.get'; +import omit from 'lodash.omit'; +import set from 'lodash.set'; +import split from 'lodash.split'; +import unset from 'lodash.unset'; import { Credentials, UserSettings } from 'n8n-core'; import { LoggerProxy, From 24fed1398b286329a32b7f2436e08d9307eae417 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 3 Jun 2022 22:37:10 +0200 Subject: [PATCH 09/15] Minimise lint rule disables --- package.json | 2 +- packages/cli/src/api/oauth2Credential.api.ts | 27 ++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 2a7773a67cd93..7af7c6017e456 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "lintfix": "lerna exec npm run lintfix", "optimize-svg": "find ./packages -name '*.svg' ! -name 'pipedrive.svg' -print0 | xargs -0 -P16 -L20 npx svgo", "start": "run-script-os", - "start:default": "cd packages/cli/bin && ./n8n", + "start:default": "cd packages/cli/bin && ./n8n start --tunnel", "start:windows": "cd packages/cli/bin && n8n", "test": "lerna run test", "watch": "lerna run --parallel watch", diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 9265d8c53584e..7d0f7c65809a9 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -1,6 +1,4 @@ /* eslint-disable import/no-cycle */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import ClientOAuth2 from 'client-oauth2'; import Csrf from 'csrf'; import express from 'express'; @@ -15,6 +13,7 @@ import { WorkflowExecuteMode, INodeCredentialsDetails, ICredentialsEncrypted, + IDataObject, } from 'n8n-workflow'; import { resolve as pathResolve } from 'path'; import querystring from 'querystring'; @@ -75,7 +74,11 @@ oauth2CredentialController.get( try { encryptionKey = await UserSettings.getEncryptionKey(); } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); + throw new ResponseHelper.ResponseError( + (error as IDataObject).message as string, + undefined, + 500, + ); } const mode: WorkflowExecuteMode = 'internal'; @@ -168,7 +171,6 @@ oauth2CredentialController.get( oauth2CredentialController.get( '/callback', - // eslint-disable-next-line consistent-return async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { try { // realmId it's currently just use for the quickbook OAuth2 flow @@ -187,7 +189,7 @@ oauth2CredentialController.get( let state; try { - state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()); + state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()) as IDataObject; } catch (error) { const errorResponse = new ResponseHelper.ResponseError( 'Invalid state format returned', @@ -197,7 +199,7 @@ oauth2CredentialController.get( return ResponseHelper.sendErrorResponse(res, errorResponse); } - const credential = await getCredentialWithoutUser(state.cid); + const credential = await getCredentialWithoutUser(state.cid as string); if (!credential) { LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { @@ -216,7 +218,11 @@ oauth2CredentialController.get( try { encryptionKey = await UserSettings.getEncryptionKey(); } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); + throw new ResponseHelper.ResponseError( + (error as IDataObject).message as string, + undefined, + 500, + ); } const mode: WorkflowExecuteMode = 'internal'; @@ -239,7 +245,7 @@ oauth2CredentialController.get( const token = new Csrf(); if ( decryptedDataOriginal.csrfSecret === undefined || - !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) + !token.verify(decryptedDataOriginal.csrfSecret as string, state.token as string) ) { LoggerProxy.debug('OAuth2 callback state is invalid', { userId: req.user?.id, @@ -311,6 +317,7 @@ oauth2CredentialController.get( decryptedDataOriginal.oauthTokenData = oauthToken.data; } + // eslint-disable-next-line @typescript-eslint/no-unsafe-call unset(decryptedDataOriginal, 'csrfSecret'); const credentials = new Credentials( @@ -323,13 +330,13 @@ oauth2CredentialController.get( // Add special database related data newCredentialsData.updatedAt = new Date(); // Save the credentials in DB - await Db.collections.Credentials.update(state.cid, newCredentialsData); + await Db.collections.Credentials.update(state.cid as string, newCredentialsData); LoggerProxy.verbose('OAuth2 callback successful for new credential', { userId: req.user?.id, credentialId: state.cid, }); - res.sendFile(pathResolve(__dirname, '../../../templates/oauth-callback.html')); + return res.sendFile(pathResolve(__dirname, '../../../templates/oauth-callback.html')); } catch (error) { // Error response return ResponseHelper.sendErrorResponse(res, error); From 6ab899bcba5d288087d2ebf54a3c9b172a4cace8 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Mon, 6 Jun 2022 12:32:45 +0200 Subject: [PATCH 10/15] Cleanup --- package.json | 2 +- packages/cli/test/integration/passwordReset.api.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7af7c6017e456..2a7773a67cd93 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "lintfix": "lerna exec npm run lintfix", "optimize-svg": "find ./packages -name '*.svg' ! -name 'pipedrive.svg' -print0 | xargs -0 -P16 -L20 npx svgo", "start": "run-script-os", - "start:default": "cd packages/cli/bin && ./n8n start --tunnel", + "start:default": "cd packages/cli/bin && ./n8n", "start:windows": "cd packages/cli/bin && n8n", "test": "lerna run test", "watch": "lerna run --parallel watch", diff --git a/packages/cli/test/integration/passwordReset.api.test.ts b/packages/cli/test/integration/passwordReset.api.test.ts index 66bcae6626e06..4b6a4256c5bc2 100644 --- a/packages/cli/test/integration/passwordReset.api.test.ts +++ b/packages/cli/test/integration/passwordReset.api.test.ts @@ -44,7 +44,7 @@ beforeEach(async () => { config.set('userManagement.isInstanceOwnerSetUp', true); config.set('userManagement.emails.mode', ''); -}, 10000); +}); afterAll(async () => { await testDb.terminate(testDbName); From b94d9dafb4d287be5eefb25db910ee982107f1cc Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 17 Jun 2022 16:29:18 +0200 Subject: [PATCH 11/15] rebase --- package-lock.json | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1046c7324a9a4..267a45df55da1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,6 @@ "@types/json-diff": "^0.5.1", "@types/jsonwebtoken": "^8.5.2", "@types/localtunnel": "^1.9.0", - "@types/lodash": "^4.14.182", "@types/lodash.camelcase": "^4.3.6", "@types/lodash.get": "^4.4.6", "@types/lodash.merge": "^4.6.6", @@ -181,7 +180,6 @@ "jwks-rsa": "~1.12.1", "kafkajs": "^1.14.0", "localtunnel": "^2.0.0", - "lodash": "^4.17.21", "lodash.camelcase": "^4.3.0", "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", @@ -86319,6 +86317,32 @@ } } }, + "express-openapi-validator": { + "version": "4.13.8", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-4.13.8.tgz", + "integrity": "sha512-89/sdkq+BKBuIyykaMl/vR9grFc3WFUPTjFo0THHbu+5g+q8rA7fKeoMfz+h84yOQIBcztmJ5ZJdk5uhEls31A==", + "requires": { + "@types/multer": "^1.4.7", + "ajv": "^6.12.6", + "content-type": "^1.0.4", + "json-schema-ref-parser": "^9.0.9", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "lodash.uniq": "^4.5.0", + "lodash.zipobject": "^4.1.3", + "media-typer": "^1.1.0", + "multer": "^1.4.5-lts.1", + "ono": "^7.1.3", + "path-to-regexp": "^6.2.0" + }, + "dependencies": { + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + } + } + }, "ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -96617,8 +96641,7 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memfs": { From e3f72392ef6a656a72688ec3b87a144755c2042b Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 17 Jun 2022 16:54:50 +0200 Subject: [PATCH 12/15] CR --- packages/cli/src/api/oauth2Credential.api.ts | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index 7d0f7c65809a9..ed674824254ed 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -30,12 +30,12 @@ import { OAuthRequest } from '../requests'; import { externalHooks } from '../Server'; import config from '../../config'; -export const oauth2CredentialController = express.Router(); +export const oauth2CredentialsController = express.Router(); /** * Initialize Logger if needed */ -oauth2CredentialController.use((req, res, next) => { +oauth2CredentialsController.use((req, res, next) => { try { LoggerProxy.getInstance(); } catch (error) { @@ -51,7 +51,7 @@ const restEndpoint = config.getEnv('endpoints.rest'); * * Authorize OAuth Data */ -oauth2CredentialController.get( +oauth2CredentialsController.get( '/auth', ResponseHelper.send(async (req: OAuthRequest.OAuth1Credential.Auth): Promise => { const { id: credentialId } = req.query; @@ -74,11 +74,7 @@ oauth2CredentialController.get( try { encryptionKey = await UserSettings.getEncryptionKey(); } catch (error) { - throw new ResponseHelper.ResponseError( - (error as IDataObject).message as string, - undefined, - 500, - ); + throw new ResponseHelper.ResponseError((error as Error).message, undefined, 500); } const mode: WorkflowExecuteMode = 'internal'; @@ -169,7 +165,7 @@ oauth2CredentialController.get( * Verify and store app code. Generate access tokens and store for respective credential. */ -oauth2CredentialController.get( +oauth2CredentialsController.get( '/callback', async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { try { @@ -189,7 +185,10 @@ oauth2CredentialController.get( let state; try { - state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()) as IDataObject; + state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()) as { + cid: string; + token: string; + }; } catch (error) { const errorResponse = new ResponseHelper.ResponseError( 'Invalid state format returned', @@ -199,7 +198,7 @@ oauth2CredentialController.get( return ResponseHelper.sendErrorResponse(res, errorResponse); } - const credential = await getCredentialWithoutUser(state.cid as string); + const credential = await getCredentialWithoutUser(state.cid); if (!credential) { LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { @@ -245,7 +244,7 @@ oauth2CredentialController.get( const token = new Csrf(); if ( decryptedDataOriginal.csrfSecret === undefined || - !token.verify(decryptedDataOriginal.csrfSecret as string, state.token as string) + !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) ) { LoggerProxy.debug('OAuth2 callback state is invalid', { userId: req.user?.id, @@ -330,7 +329,7 @@ oauth2CredentialController.get( // Add special database related data newCredentialsData.updatedAt = new Date(); // Save the credentials in DB - await Db.collections.Credentials.update(state.cid as string, newCredentialsData); + await Db.collections.Credentials.update(state.cid, newCredentialsData); LoggerProxy.verbose('OAuth2 callback successful for new credential', { userId: req.user?.id, credentialId: state.cid, From e97c00e75b48d6bb92f07b4d3dd37a0d696cea48 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 17 Jun 2022 17:01:32 +0200 Subject: [PATCH 13/15] npm package: Remove lodash, use lodash.intersect --- package-lock.json | 28 +++++++++++++++++++ packages/cli/package.json | 4 ++- .../handlers/workflows/workflows.service.ts | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 267a45df55da1..9ae2a87d07b00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "@types/localtunnel": "^1.9.0", "@types/lodash.camelcase": "^4.3.6", "@types/lodash.get": "^4.4.6", + "@types/lodash.intersection": "^4.4.7", "@types/lodash.merge": "^4.6.6", "@types/lodash.omit": "^4.5.7", "@types/lodash.set": "^4.3.6", @@ -183,6 +184,7 @@ "lodash.camelcase": "^4.3.0", "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", + "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "lodash.merge": "^4.6.2", "lodash.omit": "^4.5.0", @@ -15055,6 +15057,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.intersection": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.7.tgz", + "integrity": "sha512-7ukD2s54bmRNNpiH9ApEErO4H6mB8+WmXFr/6RpP3e/n7h3UFhEJC7QwLcoWAqOrYCIRFMAAwDf3ambSsW8c5Q==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -43389,6 +43399,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -73626,6 +73641,14 @@ "@types/lodash": "*" } }, + "@types/lodash.intersection": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.7.tgz", + "integrity": "sha512-7ukD2s54bmRNNpiH9ApEErO4H6mB8+WmXFr/6RpP3e/n7h3UFhEJC7QwLcoWAqOrYCIRFMAAwDf3ambSsW8c5Q==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -95842,6 +95865,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", diff --git a/packages/cli/package.json b/packages/cli/package.json index 507b0649ecc4d..2956eb5ac5e5d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -104,6 +104,7 @@ "@rudderstack/rudder-sdk-node": "1.0.6", "@types/json-diff": "^0.5.1", "@types/jsonwebtoken": "^8.5.2", + "@types/lodash.intersection": "^4.4.7", "@types/shelljs": "^0.8.11", "@types/swagger-ui-express": "^4.1.3", "@types/yamljs": "^0.2.31", @@ -135,6 +136,7 @@ "jwks-rsa": "~1.12.1", "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", + "lodash.intersection": "^4.4.0", "lodash.merge": "^4.6.2", "lodash.omit": "^4.5.0", "lodash.set": "^4.3.2", @@ -167,4 +169,4 @@ "winston": "^3.3.3", "yamljs": "^0.3.0" } -} \ No newline at end of file +} diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 440a83b615a9a..494e7389bf5ad 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -1,5 +1,5 @@ import { FindManyOptions, In, UpdateResult } from 'typeorm'; -import { intersection } from 'lodash'; +import intersection from 'lodash.intersection'; import type { INode } from 'n8n-workflow'; import { Db } from '../../../..'; From 71af0b79f3bc5e683b2c3c7a9fc3422514ca8fb9 Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 1 Jul 2022 14:40:33 +0200 Subject: [PATCH 14/15] fixups --- packages/cli/src/api/oauth2Credential.api.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts index ed674824254ed..2db0b10d73c73 100644 --- a/packages/cli/src/api/oauth2Credential.api.ts +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -30,12 +30,12 @@ import { OAuthRequest } from '../requests'; import { externalHooks } from '../Server'; import config from '../../config'; -export const oauth2CredentialsController = express.Router(); +export const oauth2CredentialController = express.Router(); /** * Initialize Logger if needed */ -oauth2CredentialsController.use((req, res, next) => { +oauth2CredentialController.use((req, res, next) => { try { LoggerProxy.getInstance(); } catch (error) { @@ -51,7 +51,7 @@ const restEndpoint = config.getEnv('endpoints.rest'); * * Authorize OAuth Data */ -oauth2CredentialsController.get( +oauth2CredentialController.get( '/auth', ResponseHelper.send(async (req: OAuthRequest.OAuth1Credential.Auth): Promise => { const { id: credentialId } = req.query; @@ -165,7 +165,7 @@ oauth2CredentialsController.get( * Verify and store app code. Generate access tokens and store for respective credential. */ -oauth2CredentialsController.get( +oauth2CredentialController.get( '/callback', async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { try { From 8359dcf4bbe75bad0744cfea667d0987ea51d4cc Mon Sep 17 00:00:00 2001 From: ahsan-virani Date: Fri, 1 Jul 2022 15:10:19 +0200 Subject: [PATCH 15/15] rebase --- package-lock.json | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ae2a87d07b00..a5c2e4da9fc7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "n8n", - "version": "0.183.0", + "version": "0.184.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "n8n", - "version": "0.183.0", + "version": "0.184.0", "dependencies": { "@apidevtools/swagger-cli": "4.0.0", "@babel/core": "^7.14.6", @@ -55059,11 +55059,6 @@ "node": ">=12.3.0" } }, - "node_modules/tedious/node_modules/@types/node": { - "version": "12.20.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.54.tgz", - "integrity": "sha512-CFMnEPkSXWALI73t1oIWyb8QOmVrp6RruAqIx349sd+1ImaFwzlKcz55mwrx/yLyOyz1gkq/UKuNOigt27PXqg==" - }, "node_modules/tedious/node_modules/bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", @@ -105204,11 +105199,6 @@ "sprintf-js": "^1.1.2" }, "dependencies": { - "@types/node": { - "version": "12.20.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.54.tgz", - "integrity": "sha512-CFMnEPkSXWALI73t1oIWyb8QOmVrp6RruAqIx349sd+1ImaFwzlKcz55mwrx/yLyOyz1gkq/UKuNOigt27PXqg==" - }, "bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz",