diff --git a/App/FeatureSet/Notification/API/PhoneNumber.ts b/App/FeatureSet/Notification/API/PhoneNumber.ts index 17f542265e..46998fa8a3 100644 --- a/App/FeatureSet/Notification/API/PhoneNumber.ts +++ b/App/FeatureSet/Notification/API/PhoneNumber.ts @@ -14,6 +14,7 @@ import ObjectID from "Common/Types/ObjectID"; import IncomingCallPolicyService from "Common/Server/Services/IncomingCallPolicyService"; import ProjectService from "Common/Server/Services/ProjectService"; import UserMiddleware from "Common/Server/Middleware/UserAuthorization"; +import Permission from "Common/Types/Permission"; import Express, { ExpressRequest, ExpressResponse, @@ -33,6 +34,14 @@ router.post( "/search", UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, + UserMiddleware.requirePermission({ + permissions: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.ReadProjectIncomingCallPolicy, + ], + }), async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { const body: JSONObject = req.body as JSONObject; @@ -159,6 +168,14 @@ router.post( "/list-owned", UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, + UserMiddleware.requirePermission({ + permissions: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.ReadProjectIncomingCallPolicy, + ], + }), async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { const body: JSONObject = req.body as JSONObject; @@ -244,6 +261,14 @@ router.post( "/assign-existing", UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, + UserMiddleware.requirePermission({ + permissions: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditProjectIncomingCallPolicy, + ], + }), async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { const body: JSONObject = req.body as JSONObject; @@ -394,6 +419,14 @@ router.post( "/purchase", UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, + UserMiddleware.requirePermission({ + permissions: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditProjectIncomingCallPolicy, + ], + }), async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { const body: JSONObject = req.body as JSONObject; @@ -539,6 +572,14 @@ router.delete( "/release/:incomingCallPolicyId", UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, + UserMiddleware.requirePermission({ + permissions: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditProjectIncomingCallPolicy, + ], + }), async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { const incomingCallPolicyId: ObjectID | undefined = req.params[ diff --git a/Common/Server/Middleware/UserAuthorization.ts b/Common/Server/Middleware/UserAuthorization.ts index d374af7990..6592af4d09 100644 --- a/Common/Server/Middleware/UserAuthorization.ts +++ b/Common/Server/Middleware/UserAuthorization.ts @@ -26,8 +26,11 @@ import { JSONObject } from "../../Types/JSON"; import JSONFunctions from "../../Types/JSONFunctions"; import JSONWebTokenData from "../../Types/JsonWebTokenData"; import ObjectID from "../../Types/ObjectID"; -import { +import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException"; +import Permission, { + PermissionHelper, UserGlobalAccessPermission, + UserPermission, UserTenantAccessPermission, } from "../../Types/Permission"; import UserType from "../../Types/UserType"; @@ -360,6 +363,74 @@ export default class UserMiddleware { return next(); } + public static requirePermission(data: { + permissions: Array; + }): ( + req: ExpressRequest, + res: ExpressResponse, + next: NextFunction, + ) => Promise { + return async ( + req: ExpressRequest, + res: ExpressResponse, + next: NextFunction, + ): Promise => { + const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest; + + // Master admins bypass permission checks + if (oneuptimeRequest.userType === UserType.MasterAdmin) { + return next(); + } + + const tenantId: ObjectID | undefined = oneuptimeRequest.tenantId; + + if (!tenantId) { + return Response.sendErrorResponse( + req, + res, + new NotAuthorizedException( + "Project ID is required to access this resource.", + ), + ); + } + + const userTenantPermission: UserTenantAccessPermission | undefined = + oneuptimeRequest.userTenantAccessPermission?.[tenantId.toString()]; + + if (!userTenantPermission) { + return Response.sendErrorResponse( + req, + res, + new NotAuthorizedException( + "You do not have permission to access this project.", + ), + ); + } + + const userPermissions: Array = + userTenantPermission.permissions.map( + (p: UserPermission) => p.permission, + ); + + if ( + !PermissionHelper.doesPermissionsIntersect( + userPermissions, + data.permissions, + ) + ) { + return Response.sendErrorResponse( + req, + res, + new NotAuthorizedException( + "You do not have the required permission to perform this action.", + ), + ); + } + + return next(); + }; + } + @CaptureSpan() public static async getUserTenantAccessPermissionWithTenantId(data: { req: ExpressRequest;