diff --git a/backend/src/ee/routes/v1/secret.ts b/backend/src/ee/routes/v1/secret.ts index 4133c9525..200ce35aa 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'; @@ -14,7 +14,7 @@ router.get( requireAuth({ acceptedAuthModes: ['jwt'] }), - requireWorkspaceAuth({ + requireSecretAuth({ acceptedRoles: [ADMIN, MEMBER], acceptedStatuses: [COMPLETED, GRANTED] }), diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 57ddbaeb7..119a86abe 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -7,6 +7,7 @@ import requireIntegrationAuth from './requireIntegrationAuth'; import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizationAuth'; import requireServiceTokenAuth from './requireServiceTokenAuth'; import requireServiceTokenDataAuth from './requireServiceTokenDataAuth'; +import requireSecretAuth from './requireSecretAuth'; import validateRequest from './validateRequest'; export { @@ -19,5 +20,6 @@ export { requireIntegrationAuthorizationAuth, requireServiceTokenAuth, requireServiceTokenDataAuth, + requireSecretAuth, validateRequest }; diff --git a/backend/src/middleware/requireBotAuth.ts b/backend/src/middleware/requireBotAuth.ts index e39f0d1b5..14c099393 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 000000000..8f6fc5305 --- /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 a019be1e9..156800360 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; serviceTokenData: any; diff --git a/backend/src/utils/errors.ts b/backend/src/utils/errors.ts index 2b2ce59a6..f7310d0be 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,7 +111,27 @@ 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 +}); + +//* ----->[ACTION ERRORS]<----- +export const ActionNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'action_not_found_error', + message: error?.message ?? 'The requested action was not found', + context: error?.context, + stack: error?.stack +}); //* ----->[SERVICE TOKEN DATA ERRORS]<----- export const ServiceTokenDataNotFoundError = (error?: Partial) => new RequestError({