From 6f054d8f2cf34f9fa7efd637507de1048c498ef3 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 1 Jan 2023 10:36:07 +0700 Subject: [PATCH] Add requireSecretAuth middleware --- backend/src/ee/routes/v1/secret.ts | 4 +- backend/src/middleware/index.ts | 2 + backend/src/middleware/requireBotAuth.ts | 2 +- backend/src/middleware/requireSecretAuth.ts | 50 +++++++++++++++++++ backend/src/types/express/index.d.ts | 4 +- backend/src/utils/errors.ts | 34 ++++++++----- frontend/components/utilities/attemptLogin.js | 1 + 7 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 backend/src/middleware/requireSecretAuth.ts diff --git a/backend/src/ee/routes/v1/secret.ts b/backend/src/ee/routes/v1/secret.ts index a866a6320e..7217c96e28 100644 --- a/backend/src/ee/routes/v1/secret.ts +++ b/backend/src/ee/routes/v1/secret.ts @@ -2,7 +2,7 @@ import express from 'express'; const router = express.Router(); import { requireAuth, - requireWorkspaceAuth, + requireSecretAuth, validateRequest } from '../../../middleware'; import { body, query, param } from 'express-validator'; @@ -12,7 +12,7 @@ import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../../variables'; router.get( '/:secretId/secret-versions', requireAuth, - requireWorkspaceAuth({ + requireSecretAuth({ acceptedRoles: [ADMIN, MEMBER], acceptedStatuses: [COMPLETED, GRANTED] }), diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 7fcba66e10..bb2cc875c9 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -6,6 +6,7 @@ import requireOrganizationAuth from './requireOrganizationAuth'; import requireIntegrationAuth from './requireIntegrationAuth'; import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizationAuth'; import requireServiceTokenAuth from './requireServiceTokenAuth'; +import requireSecretAuth from './requireSecretAuth'; import validateRequest from './validateRequest'; export { @@ -17,5 +18,6 @@ export { requireIntegrationAuth, requireIntegrationAuthorizationAuth, requireServiceTokenAuth, + requireSecretAuth, validateRequest }; diff --git a/backend/src/middleware/requireBotAuth.ts b/backend/src/middleware/requireBotAuth.ts index e39f0d1b56..14c0993933 100644 --- a/backend/src/middleware/requireBotAuth.ts +++ b/backend/src/middleware/requireBotAuth.ts @@ -15,7 +15,7 @@ const requireBotAuth = ({ location?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { - const bot = await Bot.findOne({ _id: req[location].botId }); + const bot = await Bot.findById(req[location].botId); if (!bot) { return next(AccountNotFoundError({message: 'Failed to locate Bot account'})) diff --git a/backend/src/middleware/requireSecretAuth.ts b/backend/src/middleware/requireSecretAuth.ts new file mode 100644 index 0000000000..8f6fc5305f --- /dev/null +++ b/backend/src/middleware/requireSecretAuth.ts @@ -0,0 +1,50 @@ +import { Request, Response, NextFunction } from 'express'; +import { UnauthorizedRequestError, SecretNotFoundError } from '../utils/errors'; +import { Secret } from '../models'; +import { + validateMembership +} from '../helpers/membership'; + +/** + * Validate if user on request has proper membership to modify secret. + * @param {Object} obj + * @param {String[]} obj.acceptedRoles - accepted workspace roles + * @param {String[]} obj.acceptedStatuses - accepted workspace statuses + * @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing + */ +const requireSecretAuth = ({ + acceptedRoles, + acceptedStatuses +}: { + acceptedRoles: string[]; + acceptedStatuses: string[]; +}) => { + return async (req: Request, res: Response, next: NextFunction) => { + try { + const { secretId } = req.params; + + const secret = await Secret.findById(secretId); + + if (!secret) { + return next(SecretNotFoundError({ + message: 'Failed to find secret' + })); + } + + await validateMembership({ + userId: req.user._id.toString(), + workspaceId: secret.workspace.toString(), + acceptedRoles, + acceptedStatuses + }); + + req.secret = secret as any; + + next(); + } catch (err) { + return next(UnauthorizedRequestError({ message: 'Unable to authenticate secret' })); + } + } +} + +export default requireSecretAuth; \ No newline at end of file diff --git a/backend/src/types/express/index.d.ts b/backend/src/types/express/index.d.ts index 319562fd09..68961a946e 100644 --- a/backend/src/types/express/index.d.ts +++ b/backend/src/types/express/index.d.ts @@ -1,6 +1,5 @@ import * as express from 'express'; - // TODO: fix (any) types declare global { namespace Express { @@ -8,11 +7,12 @@ declare global { user: any; workspace: any; membership: any; - organizationt: any; + organization: any; membershipOrg: any; integration: any; integrationAuth: any; bot: any; + secret: any; serviceToken: any; accessToken: any; query?: any; diff --git a/backend/src/utils/errors.ts b/backend/src/utils/errors.ts index 40c467131e..49afb217af 100644 --- a/backend/src/utils/errors.ts +++ b/backend/src/utils/errors.ts @@ -8,7 +8,7 @@ export const RouteNotFoundError = (error?: Partial) => new message: error?.message ?? 'The requested source was not found', context: error?.context, stack: error?.stack -}) +}); export const MethodNotAllowedError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.INFO, @@ -17,7 +17,7 @@ export const MethodNotAllowedError = (error?: Partial) => n message: error?.message ?? 'The requested method is not allowed for the resource', context: error?.context, stack: error?.stack -}) +}); export const UnauthorizedRequestError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.INFO, @@ -26,7 +26,7 @@ export const UnauthorizedRequestError = (error?: Partial) = message: error?.message ?? 'You are not authorized to access this resource', context: error?.context, stack: error?.stack -}) +}); export const ForbiddenRequestError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.INFO, @@ -35,7 +35,7 @@ export const ForbiddenRequestError = (error?: Partial) => n message: error?.message ?? 'You are not allowed to access this resource', context: error?.context, stack: error?.stack -}) +}); export const BadRequestError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.INFO, @@ -44,7 +44,7 @@ export const BadRequestError = (error?: Partial) => new Req message: error?.message ?? 'The request is invalid or cannot be served', context: error?.context, stack: error?.stack -}) +}); export const InternalServerError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, @@ -53,7 +53,7 @@ export const InternalServerError = (error?: Partial) => new message: error?.message ?? 'The server encountered an error while processing the request', context: error?.context, stack: error?.stack -}) +}); export const ServiceUnavailableError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, @@ -62,7 +62,7 @@ export const ServiceUnavailableError = (error?: Partial) => message: error?.message ?? 'The service is currently unavailable. Please try again later.', context: error?.context, stack: error?.stack -}) +}); export const ValidationError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, @@ -71,7 +71,7 @@ export const ValidationError = (error?: Partial) => new Req message: error?.message ?? 'The request failed validation', context: error?.context, stack: error?.stack -}) +}); //* ----->[INTEGRATION ERRORS]<----- export const IntegrationNotFoundError = (error?: Partial) => new RequestError({ @@ -81,7 +81,7 @@ export const IntegrationNotFoundError = (error?: Partial) = message: error?.message ?? 'The requested integration was not found', context: error?.context, stack: error?.stack -}) +}); //* ----->[WORKSPACE ERRORS]<----- export const WorkspaceNotFoundError = (error?: Partial) => new RequestError({ @@ -91,7 +91,7 @@ export const WorkspaceNotFoundError = (error?: Partial) => message: error?.message ?? 'The requested workspace was not found', context: error?.context, stack: error?.stack -}) +}); //* ----->[ORGANIZATION ERRORS]<----- export const OrganizationNotFoundError = (error?: Partial) => new RequestError({ @@ -101,7 +101,7 @@ export const OrganizationNotFoundError = (error?: Partial) message: error?.message ?? 'The requested organization was not found', context: error?.context, stack: error?.stack -}) +}); //* ----->[ACCOUNT ERRORS]<----- export const AccountNotFoundError = (error?: Partial) => new RequestError({ @@ -111,6 +111,16 @@ export const AccountNotFoundError = (error?: Partial) => ne message: error?.message ?? 'The requested account was not found', context: error?.context, stack: error?.stack -}) +}); + +//* ----->[SECRET ERRORS]<----- +export const SecretNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'secret_not_found_error', + message: error?.message ?? 'The requested secret was not found', + context: error?.context, + stack: error?.stack +}); //* ----->[MISC ERRORS]<----- diff --git a/frontend/components/utilities/attemptLogin.js b/frontend/components/utilities/attemptLogin.js index e1fc5f9cf0..db4725c0d6 100644 --- a/frontend/components/utilities/attemptLogin.js +++ b/frontend/components/utilities/attemptLogin.js @@ -52,6 +52,7 @@ const attemptLogin = async ( // if everything works, go the main dashboard page. const { token, publicKey, encryptedPrivateKey, iv, tag } = await login2(email, clientProof); + SecurityClient.setToken(token); const privateKey = Aes256Gcm.decrypt({