From 61f94ac084a5a1f09711bdf9c6bb880f929d9f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 19 Jan 2022 17:28:58 +0100 Subject: [PATCH 01/12] :zap: Refactor /oauth1-credential/auth --- packages/cli/src/CredentialsHelper.ts | 48 +++++++- packages/cli/src/Interfaces.ts | 2 + packages/cli/src/Server.ts | 168 +++++++++++++------------- packages/cli/src/requests.d.ts | 14 +++ 4 files changed, 147 insertions(+), 85 deletions(-) create mode 100644 packages/cli/src/requests.d.ts diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 8f51c655534f9..9b19876da6e3b 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -1,3 +1,5 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Credentials } from 'n8n-core'; import { @@ -17,7 +19,8 @@ import { } from 'n8n-workflow'; // eslint-disable-next-line import/no-cycle -import { CredentialsOverwrites, CredentialTypes, Db, ICredentialsDb } from '.'; +import { CredentialsOverwrites, CredentialTypes, Db, ICredentialsDb, WhereClause } from '.'; +import { User } from './databases/entities/User'; const mockNodeTypes: INodeTypes = { nodeTypes: {}, @@ -270,3 +273,46 @@ export class CredentialsHelper extends ICredentialsHelper { await Db.collections.Credentials!.update(findQuery, newCredentialsData); } } + +/** + * Build a `where` clause for a `find()` or `findOne()` operation + * in the `shared_workflow` or `shared_credentials` tables. + */ +export function whereClause({ + user, + entityType, + entityId = '', +}: { + user: User; + entityType: 'workflow' | 'credentials'; + entityId?: string; +}): WhereClause { + const where: WhereClause = entityId ? { [entityType]: { id: entityId } } : {}; + + if (user.globalRole.name !== 'owner') { + where.user = { id: user.id }; + } + + return where; +} + +/** + * Get a credential if shared with a user. + */ +export async function getCredentialForUser( + credentialId: string, + user: User, +): Promise { + const sharedCredential = await Db.collections.SharedCredentials!.findOne({ + relations: ['credentials'], + where: whereClause({ + user, + entityType: 'credentials', + entityId: credentialId, + }), + }); + + if (!sharedCredential) return null; + + return sharedCredential.credentials as ICredentialsDb; +} diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 87489e026b7f5..34fe15ad203fe 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -590,3 +590,5 @@ export interface IWorkflowExecuteProcess { workflow: Workflow; workflowExecute: WorkflowExecute; } + +export type WhereClause = Record; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index bb194f7c18dac..bdcdfb8f7ffe9 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -140,6 +140,7 @@ import { WorkflowExecuteAdditionalData, WorkflowHelpers, WorkflowRunner, + getCredentialForUser, } from '.'; import * as config from '../config'; @@ -157,6 +158,7 @@ import { getNodeTranslationPath } from './TranslationHelpers'; import { userManagementRouter } from './UserManagement'; import { User } from './databases/entities/User'; import { CredentialsEntity } from './databases/entities/CredentialsEntity'; +import { OAuthRequest } from './requests'; require('body-parser-xml')(bodyParser); @@ -1881,111 +1883,109 @@ class App { // Authorize OAuth Data this.app.get( `/${this.restEndpoint}/oauth1-credential/auth`, - ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - if (req.query.id === undefined) { - res.status(500).send('Required credential id is missing!'); - return ''; - } + ResponseHelper.send( + async (req: OAuthRequest.OAuth1CredentialAuth, res: express.Response): Promise => { + const { id: credentialId } = req.query; - const queryBuilder = Db.collections.Credentials!.createQueryBuilder('c'); - queryBuilder.andWhere('c.id = :id', { id: req.query.id }); - queryBuilder.innerJoin('c.shared', 'shared'); - queryBuilder.andWhere('shared.userId = :userId', { - userId: (req.user as User).id, - }); + if (!credentialId) { + res.status(500).send('Required credential ID is missing!'); + return ''; + } - const result = await queryBuilder.getOne(); - if (result === undefined) { - res.status(404).send('The credential is not known.'); - return ''; - } + const credential = await getCredentialForUser(credentialId, req.user); - let encryptionKey; - encryptionKey = await UserSettings.getEncryptionKey(); - if (encryptionKey === undefined) { - res.status(500).send('No encryption key got found to decrypt the credentials!'); - return ''; - } + if (!credential) { + res.status(404).send('The credential is not known.'); + return ''; + } - const mode: WorkflowExecuteMode = 'internal'; - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - result as INodeCredentialsDetails, - result.type, - mode, - true, - ); - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - result.type, - mode, - ); + const encryptionKey = await UserSettings.getEncryptionKey(); - const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string; + if (!encryptionKey) { + res.status(500).send('No encryption key found to decrypt the credentials!'); + return ''; + } - const oAuthOptions: clientOAuth1.Options = { - consumer: { - key: _.get(oauthCredentials, 'consumerKey') as string, - secret: _.get(oauthCredentials, 'consumerSecret') as string, - }, - signature_method: signatureMethod, - // eslint-disable-next-line @typescript-eslint/naming-convention - hash_function(base, key) { - const algorithm = signatureMethod === 'HMAC-SHA1' ? 'sha1' : 'sha256'; - return createHmac(algorithm, key).update(base).digest('base64'); - }, - }; + const mode: WorkflowExecuteMode = 'internal'; + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + credential.type, + mode, + true, + ); + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + credential.type, + mode, + ); - const oauthRequestData = { - oauth_callback: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth1-credential/callback?cid=${req.query.id}`, - }; + const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string; - await this.externalHooks.run('oauth1.authenticate', [oAuthOptions, oauthRequestData]); + const oAuthOptions: clientOAuth1.Options = { + consumer: { + key: _.get(oauthCredentials, 'consumerKey') as string, + secret: _.get(oauthCredentials, 'consumerSecret') as string, + }, + signature_method: signatureMethod, + // eslint-disable-next-line @typescript-eslint/naming-convention + hash_function(base, key) { + const algorithm = signatureMethod === 'HMAC-SHA1' ? 'sha1' : 'sha256'; + return createHmac(algorithm, key).update(base).digest('base64'); + }, + }; - // eslint-disable-next-line new-cap - const oauth = new clientOAuth1(oAuthOptions); + const oauthRequestData = { + oauth_callback: `${WebhookHelpers.getWebhookBaseUrl()}${ + this.restEndpoint + }/oauth1-credential/callback?cid=${credentialId}`, + }; - const options: RequestOptions = { - method: 'POST', - url: _.get(oauthCredentials, 'requestTokenUrl') as string, - data: oauthRequestData, - }; + await this.externalHooks.run('oauth1.authenticate', [oAuthOptions, oauthRequestData]); - const data = oauth.toHeader(oauth.authorize(options)); + // eslint-disable-next-line new-cap + const oauth = new clientOAuth1(oAuthOptions); - // @ts-ignore - options.headers = data; + const options: RequestOptions = { + method: 'POST', + url: _.get(oauthCredentials, 'requestTokenUrl') as string, + data: oauthRequestData, + }; - const response = await requestPromise(options); + const data = oauth.toHeader(oauth.authorize(options)); - // Response comes as x-www-form-urlencoded string so convert it to JSON + // @ts-ignore + options.headers = data; - const responseJson = querystring.parse(response); + const response = await requestPromise(options); - const returnUri = `${_.get(oauthCredentials, 'authUrl')}?oauth_token=${ - responseJson.oauth_token - }`; + // Response comes as x-www-form-urlencoded string so convert it to JSON - // Encrypt the data - const credentials = new Credentials( - result as INodeCredentialsDetails, - result.type, - result.nodesAccess, - ); + const responseJson = querystring.parse(response); - credentials.setData(decryptedDataOriginal, encryptionKey); - const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; + const returnUri = `${_.get(oauthCredentials, 'authUrl')}?oauth_token=${ + responseJson.oauth_token + }`; - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); + // Encrypt the data + const credentials = new Credentials( + credential as INodeCredentialsDetails, + credential.type, + credential.nodesAccess, + ); - // Update the credentials in DB - await Db.collections.Credentials!.update(req.query.id as string, newCredentialsData); + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - return returnUri; - }), + // Add special database related data + newCredentialsData.updatedAt = this.getCurrentDate(); + + // Update the credentials in DB + await Db.collections.Credentials!.update(credentialId, newCredentialsData); + + return returnUri; + }, + ), ); // Verify and store app code. Generate access tokens and store for respective credential. diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts new file mode 100644 index 0000000000000..38e112eb164a6 --- /dev/null +++ b/packages/cli/src/requests.d.ts @@ -0,0 +1,14 @@ +/* eslint-disable import/no-cycle */ +import express = require('express'); +import { User } from './databases/entities/User'; + +export type AuthenticatedRequest< + RouteParams = {}, + ResponseBody = {}, + RequestBody = {}, + RequestQuery = {}, +> = express.Request & { user: User }; + +export declare namespace OAuthRequest { + type OAuth1CredentialAuth = AuthenticatedRequest<{}, {}, {}, { id: string }>; +} From 756c98ccc0b4b5a20c87c2f1e9ee9004d8b50628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 19 Jan 2022 18:04:24 +0100 Subject: [PATCH 02/12] :zap: Refactor /oauth2-credential/auth --- packages/cli/src/CredentialsHelper.ts | 2 +- packages/cli/src/Server.ts | 164 +++++++++++++------------- packages/cli/src/requests.d.ts | 1 + 3 files changed, 83 insertions(+), 84 deletions(-) diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 9b19876da6e3b..01b693b84031f 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -297,7 +297,7 @@ export function whereClause({ } /** - * Get a credential if shared with a user. + * Get a credential if it has been shared with a user. */ export async function getCredentialForUser( credentialId: string, diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index bdcdfb8f7ffe9..a48dd7b3160c6 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -2103,104 +2103,102 @@ class App { // Authorize OAuth Data this.app.get( `/${this.restEndpoint}/oauth2-credential/auth`, - ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - if (req.query.id === undefined) { - res.status(500).send('Required credential id is missing.'); - return ''; - } + ResponseHelper.send( + async (req: OAuthRequest.OAuth2CredentialAuth, res: express.Response): Promise => { + const { id: credentialId } = req.query; - const queryBuilder = Db.collections.Credentials!.createQueryBuilder('c'); - queryBuilder.andWhere('c.id = :id', { id: req.query.id }); - queryBuilder.innerJoin('c.shared', 'shared'); - queryBuilder.andWhere('shared.userId = :userId', { - userId: (req.user as User).id, - }); + if (!credentialId) { + res.status(500).send('Required credential ID is missing.'); + return ''; + } - const result = await queryBuilder.getOne(); - if (result === undefined) { - res.status(404).send('The credential is not known.'); - return ''; - } + const credential = await getCredentialForUser(credentialId, req.user); - let encryptionKey; - encryptionKey = await UserSettings.getEncryptionKey(); - if (encryptionKey === undefined) { - res.status(500).send('No encryption key got found to decrypt the credentials!'); - return ''; - } + if (!credential) { + res.status(404).send('The credential is not known.'); + return ''; + } - const mode: WorkflowExecuteMode = 'internal'; - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - result as INodeCredentialsDetails, - result.type, - mode, - true, - ); - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - result.type, - mode, - ); + const encryptionKey = await UserSettings.getEncryptionKey(); - 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, - }; + if (!encryptionKey) { + res.status(500).send('No encryption key found to decrypt the credentials!'); + return ''; + } - await this.externalHooks.run('oauth2.authenticate', [oAuthOptions]); + const mode: WorkflowExecuteMode = 'internal'; + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + credential.type, + mode, + true, + ); + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + credential.type, + mode, + ); - const oAuthObj = new clientOAuth2(oAuthOptions); + 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'); - // Encrypt the data - const credentials = new Credentials( - result as INodeCredentialsDetails, - result.type, - result.nodesAccess, - ); - decryptedDataOriginal.csrfSecret = csrfSecret; + 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; + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); + // 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); + // 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(); + 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 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}`; - } + if (authQueryParameters) { + returnUri += `&${authQueryParameters}`; + } - return returnUri; - }), + return returnUri; + }, + ), ); // ---------------------------------------- diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index 38e112eb164a6..554189c6d4715 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -11,4 +11,5 @@ export type AuthenticatedRequest< export declare namespace OAuthRequest { type OAuth1CredentialAuth = AuthenticatedRequest<{}, {}, {}, { id: string }>; + type OAuth2CredentialAuth = OAuth1CredentialAuth; } From 3465ecdf4e7569ceb8cafc3f868bedd35390b118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 19 Jan 2022 18:08:35 +0100 Subject: [PATCH 03/12] :zap: Refactor /oauth1-credential/callback --- packages/cli/src/Server.ts | 38 ++++++++++++++-------------------- packages/cli/src/requests.d.ts | 6 ++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index a48dd7b3160c6..4958f8c597840 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1991,11 +1991,11 @@ class App { // Verify and store app code. Generate access tokens and store for respective credential. this.app.get( `/${this.restEndpoint}/oauth1-credential/callback`, - async (req: express.Request, res: express.Response) => { + async (req: OAuthRequest.OAuth1CredentialCallback, res: express.Response) => { try { - const { oauth_verifier, oauth_token, cid } = req.query; + const { oauth_verifier, oauth_token, cid: credentialId } = req.query; - if (oauth_verifier === undefined || oauth_token === undefined) { + if (!oauth_verifier || !oauth_token) { const errorResponse = new ResponseHelper.ResponseError( `Insufficient parameters for OAuth1 callback. Received following query parameters: ${JSON.stringify( req.query, @@ -2006,15 +2006,9 @@ class App { return ResponseHelper.sendErrorResponse(res, errorResponse); } - const queryBuilder = Db.collections.Credentials!.createQueryBuilder('c'); - queryBuilder.andWhere('c.id = :id', { id: cid }); - queryBuilder.innerJoin('c.shared', 'shared'); - queryBuilder.andWhere('shared.userId = :userId', { - userId: (req.user as User).id, - }); + const credential = await getCredentialForUser(credentialId, req.user); - const result = await queryBuilder.getOne(); - if (result === undefined) { + if (!credential) { const errorResponse = new ResponseHelper.ResponseError( 'The credential is not known.', undefined, @@ -2023,11 +2017,11 @@ class App { return ResponseHelper.sendErrorResponse(res, errorResponse); } - let encryptionKey; - encryptionKey = await UserSettings.getEncryptionKey(); - if (encryptionKey === undefined) { + const encryptionKey = await UserSettings.getEncryptionKey(); + + if (!encryptionKey) { const errorResponse = new ResponseHelper.ResponseError( - 'No encryption key got found to decrypt the credentials!', + 'No encryption key found to decrypt the credentials!', undefined, 503, ); @@ -2037,14 +2031,14 @@ class App { const mode: WorkflowExecuteMode = 'internal'; const credentialsHelper = new CredentialsHelper(encryptionKey); const decryptedDataOriginal = await credentialsHelper.getDecrypted( - result as INodeCredentialsDetails, - result.type, + credential as INodeCredentialsDetails, + credential.type, mode, true, ); const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( decryptedDataOriginal, - result.type, + credential.type, mode, ); @@ -2077,16 +2071,16 @@ class App { decryptedDataOriginal.oauthTokenData = oauthTokenJson; const credentials = new Credentials( - result as INodeCredentialsDetails, - result.type, - result.nodesAccess, + 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(cid as any, newCredentialsData); + await Db.collections.Credentials!.update(credentialId, newCredentialsData); res.sendFile(pathResolve(__dirname, '../../templates/oauth-callback.html')); } catch (error) { diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index 554189c6d4715..03c483587d8d3 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -12,4 +12,10 @@ export type AuthenticatedRequest< export declare namespace OAuthRequest { type OAuth1CredentialAuth = AuthenticatedRequest<{}, {}, {}, { id: string }>; type OAuth2CredentialAuth = OAuth1CredentialAuth; + type OAuth1CredentialCallback = AuthenticatedRequest< + {}, + {}, + {}, + { oauth_verifier: string; oauth_token: string; cid: string } + >; } From 0f0ea592cb2d35da18531a4bbe4ff55242d03e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 19 Jan 2022 18:12:57 +0100 Subject: [PATCH 04/12] :zap: Refactor /oauth2-credential/callback --- packages/cli/src/Server.ts | 34 ++++++++++++++-------------------- packages/cli/src/requests.d.ts | 1 + 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 4958f8c597840..db60060c3c832 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -2202,12 +2202,12 @@ class App { // Verify and store app code. Generate access tokens and store for respective credential. this.app.get( `/${this.restEndpoint}/oauth2-credential/callback`, - async (req: express.Request, res: express.Response) => { + async (req: OAuthRequest.OAuth2CredentialCallback, res: express.Response) => { try { // realmId it's currently just use for the quickbook OAuth2 flow const { code, state: stateEncoded } = req.query; - if (code === undefined || stateEncoded === undefined) { + if (!code || !stateEncoded) { const errorResponse = new ResponseHelper.ResponseError( `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( req.query, @@ -2220,7 +2220,7 @@ class App { let state; try { - state = JSON.parse(Buffer.from(stateEncoded as string, 'base64').toString()); + state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()); } catch (error) { const errorResponse = new ResponseHelper.ResponseError( 'Invalid state format returned', @@ -2230,15 +2230,9 @@ class App { return ResponseHelper.sendErrorResponse(res, errorResponse); } - const queryBuilder = Db.collections.Credentials!.createQueryBuilder('c'); - queryBuilder.andWhere('c.id = :id', { id: state.cid }); - queryBuilder.innerJoin('c.shared', 'shared'); - queryBuilder.andWhere('shared.userId = :userId', { - userId: (req.user as User).id, - }); + const credential = await getCredentialForUser(state.cid, req.user); - const result = await queryBuilder.getOne(); - if (result === undefined) { + if (!credential) { const errorResponse = new ResponseHelper.ResponseError( 'The credential is not known.', undefined, @@ -2247,9 +2241,9 @@ class App { return ResponseHelper.sendErrorResponse(res, errorResponse); } - let encryptionKey; - encryptionKey = await UserSettings.getEncryptionKey(); - if (encryptionKey === undefined) { + const encryptionKey = await UserSettings.getEncryptionKey(); + + if (!encryptionKey) { const errorResponse = new ResponseHelper.ResponseError( 'No encryption key got found to decrypt the credentials!', undefined, @@ -2261,14 +2255,14 @@ class App { const mode: WorkflowExecuteMode = 'internal'; const credentialsHelper = new CredentialsHelper(encryptionKey); const decryptedDataOriginal = await credentialsHelper.getDecrypted( - result as INodeCredentialsDetails, - result.type, + credential as INodeCredentialsDetails, + credential.type, mode, true, ); const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( decryptedDataOriginal, - result.type, + credential.type, mode, ); @@ -2344,9 +2338,9 @@ class App { _.unset(decryptedDataOriginal, 'csrfSecret'); const credentials = new Credentials( - result as INodeCredentialsDetails, - result.type, - result.nodesAccess, + credential as INodeCredentialsDetails, + credential.type, + credential.nodesAccess, ); credentials.setData(decryptedDataOriginal, encryptionKey); const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index 03c483587d8d3..1b82181f77bed 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -18,4 +18,5 @@ export declare namespace OAuthRequest { {}, { oauth_verifier: string; oauth_token: string; cid: string } >; + type OAuth2CredentialCallback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }>; } From acbbe2a5b91070541966f12a792b702871fd83f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 19 Jan 2022 18:19:42 +0100 Subject: [PATCH 05/12] :zap: Refactor /node-parameter-options --- packages/cli/src/Server.ts | 31 ++++++++++++++++--------------- packages/cli/src/requests.d.ts | 13 +++++++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index db60060c3c832..e84652e640f9f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -158,7 +158,7 @@ import { getNodeTranslationPath } from './TranslationHelpers'; import { userManagementRouter } from './UserManagement'; import { User } from './databases/entities/User'; import { CredentialsEntity } from './databases/entities/CredentialsEntity'; -import { OAuthRequest } from './requests'; +import { NodeParameterOptionsRequest, OAuthRequest } from './requests'; require('body-parser-xml')(bodyParser); @@ -1201,34 +1201,35 @@ class App { this.app.get( `/${this.restEndpoint}/node-parameter-options`, ResponseHelper.send( - async (req: express.Request, res: express.Response): Promise => { + async (req: NodeParameterOptionsRequest): Promise => { const nodeTypeAndVersion = JSON.parse( - `${req.query.nodeTypeAndVersion}`, + req.query.nodeTypeAndVersion, ) as INodeTypeNameVersion; - const path = req.query.path as string; - let credentials: INodeCredentials | undefined; + + const { path, methodName } = req.query; + const currentNodeParameters = JSON.parse( - `${req.query.currentNodeParameters}`, + req.query.currentNodeParameters, ) as INodeParameters; - if (req.query.credentials !== undefined) { - credentials = JSON.parse(req.query.credentials as string); - } - const methodName = req.query.methodName as string; - const nodeTypes = NodeTypes(); + let credentials: INodeCredentials | undefined; + + if (req.query.credentials) { + credentials = JSON.parse(req.query.credentials); + // TODO UM: restrict user access to credentials he cannot use. + } - // @ts-ignore const loadDataInstance = new LoadNodeParameterOptions( nodeTypeAndVersion, - nodeTypes, + NodeTypes(), path, currentNodeParameters, credentials, ); const additionalData = await WorkflowExecuteAdditionalData.getBase(currentNodeParameters); - // TODO UM: restrict user access to credentials he cannot use. - additionalData.userId = (req.user as User).id; + + additionalData.userId = req.user.id; return loadDataInstance.getOptions(methodName, additionalData); }, diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index 1b82181f77bed..e686109122dda 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -20,3 +20,16 @@ export declare namespace OAuthRequest { >; type OAuth2CredentialCallback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }>; } + +export type NodeParameterOptionsRequest = AuthenticatedRequest< + {}, + {}, + {}, + { + nodeTypeAndVersion: string; + methodName: string; + path: string; + currentNodeParameters: string; + credentials: string; + } +>; From 86c74d89cbcd0084962477af43c120de46c66753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 20 Jan 2022 16:38:12 +0100 Subject: [PATCH 06/12] :zap: Refactor validation --- packages/cli/src/Server.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index e84652e640f9f..4a763eafac1c5 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1889,22 +1889,27 @@ class App { const { id: credentialId } = req.query; if (!credentialId) { - res.status(500).send('Required credential ID is missing!'); - return ''; + throw new ResponseHelper.ResponseError( + 'Required credential ID is missing', + undefined, + 400, + ); } const credential = await getCredentialForUser(credentialId, req.user); if (!credential) { - res.status(404).send('The credential is not known.'); - return ''; + throw new ResponseHelper.ResponseError('The credential is not known', undefined, 404); } const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - res.status(500).send('No encryption key found to decrypt the credentials!'); - return ''; + throw new ResponseHelper.ResponseError( + 'No encryption key found to decrypt the credentials', + undefined, + 500, + ); } const mode: WorkflowExecuteMode = 'internal'; @@ -2103,22 +2108,27 @@ class App { const { id: credentialId } = req.query; if (!credentialId) { - res.status(500).send('Required credential ID is missing.'); - return ''; + throw new ResponseHelper.ResponseError( + 'Required credential ID is missing', + undefined, + 400, + ); } const credential = await getCredentialForUser(credentialId, req.user); if (!credential) { - res.status(404).send('The credential is not known.'); - return ''; + throw new ResponseHelper.ResponseError('The credential is not known', undefined, 404); } const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - res.status(500).send('No encryption key found to decrypt the credentials!'); - return ''; + throw new ResponseHelper.ResponseError( + 'No encryption key found to decrypt the credentials', + undefined, + 500, + ); } const mode: WorkflowExecuteMode = 'internal'; From ed764cb41962d82305a08f725c479de03ae1c254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 24 Jan 2022 17:03:50 +0100 Subject: [PATCH 07/12] :zap: Refactor /user-survey --- packages/cli/src/PersonalizationSurvey.ts | 15 +-------------- packages/cli/src/Server.ts | 17 ++++++++++------- packages/cli/src/UserManagement/Interfaces.ts | 3 ++- packages/cli/src/databases/entities/User.ts | 4 ++-- packages/cli/src/requests.d.ts | 3 +++ 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/PersonalizationSurvey.ts b/packages/cli/src/PersonalizationSurvey.ts index b384b4894c758..cfc95c9996437 100644 --- a/packages/cli/src/PersonalizationSurvey.ts +++ b/packages/cli/src/PersonalizationSurvey.ts @@ -1,13 +1,10 @@ -import { readFileSync, writeFile } from 'fs'; -import { promisify } from 'util'; +import { readFileSync } from 'fs'; import { UserSettings } from 'n8n-core'; import * as config from '../config'; // eslint-disable-next-line import/no-cycle import { Db, IPersonalizationSurvey, IPersonalizationSurveyAnswers } from '.'; -const fsWriteFile = promisify(writeFile); - const PERSONALIZATION_SURVEY_FILENAME = 'personalizationSurvey.json'; function loadSurveyFromDisk(): IPersonalizationSurveyAnswers | undefined { @@ -23,16 +20,6 @@ function loadSurveyFromDisk(): IPersonalizationSurveyAnswers | undefined { } } -export async function writeSurveyToDisk( - surveyAnswers: IPersonalizationSurveyAnswers, -): Promise { - const userSettingsPath = UserSettings.getUserN8nFolderPath(); - await fsWriteFile( - `${userSettingsPath}/${PERSONALIZATION_SURVEY_FILENAME}`, - JSON.stringify(surveyAnswers, null, '\t'), - ); -} - export async function preparePersonalizationSurvey(): Promise { const survey: IPersonalizationSurvey = { shouldShow: false, diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 4a763eafac1c5..70d700b21973f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -158,7 +158,7 @@ import { getNodeTranslationPath } from './TranslationHelpers'; import { userManagementRouter } from './UserManagement'; import { User } from './databases/entities/User'; import { CredentialsEntity } from './databases/entities/CredentialsEntity'; -import { NodeParameterOptionsRequest, OAuthRequest } from './requests'; +import { NodeParameterOptionsRequest, OAuthRequest, UserSurveyRequest } from './requests'; require('body-parser-xml')(bodyParser); @@ -2964,22 +2964,25 @@ class App { // Process personalization survey responses this.app.post( `/${this.restEndpoint}/user-survey`, - async (req: express.Request, res: express.Response) => { + async (req: UserSurveyRequest, res: express.Response) => { if (!this.frontendSettings.personalizationSurvey.shouldShow) { // TODO UM: check if this needs permission check for UM - ResponseHelper.sendErrorResponse( + return ResponseHelper.sendErrorResponse( res, new ResponseHelper.ResponseError('User survey already submitted', undefined, 400), false, ); } - const answers = req.body as IPersonalizationSurveyAnswers; - await PersonalizationSurvey.writeSurveyToDisk(answers); + const { body: personalizationAnswers } = req; + await Db.collections.User!.update(req.user.id, { personalizationAnswers }); + this.frontendSettings.personalizationSurvey.shouldShow = false; - this.frontendSettings.personalizationSurvey.answers = answers; + this.frontendSettings.personalizationSurvey.answers = personalizationAnswers; ResponseHelper.sendSuccessResponse(res, undefined, true, 200); - void InternalHooksManager.getInstance().onPersonalizationSurveySubmitted(answers); + void InternalHooksManager.getInstance().onPersonalizationSurveySubmitted( + personalizationAnswers, + ); }, ); diff --git a/packages/cli/src/UserManagement/Interfaces.ts b/packages/cli/src/UserManagement/Interfaces.ts index e286a33370132..084126b6d0532 100644 --- a/packages/cli/src/UserManagement/Interfaces.ts +++ b/packages/cli/src/UserManagement/Interfaces.ts @@ -3,6 +3,7 @@ import { Application } from 'express'; import express = require('express'); import { JwtFromRequestFunction } from 'passport-jwt'; import { User } from '../databases/entities/User'; +import { IPersonalizationSurveyAnswers } from '../Interfaces'; export interface JwtToken { token: string; @@ -20,7 +21,7 @@ export interface PublicUser { email?: string; firstName?: string; lastName?: string; - personalizationAnswers?: { [key: string]: string } | null; + personalizationAnswers?: IPersonalizationSurveyAnswers | null; password?: string; passwordResetToken?: string; } diff --git a/packages/cli/src/databases/entities/User.ts b/packages/cli/src/databases/entities/User.ts index bd2536c001857..eaeadd827f48b 100644 --- a/packages/cli/src/databases/entities/User.ts +++ b/packages/cli/src/databases/entities/User.ts @@ -13,7 +13,7 @@ import { } from 'typeorm'; import { IsEmail } from 'class-validator'; import config = require('../../../config'); -import { DatabaseType } from '../..'; +import { DatabaseType, IPersonalizationSurveyAnswers } from '../..'; import { Role } from './Role'; import { SharedWorkflow } from './SharedWorkflow'; import { SharedCredentials } from './SharedCredentials'; @@ -75,7 +75,7 @@ export class User { type: resolveDataType('json') as ColumnOptions['type'], nullable: true, }) - personalizationAnswers: { [key: string]: string } | null; + personalizationAnswers: IPersonalizationSurveyAnswers | null; @ManyToOne(() => Role, (role) => role.globalForUsers, { cascade: true, diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index e686109122dda..f99ed6818d5ef 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -1,6 +1,7 @@ /* eslint-disable import/no-cycle */ import express = require('express'); import { User } from './databases/entities/User'; +import { IPersonalizationSurveyAnswers } from './Interfaces'; export type AuthenticatedRequest< RouteParams = {}, @@ -33,3 +34,5 @@ export type NodeParameterOptionsRequest = AuthenticatedRequest< credentials: string; } >; + +export type UserSurveyRequest = AuthenticatedRequest<{}, {}, IPersonalizationSurveyAnswers>; From 8a466d559b56a354b9a70d997c18608f191869d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 24 Jan 2022 17:17:04 +0100 Subject: [PATCH 08/12] :shirt: Fix lint --- packages/cli/src/UserManagement/Interfaces.ts | 2 +- packages/cli/src/UserManagement/routes/users.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/UserManagement/Interfaces.ts b/packages/cli/src/UserManagement/Interfaces.ts index f0cfbb958d610..e824e6316e295 100644 --- a/packages/cli/src/UserManagement/Interfaces.ts +++ b/packages/cli/src/UserManagement/Interfaces.ts @@ -1,8 +1,8 @@ /* eslint-disable import/no-cycle */ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { Application } from 'express'; import express = require('express'); import { JwtFromRequestFunction } from 'passport-jwt'; -import { Interface } from 'readline'; import { User } from '../databases/entities/User'; import { IPersonalizationSurveyAnswers } from '../Interfaces'; diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index 05b40ff3e9aaa..4786ba1c8cd8a 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -4,7 +4,7 @@ import { Request, Response } from 'express'; import { getConnection, In } from 'typeorm'; import { LoggerProxy } from 'n8n-workflow'; import { genSaltSync, hashSync } from 'bcryptjs'; -import { Db, GenericHelpers, ICredentialsResponse, ResponseHelper } from '../..'; +import { Db, GenericHelpers, ResponseHelper } from '../..'; import { AuthenticatedRequest, N8nApp, UserRequest } from '../Interfaces'; import { isEmailSetup, isValidEmail, sanitizeUser } from '../UserManagementHelper'; import { User } from '../../databases/entities/User'; From 7dd9a0bacf3b9f80a748abdda1098d150ff80c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 25 Jan 2022 15:52:10 +0100 Subject: [PATCH 09/12] :fire: Remove /user-survey endpoint --- packages/cli/src/Server.ts | 30 ------------------------------ packages/cli/src/requests.d.ts | 3 --- 2 files changed, 33 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b4a960b5199f4..0d3b27f30479a 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -175,7 +175,6 @@ import type { WorkflowRequest, NodeParameterOptionsRequest, OAuthRequest, - UserSurveyRequest, } from './requests'; import { DEFAULT_EXECUTIONS_GET_ALL_LIMIT } from './GenericHelpers'; import { ExecutionEntity } from './databases/entities/ExecutionEntity'; @@ -3013,35 +3012,6 @@ class App { ), ); - // ---------------------------------------- - // User Survey - // ---------------------------------------- - - // Process personalization survey responses - this.app.post( - `/${this.restEndpoint}/user-survey`, - async (req: UserSurveyRequest, res: express.Response) => { - if (!this.frontendSettings.personalizationSurvey.shouldShow) { - // TODO UM: check if this needs permission check for UM - return ResponseHelper.sendErrorResponse( - res, - new ResponseHelper.ResponseError('User survey already submitted', undefined, 400), - false, - ); - } - - const { body: personalizationAnswers } = req; - await Db.collections.User!.update(req.user.id, { personalizationAnswers }); - - this.frontendSettings.personalizationSurvey.shouldShow = false; - this.frontendSettings.personalizationSurvey.answers = personalizationAnswers; - ResponseHelper.sendSuccessResponse(res, undefined, true, 200); - void InternalHooksManager.getInstance().onPersonalizationSurveySubmitted( - personalizationAnswers, - ); - }, - ); - // ---------------------------------------- // Webhooks // ---------------------------------------- diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index fef359c927fa2..3726b1379d168 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -1,7 +1,6 @@ /* eslint-disable import/no-cycle */ import express = require('express'); import { User } from './databases/entities/User'; -import { IPersonalizationSurveyAnswers } from './Interfaces'; import { IExecutionDeleteFilter } from '.'; import { IConnections, INode, IWorkflowSettings } from '../../workflow/dist/src'; @@ -37,8 +36,6 @@ export type NodeParameterOptionsRequest = AuthenticatedRequest< } >; -export type UserSurveyRequest = AuthenticatedRequest<{}, {}, IPersonalizationSurveyAnswers>; - export declare namespace ExecutionRequest { type GetAllQsParam = { filter: string; // '{ waitTill: string; finished: boolean, [other: string]: string }' From e88fbb8d76092798ae26f1f07fcbf8e4301120c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 27 Jan 2022 17:59:05 +0100 Subject: [PATCH 10/12] :zap: Make repeated error messages into constants --- packages/cli/src/Server.ts | 33 +++++++++++++++++++++------------ packages/cli/src/constants.ts | 6 ++++++ 2 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 packages/cli/src/constants.ts diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 0a56455508700..d002e37be353f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -177,6 +177,7 @@ import { DEFAULT_EXECUTIONS_GET_ALL_LIMIT, validateEntity } from './GenericHelpe import { ExecutionEntity } from './databases/entities/ExecutionEntity'; import { SharedWorkflow } from './databases/entities/SharedWorkflow'; import { SharedCredentials } from './databases/entities/SharedCredentials'; +import { RESPONSE_ERROR_MESSAGES } from './constants'; require('body-parser-xml')(bodyParser); @@ -1571,7 +1572,7 @@ class App { const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - throw new Error('No encryption key was found to encrypt the credential!'); + throw new Error(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY); } // Encrypt the data @@ -1746,7 +1747,7 @@ class App { const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - throw new Error('No encryption key was found to encrypt the credential!'); + throw new Error(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY); } const coreCredential = new Credentials( @@ -1840,7 +1841,7 @@ class App { const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - throw new Error('No encryption key was found to decrypt the credentials!'); + throw new Error(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY); } const coreCredential = new Credentials( @@ -1900,7 +1901,7 @@ class App { encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { - throw new Error('No encryption key was found to decrypt the credentials!'); + throw new Error(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY); } } @@ -1989,14 +1990,18 @@ class App { const credential = await getCredentialForUser(credentialId, req.user); if (!credential) { - throw new ResponseHelper.ResponseError('The credential is not known', undefined, 404); + throw new ResponseHelper.ResponseError( + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, + undefined, + 404, + ); } const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { throw new ResponseHelper.ResponseError( - 'No encryption key found to decrypt the credentials', + RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY, undefined, 500, ); @@ -2106,7 +2111,7 @@ class App { if (!credential) { const errorResponse = new ResponseHelper.ResponseError( - 'The credential is not known.', + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, undefined, 404, ); @@ -2117,7 +2122,7 @@ class App { if (!encryptionKey) { const errorResponse = new ResponseHelper.ResponseError( - 'No encryption key found to decrypt the credentials!', + RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY, undefined, 503, ); @@ -2208,14 +2213,18 @@ class App { const credential = await getCredentialForUser(credentialId, req.user); if (!credential) { - throw new ResponseHelper.ResponseError('The credential is not known', undefined, 404); + throw new ResponseHelper.ResponseError( + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, + undefined, + 404, + ); } const encryptionKey = await UserSettings.getEncryptionKey(); if (!encryptionKey) { throw new ResponseHelper.ResponseError( - 'No encryption key found to decrypt the credentials', + RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY, undefined, 500, ); @@ -2335,7 +2344,7 @@ class App { if (!credential) { const errorResponse = new ResponseHelper.ResponseError( - 'The credential is not known.', + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, undefined, 404, ); @@ -2346,7 +2355,7 @@ class App { if (!encryptionKey) { const errorResponse = new ResponseHelper.ResponseError( - 'No encryption key got found to decrypt the credentials!', + RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY, undefined, 503, ); diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts new file mode 100644 index 0000000000000..35eb23bb8f12a --- /dev/null +++ b/packages/cli/src/constants.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + +export const RESPONSE_ERROR_MESSAGES = { + NO_CREDENTIAL: 'Credential not found', + NO_ENCRYPTION_KEY: 'No encryption key found to decrypt the credentials', +}; From 243db6ac79ca20da2a8c347a22c1091d1833e28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 27 Jan 2022 17:59:48 +0100 Subject: [PATCH 11/12] :fire: Remove leftover comment --- packages/cli/src/Server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index d002e37be353f..1830017de8e83 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1269,7 +1269,6 @@ class App { if (req.query.credentials) { credentials = JSON.parse(req.query.credentials); - // TODO UM: restrict user access to credentials he cannot use. } const loadDataInstance = new LoadNodeParameterOptions( From 568ff56ad994a15c34b004ff6cce339522f0a1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 27 Jan 2022 18:00:56 +0100 Subject: [PATCH 12/12] :pencil2: Adjust wording in missing encryption key message --- packages/cli/src/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts index 35eb23bb8f12a..da2dd5cffb59f 100644 --- a/packages/cli/src/constants.ts +++ b/packages/cli/src/constants.ts @@ -2,5 +2,5 @@ export const RESPONSE_ERROR_MESSAGES = { NO_CREDENTIAL: 'Credential not found', - NO_ENCRYPTION_KEY: 'No encryption key found to decrypt the credentials', + NO_ENCRYPTION_KEY: 'Encryption key missing to decrypt credentials', };