From 77438f9282f83ef78b79e2bd0393f4383662e3c0 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 19 Nov 2023 18:10:02 +0200 Subject: [PATCH] Replace getUserProjectPermissions with more generic getAuthDataProjectPermissions --- backend/src/controllers/v1/botController.ts | 14 +- .../v1/integrationAuthController.ts | 174 +++++---- .../controllers/v1/integrationController.ts | 37 +- backend/src/controllers/v1/keyController.ts | 8 +- .../controllers/v1/membershipController.ts | 29 +- .../controllers/v1/secretImpsController.ts | 49 ++- .../controllers/v1/secretsFolderController.ts | 25 +- .../src/controllers/v1/webhookController.ts | 44 ++- .../src/controllers/v1/workspaceController.ts | 45 ++- .../controllers/v2/environmentController.ts | 124 ++---- .../controllers/v2/membershipController.ts | 8 +- .../src/controllers/v2/secretsController.ts | 8 +- .../v2/serviceTokenDataController.ts | 18 +- backend/src/controllers/v2/tagController.ts | 24 +- .../src/controllers/v2/workspaceController.ts | 26 +- .../src/controllers/v3/secretsController.ts | 361 ++++++++++-------- .../controllers/v3/workspacesController.ts | 61 ++- .../src/ee/controllers/v1/roleController.ts | 41 +- .../v1/secretApprovalPolicyController.ts | 39 +- .../v1/secretApprovalRequestsController.ts | 73 +++- .../src/ee/controllers/v1/secretController.ts | 18 +- .../v1/secretRotationController.ts | 31 +- .../v1/secretRotationProviderController.ts | 11 +- .../v1/secretSnapshotController.ts | 11 +- .../ee/controllers/v1/workspaceController.ts | 57 ++- .../v3/serviceTokenDataController.ts | 24 +- backend/src/ee/models/auditLog/enums.ts | 2 +- backend/src/ee/models/auditLog/types.ts | 8 +- backend/src/ee/services/ProjectRoleService.ts | 116 ++++-- backend/src/routes/v2/environment.ts | 8 - backend/src/validation/serviceTokenDataV3.ts | 59 --- frontend/src/hooks/api/workspace/index.tsx | 1 - frontend/src/hooks/api/workspace/queries.tsx | 20 +- frontend/src/hooks/api/workspace/types.ts | 5 - 34 files changed, 926 insertions(+), 653 deletions(-) diff --git a/backend/src/controllers/v1/botController.ts b/backend/src/controllers/v1/botController.ts index 65251e83a..3a2ff606a 100644 --- a/backend/src/controllers/v1/botController.ts +++ b/backend/src/controllers/v1/botController.ts @@ -7,7 +7,7 @@ import * as reqValidator from "../../validation/bot"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { BadRequestError } from "../../utils/errors"; @@ -28,7 +28,11 @@ export const getBotByWorkspaceId = async (req: Request, res: Response) => { const { params: { workspaceId } } = await validateRequest(reqValidator.GetBotByWorkspaceIdV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -70,7 +74,11 @@ export const setBotActiveState = async (req: Request, res: Response) => { } const userId = req.user._id; - const { permission } = await getUserProjectPermissions(userId, bot.workspace.toString()); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: bot.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index ec166df48..d04ead63f 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -25,7 +25,7 @@ import * as reqValidator from "../../validation/integrationAuth"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { getIntegrationAuthAccessHelper } from "../../helpers"; @@ -40,15 +40,15 @@ export const getIntegrationAuth = async (req: Request, res: Response) => { const integrationAuth = await IntegrationAuth.findById(integrationAuthId); - if (!integrationAuth) - return res.status(400).send({ - message: "Failed to find integration authorization" - }); + if (!integrationAuth) return res.status(400).send({ + message: "Failed to find integration authorization" + }); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -79,7 +79,11 @@ export const oAuthExchange = async (req: Request, res: Response) => { } = await validateRequest(reqValidator.OauthExchangeV1, req); if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration"); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Integrations @@ -131,7 +135,11 @@ export const saveIntegrationToken = async (req: Request, res: Response) => { body: { workspaceId, integration, url, accessId, namespace, accessToken, refreshToken } } = await validateRequest(reqValidator.SaveIntegrationAccessTokenV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Integrations @@ -224,11 +232,12 @@ export const getIntegrationAuthApps = async (req: Request, res: Response) => { const { integrationAuth, accessToken, accessId } = await getIntegrationAuthAccessHelper({ integrationAuthId: new Types.ObjectId(integrationAuthId) }); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -263,10 +272,11 @@ export const getIntegrationAuthTeams = async (req: Request, res: Response) => { integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -299,10 +309,11 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -360,10 +371,11 @@ export const getIntegrationAuthChecklyGroups = async (req: Request, res: Respons integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -413,10 +425,11 @@ export const getIntegrationAuthQoveryOrgs = async (req: Request, res: Response) integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -465,10 +478,11 @@ export const getIntegrationAuthQoveryProjects = async (req: Request, res: Respon integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -526,10 +540,11 @@ export const getIntegrationAuthQoveryEnvironments = async (req: Request, res: Re integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -587,10 +602,11 @@ export const getIntegrationAuthQoveryApps = async (req: Request, res: Response) integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -648,10 +664,11 @@ export const getIntegrationAuthQoveryContainers = async (req: Request, res: Resp integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -709,10 +726,11 @@ export const getIntegrationAuthQoveryJobs = async (req: Request, res: Response) integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -771,10 +789,11 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -864,10 +883,11 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -988,10 +1008,11 @@ export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: R integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -1044,10 +1065,11 @@ export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -1132,10 +1154,11 @@ export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res: integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -1201,10 +1224,11 @@ export const deleteIntegrationAuth = async (req: Request, res: Response) => { integrationAuthId: new Types.ObjectId(integrationAuthId) }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations diff --git a/backend/src/controllers/v1/integrationController.ts b/backend/src/controllers/v1/integrationController.ts index e709d9d0c..d426be0a1 100644 --- a/backend/src/controllers/v1/integrationController.ts +++ b/backend/src/controllers/v1/integrationController.ts @@ -13,7 +13,7 @@ import * as reqValidator from "../../validation/integration"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; @@ -51,11 +51,12 @@ export const createIntegration = async (req: Request, res: Response) => { ); if (!integrationAuth) throw BadRequestError({ message: "Integration auth not found" }); - - const { permission } = await getUserProjectPermissions( - req.user._id, - integrationAuth.workspace._id.toString() - ); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integrationAuth.workspace._id + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Integrations @@ -164,10 +165,11 @@ export const updateIntegration = async (req: Request, res: Response) => { const integration = await Integration.findById(integrationId); if (!integration) throw BadRequestError({ message: "Integration not found" }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integration.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integration.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations @@ -234,10 +236,11 @@ export const deleteIntegration = async (req: Request, res: Response) => { const integration = await Integration.findById(integrationId); if (!integration) throw BadRequestError({ message: "Integration not found" }); - const { permission } = await getUserProjectPermissions( - req.user._id, - integration.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: integration.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations @@ -285,7 +288,11 @@ export const manualSync = async (req: Request, res: Response) => { body: { workspaceId, environment } } = await validateRequest(reqValidator.ManualSyncV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations diff --git a/backend/src/controllers/v1/keyController.ts b/backend/src/controllers/v1/keyController.ts index 242cb8f82..956487814 100644 --- a/backend/src/controllers/v1/keyController.ts +++ b/backend/src/controllers/v1/keyController.ts @@ -9,7 +9,7 @@ import * as reqValidator from "../../validation/key"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; @@ -26,7 +26,11 @@ export const uploadKey = async (req: Request, res: Response) => { body: { key } } = await validateRequest(reqValidator.UploadKeyV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Member diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index 928d5b50b..cb11c4b74 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -12,7 +12,7 @@ import * as reqValidator from "../../validation/membership"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { BadRequestError } from "../../utils/errors"; @@ -63,11 +63,12 @@ export const deleteMembership = async (req: Request, res: Response) => { if (!membershipToDelete) { throw new Error("Failed to delete workspace membership that doesn't exist"); } - - const { permission } = await getUserProjectPermissions( - req.user._id, - membershipToDelete.workspace.toString() - ); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: membershipToDelete.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Member @@ -118,10 +119,11 @@ export const changeMembershipRole = async (req: Request, res: Response) => { throw new Error("Failed to find membership to change role"); } - const { permission } = await getUserProjectPermissions( - req.user._id, - membershipToChangeRole.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: membershipToChangeRole.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Member @@ -191,7 +193,12 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => { params: { workspaceId }, body: { email } } = await validateRequest(InviteUserToWorkspaceV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Member diff --git a/backend/src/controllers/v1/secretImpsController.ts b/backend/src/controllers/v1/secretImpsController.ts index 80adfc69a..14c99bb03 100644 --- a/backend/src/controllers/v1/secretImpsController.ts +++ b/backend/src/controllers/v1/secretImpsController.ts @@ -1,4 +1,6 @@ + import { Request, Response } from "express"; +import { Types } from "mongoose"; import { isValidScope } from "../../helpers"; import { Folder, IServiceTokenData, SecretImport, ServiceTokenData } from "../../models"; import { getAllImportedSecrets } from "../../services/SecretImportService"; @@ -15,7 +17,7 @@ import * as reqValidator from "../../validation/secretImports"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError, subject } from "@casl/ability"; @@ -105,7 +107,11 @@ export const createSecretImp = async (req: Request, res: Response) => { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } } else { - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) @@ -313,10 +319,11 @@ export const updateSecretImport = async (req: Request, res: Response) => { } } else { // non token entry check - const { permission } = await getUserProjectPermissions( - req.user._id, - importSecDoc.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: importSecDoc.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { @@ -442,10 +449,11 @@ export const deleteSecretImport = async (req: Request, res: Response) => { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } } else { - const { permission } = await getUserProjectPermissions( - req.user._id, - importSecDoc.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: importSecDoc.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { @@ -550,7 +558,11 @@ export const getSecretImports = async (req: Request, res: Response) => { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } } else { - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { @@ -604,7 +616,11 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } } else { - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { @@ -657,10 +673,11 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => { permissionCheckFn = (env: string, secPath: string) => isValidScope(req.authData.authPayload as IServiceTokenData, env, secPath); } else { - const { permission } = await getUserProjectPermissions( - req.user._id, - importSecDoc.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: importSecDoc.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { diff --git a/backend/src/controllers/v1/secretsFolderController.ts b/backend/src/controllers/v1/secretsFolderController.ts index af9ee5277..52627b2e3 100644 --- a/backend/src/controllers/v1/secretsFolderController.ts +++ b/backend/src/controllers/v1/secretsFolderController.ts @@ -17,7 +17,7 @@ import { import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; import * as reqValidator from "../../validation/folders"; @@ -125,7 +125,11 @@ export const createFolder = async (req: Request, res: Response) => { } } else { // user check - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) @@ -332,7 +336,11 @@ export const updateFolderById = async (req: Request, res: Response) => { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } } else { - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) @@ -502,7 +510,11 @@ export const deleteFolder = async (req: Request, res: Response) => { } } else { // check that user is a member of the workspace - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) @@ -649,7 +661,10 @@ export const getFolders = async (req: Request, res: Response) => { } } else { // check that user is a member of the workspace - await getUserProjectPermissions(req.user._id, workspaceId); + await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); } const folders = await Folder.findOne({ workspace: workspaceId, environment }); diff --git a/backend/src/controllers/v1/webhookController.ts b/backend/src/controllers/v1/webhookController.ts index 09175ebfe..852a3aeed 100644 --- a/backend/src/controllers/v1/webhookController.ts +++ b/backend/src/controllers/v1/webhookController.ts @@ -16,7 +16,7 @@ import * as reqValidator from "../../validation/webhooks"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; @@ -26,7 +26,11 @@ export const createWebhook = async (req: Request, res: Response) => { body: { webhookUrl, webhookSecretKey, environment, workspaceId, secretPath } } = await validateRequest(reqValidator.CreateWebhookV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks @@ -98,11 +102,11 @@ export const updateWebhook = async (req: Request, res: Response) => { if (!webhook) { throw BadRequestError({ message: "Webhook not found!!" }); } - - const { permission } = await getUserProjectPermissions( - req.user._id, - webhook.workspace.toString() - ); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: webhook.workspace + }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks @@ -146,10 +150,11 @@ export const deleteWebhook = async (req: Request, res: Response) => { throw ResourceNotFoundError({ message: "Webhook not found!!" }); } - const { permission } = await getUserProjectPermissions( - req.user._id, - webhook.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: webhook.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks @@ -193,10 +198,11 @@ export const testWebhook = async (req: Request, res: Response) => { throw BadRequestError({ message: "Webhook not found!!" }); } - const { permission } = await getUserProjectPermissions( - req.user._id, - webhook.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: webhook.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks @@ -236,8 +242,12 @@ export const listWebhooks = async (req: Request, res: Response) => { const { query: { environment, workspaceId, secretPath } } = await validateRequest(reqValidator.ListWebhooksV1, req); - - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks diff --git a/backend/src/controllers/v1/workspaceController.ts b/backend/src/controllers/v1/workspaceController.ts index 1b3a9fd3f..9d3ae5b80 100644 --- a/backend/src/controllers/v1/workspaceController.ts +++ b/backend/src/controllers/v1/workspaceController.ts @@ -25,7 +25,7 @@ import * as reqValidator from "../../validation"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; /** @@ -39,7 +39,11 @@ export const getWorkspacePublicKeys = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspacePublicKeysV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Member @@ -72,7 +76,11 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceMembershipsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Member @@ -195,7 +203,11 @@ export const deleteWorkspace = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.DeleteWorkspaceV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace @@ -223,7 +235,11 @@ export const changeWorkspaceName = async (req: Request, res: Response) => { body: { name } } = await validateRequest(reqValidator.ChangeWorkspaceNameV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace @@ -257,7 +273,12 @@ export const getWorkspaceIntegrations = async (req: Request, res: Response) => { const { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceIntegrationsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -283,7 +304,11 @@ export const getWorkspaceIntegrationAuthorizations = async (req: Request, res: R params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceIntegrationAuthorizationsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Integrations @@ -309,7 +334,11 @@ export const getWorkspaceServiceTokens = async (req: Request, res: Response) => params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceServiceTokensV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts index 130e2e225..3b412638b 100644 --- a/backend/src/controllers/v2/environmentController.ts +++ b/backend/src/controllers/v2/environmentController.ts @@ -12,14 +12,12 @@ import { import { EventType, SecretVersion } from "../../ee/models"; import { EEAuditLogService, EELicenseService } from "../../ee/services"; import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors"; -import _ from "lodash"; -import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables"; import { validateRequest } from "../../helpers/validation"; import * as reqValidator from "../../validation/environments"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { SecretImport } from "../../models"; @@ -114,7 +112,11 @@ export const createWorkspaceEnvironment = async (req: Request, res: Response) => body: { environmentName, environmentSlug } } = await validateRequest(reqValidator.CreateWorkspaceEnvironmentV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Environments @@ -191,7 +193,11 @@ export const reorderWorkspaceEnvironments = async (req: Request, res: Response) body: { environmentName, environmentSlug, otherEnvironmentSlug, otherEnvironmentName } } = await validateRequest(reqValidator.ReorderWorkspaceEnvironmentsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Environments @@ -322,7 +328,11 @@ export const renameWorkspaceEnvironment = async (req: Request, res: Response) => body: { environmentName, environmentSlug, oldEnvironmentSlug } } = await validateRequest(reqValidator.UpdateWorkspaceEnvironmentV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Environments @@ -511,7 +521,11 @@ export const deleteWorkspaceEnvironment = async (req: Request, res: Response) => body: { environmentSlug } } = await validateRequest(reqValidator.DeleteWorkspaceEnvironmentV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Environments @@ -587,98 +601,4 @@ export const deleteWorkspaceEnvironment = async (req: Request, res: Response) => workspace: workspaceId, environment: environmentSlug }); -}; - -// TODO(akhilmhdh) after rbac this can be completely removed -export const getAllAccessibleEnvironmentsOfWorkspace = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Get all accessible environments of a workspace' - #swagger.description = 'Fetch all environments that the user has access to in a specified workspace' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of the workspace", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessibleEnvironments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "Development" - }, - "slug": { - "type": "string", - "example": "development" - }, - "isWriteDenied": { - "type": "boolean", - "example": false - }, - "isReadDenied": { - "type": "boolean", - "example": false - } - } - } - } - }, - "description": "List of environments the user has access to in the specified workspace" - } - } - } - } - */ - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetAllAccessibileEnvironmentsOfWorkspaceV2, req); - - const { membership: workspacesUserIsMemberOf } = await getUserProjectPermissions( - req.user._id, - workspaceId - ); - - const accessibleEnvironments: any = []; - const deniedPermission = workspacesUserIsMemberOf.deniedPermissions; - - const relatedWorkspace = await Workspace.findById(workspaceId); - if (!relatedWorkspace) { - throw BadRequestError(); - } - relatedWorkspace.environments.forEach((environment) => { - const isReadBlocked = _.some(deniedPermission, { - environmentSlug: environment.slug, - ability: PERMISSION_READ_SECRETS - }); - const isWriteBlocked = _.some(deniedPermission, { - environmentSlug: environment.slug, - ability: PERMISSION_WRITE_SECRETS - }); - if (isReadBlocked && isWriteBlocked) { - return; - } else { - accessibleEnvironments.push({ - name: environment.name, - slug: environment.slug, - isWriteDenied: isWriteBlocked, - isReadDenied: isReadBlocked - }); - } - }); - - res.json({ accessibleEnvironments }); -}; +}; \ No newline at end of file diff --git a/backend/src/controllers/v2/membershipController.ts b/backend/src/controllers/v2/membershipController.ts index 537134963..d6d25dad5 100644 --- a/backend/src/controllers/v2/membershipController.ts +++ b/backend/src/controllers/v2/membershipController.ts @@ -8,7 +8,7 @@ import { EEAuditLogService } from "../../ee/services"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { sendMail } from "../../helpers"; import { validateRequest } from "../../helpers/validation"; @@ -27,7 +27,11 @@ export const addUserToWorkspace = async (req: Request, res: Response) => { if (!workspace) throw new Error("Failed to find workspace"); // check permission - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Member diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index b0362f6a4..362221bf4 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -39,7 +39,7 @@ import { import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError, subject } from "@casl/ability"; @@ -159,7 +159,11 @@ export const batchSecrets = async (req: Request, res: Response) => { } // not using service token using auth if (!(req.authData.authPayload instanceof ServiceTokenData)) { - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + if (createSecrets.length) ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, diff --git a/backend/src/controllers/v2/serviceTokenDataController.ts b/backend/src/controllers/v2/serviceTokenDataController.ts index b8f1b8f43..b49639d3f 100644 --- a/backend/src/controllers/v2/serviceTokenDataController.ts +++ b/backend/src/controllers/v2/serviceTokenDataController.ts @@ -11,7 +11,7 @@ import * as reqValidator from "../../validation/serviceTokenData"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { Types } from "mongoose"; @@ -75,7 +75,12 @@ export const createServiceTokenData = async (req: Request, res: Response) => { const { body: { workspaceId, permissions, tag, encryptedKey, scopes, name, expiresIn, iv } } = await validateRequest(reqValidator.CreateServiceTokenV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens @@ -151,10 +156,11 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => { let serviceTokenData = await ServiceTokenData.findById(serviceTokenDataId); if (!serviceTokenData) throw BadRequestError({ message: "Service token not found" }); - const { permission } = await getUserProjectPermissions( - req.user._id, - serviceTokenData.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: serviceTokenData.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens diff --git a/backend/src/controllers/v2/tagController.ts b/backend/src/controllers/v2/tagController.ts index 668903222..c803b0e18 100644 --- a/backend/src/controllers/v2/tagController.ts +++ b/backend/src/controllers/v2/tagController.ts @@ -7,7 +7,7 @@ import { validateRequest } from "../../helpers/validation"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import * as reqValidator from "../../validation/tags"; @@ -17,7 +17,11 @@ export const createWorkspaceTag = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.CreateWorkspaceTagsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.Tags @@ -45,10 +49,11 @@ export const deleteWorkspaceTag = async (req: Request, res: Response) => { throw BadRequestError(); } - const { permission } = await getUserProjectPermissions( - req.user._id, - tagFromDB.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: tagFromDB.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Tags @@ -66,7 +71,12 @@ export const getWorkspaceTags = async (req: Request, res: Response) => { const { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceTagsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Tags diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index 040c68f31..8c5e70952 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -16,7 +16,7 @@ import * as reqValidator from "../../validation"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; @@ -272,7 +272,11 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceMembershipsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.Member @@ -352,7 +356,11 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) => body: { role } } = await validateRequest(reqValidator.UpdateWorkspaceMembershipsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Member @@ -420,7 +428,11 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) => params: { workspaceId, membershipId } } = await validateRequest(reqValidator.DeleteWorkspaceMembershipsV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.Member @@ -452,7 +464,11 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => { body: { autoCapitalization } } = await validateRequest(reqValidator.ToggleAutoCapitalizationV2, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.Settings diff --git a/backend/src/controllers/v3/secretsController.ts b/backend/src/controllers/v3/secretsController.ts index 6bb4dcaa8..3eb1b3245 100644 --- a/backend/src/controllers/v3/secretsController.ts +++ b/backend/src/controllers/v3/secretsController.ts @@ -6,7 +6,7 @@ import { BotService } from "../../services"; import { containsGlobPatterns, repackageSecretToRaw } from "../../helpers/secrets"; import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; import { getAllImportedSecrets } from "../../services/SecretImportService"; -import { Folder, IMembership, IServiceTokenData, IServiceTokenDataV3 } from "../../models"; +import { Folder, IServiceTokenData, Membership, User } from "../../models"; import { getFolderByPath } from "../../services/FolderService"; import { BadRequestError } from "../../utils/errors"; import { validateRequest } from "../../helpers/validation"; @@ -14,13 +14,10 @@ import * as reqValidator from "../../validation/secrets"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError, subject } from "@casl/ability"; -import { - validateServiceTokenDataClientForWorkspace, - validateServiceTokenDataV3ClientForWorkspace -} from "../../validation"; +import { validateServiceTokenDataClientForWorkspace } from "../../validation"; import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables"; import { ActorType } from "../../ee/models"; import { UnauthorizedRequestError } from "../../utils/errors"; @@ -30,7 +27,6 @@ import { getSecretPolicyOfBoard } from "../../ee/services/SecretApprovalService"; import { CommitType } from "../../ee/models/secretApprovalRequest"; -import { IRole } from "../../ee/models/role"; const checkSecretsPermission = async ({ authData, @@ -46,7 +42,6 @@ const checkSecretsPermission = async ({ secretAction: ProjectPermissionActions; // CRUD }): Promise<{ authVerifier: (env: string, secPath: string) => boolean; - membership?: Omit & { customRole: IRole }; }> => { let STV2RequiredPermissions = []; @@ -67,10 +62,11 @@ const checkSecretsPermission = async ({ switch (authData.actor.type) { case ActorType.USER: { - const { permission, membership } = await getUserProjectPermissions( - authData.actor.metadata.userId, - workspaceId - ); + const { permission } = await getAuthDataProjectPermissions({ + authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( secretAction, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) @@ -83,8 +79,7 @@ const checkSecretsPermission = async ({ environment: env, secretPath: secPath }) - ), - membership + ) }; } case ActorType.SERVICE: { @@ -98,27 +93,25 @@ const checkSecretsPermission = async ({ return { authVerifier: () => true }; } case ActorType.SERVICE_V3: { - - // TODO: redo this part - await validateServiceTokenDataV3ClientForWorkspace({ + const { permission } = await getAuthDataProjectPermissions({ authData, - serviceTokenData: authData.authPayload as IServiceTokenDataV3, - workspaceId: new Types.ObjectId(workspaceId), - environment, + workspaceId: new Types.ObjectId(workspaceId) }); - // TODO: return an authVerifier - - // return { - // authVerifier: (env: string, secPath: string) => - // isValidScopeV3({ - // authPayload: authData.authPayload as IServiceTokenDataV3, - // environment: env, - // secretPath: secPath, - // requiredPermissions: STV3RequiredPermissions - // }) - // }; - return { authVerifier: () => true } + ForbiddenError.from(permission).throwUnlessCan( + secretAction, + subject(ProjectPermissionSub.Secrets, { environment, secretPath }) + ); + return { + authVerifier: (env: string, secPath: string) => + permission.can( + secretAction, + subject(ProjectPermissionSub.Secrets, { + environment: env, + secretPath: secPath + }) + ) + }; } default: { throw UnauthorizedRequestError(); @@ -898,7 +891,7 @@ export const createSecret = async (req: Request, res: Response) => { params: { secretName } } = await validateRequest(reqValidator.CreateSecretV3, req); - const { membership } = await checkSecretsPermission({ + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -906,35 +899,42 @@ export const createSecret = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Create }); - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - authData: req.authData, - data: { - [CommitType.CREATE]: [ - { - secretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership && type !== "personal") { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + authData: req.authData, + data: { + [CommitType.CREATE]: [ + { + secretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV + } + ] + } + }); + return res.send({ approval: secretApprovalRequest }); + } } } @@ -1007,7 +1007,7 @@ export const updateSecretByName = async (req: Request, res: Response) => { throw BadRequestError({ message: "Missing encrypted key" }); } - const { membership } = await checkSecretsPermission({ + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -1015,37 +1015,44 @@ export const updateSecretByName = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Edit }); - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - authData: req.authData, - data: { - [CommitType.UPDATE]: [ - { - secretName, - newSecretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - tags, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership && type !== "personal") { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + authData: req.authData, + data: { + [CommitType.UPDATE]: [ + { + secretName, + newSecretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + tags, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV + } + ] + } + }); + return res.send({ approval: secretApprovalRequest }); + } } } @@ -1095,7 +1102,7 @@ export const deleteSecretByName = async (req: Request, res: Response) => { params: { secretName } } = await validateRequest(reqValidator.DeleteSecretByNameV3, req); - const { membership } = await checkSecretsPermission({ + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -1103,25 +1110,32 @@ export const deleteSecretByName = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Delete }); - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - authData: req.authData, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.DELETE]: [ - { - secretName - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership && type !== "personal") { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + authData: req.authData, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + data: { + [CommitType.DELETE]: [ + { + secretName + } + ] + } + }); + return res.send({ approval: secretApprovalRequest }); + } } } @@ -1153,7 +1167,7 @@ export const createSecretByNameBatch = async (req: Request, res: Response) => { body: { secrets, secretPath, environment, workspaceId } } = await validateRequest(reqValidator.CreateSecretByNameBatchV3, req); - const { membership } = await checkSecretsPermission({ + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -1161,21 +1175,28 @@ export const createSecretByNameBatch = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Create }); - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - authData: req.authData, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.CREATE]: secrets.filter(({ type }) => type === "shared") - } - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership) { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + authData: req.authData, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + data: { + [CommitType.CREATE]: secrets.filter(({ type }) => type === "shared") + } + }); + return res.send({ approval: secretApprovalRequest }); + } } } @@ -1205,7 +1226,7 @@ export const updateSecretByNameBatch = async (req: Request, res: Response) => { body: { secrets, secretPath, environment, workspaceId } } = await validateRequest(reqValidator.UpdateSecretByNameBatchV3, req); - const { membership } = await checkSecretsPermission({ + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -1213,21 +1234,28 @@ export const updateSecretByNameBatch = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Edit }); - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.UPDATE]: secrets.filter(({ type }) => type === "shared") - }, - authData: req.authData - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership) { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + data: { + [CommitType.UPDATE]: secrets.filter(({ type }) => type === "shared") + }, + authData: req.authData + }); + return res.send({ approval: secretApprovalRequest }); + } } } @@ -1256,8 +1284,8 @@ export const deleteSecretByNameBatch = async (req: Request, res: Response) => { const { body: { secrets, secretPath, environment, workspaceId } } = await validateRequest(reqValidator.DeleteSecretByNameBatchV3, req); - - const { membership } = await checkSecretsPermission({ + + await checkSecretsPermission({ authData: req.authData, workspaceId, environment, @@ -1265,21 +1293,28 @@ export const deleteSecretByNameBatch = async (req: Request, res: Response) => { secretAction: ProjectPermissionActions.Delete }); - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.DELETE]: secrets.filter(({ type }) => type === "shared") - }, - authData: req.authData - }); - return res.send({ approval: secretApprovalRequest }); + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (membership) { + const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); + if (secretApprovalPolicy) { + const secretApprovalRequest = await generateSecretApprovalRequest({ + workspaceId, + environment, + secretPath, + policy: secretApprovalPolicy, + commiterMembershipId: membership._id.toString(), + data: { + [CommitType.DELETE]: secrets.filter(({ type }) => type === "shared") + }, + authData: req.authData + }); + return res.send({ approval: secretApprovalRequest }); + } } } diff --git a/backend/src/controllers/v3/workspacesController.ts b/backend/src/controllers/v3/workspacesController.ts index f1edca1f7..c47fdf619 100644 --- a/backend/src/controllers/v3/workspacesController.ts +++ b/backend/src/controllers/v3/workspacesController.ts @@ -1,9 +1,9 @@ import { Request, Response } from "express"; import { Types } from "mongoose"; import { validateRequest } from "../../helpers/validation"; -import { Secret, ServiceTokenDataV3 } from "../../models"; +import { Membership, Secret, ServiceTokenDataV3, User } from "../../models"; import { SecretService } from "../../services"; -import { getUserProjectPermissions } from "../../ee/services/ProjectRoleService"; +import { getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; import { UnauthorizedRequestError } from "../../utils/errors"; import * as reqValidator from "../../validation/workspace"; @@ -19,9 +19,22 @@ export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response) params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceBlinkIndexStatusV3, req); - const { membership } = await getUserProjectPermissions(req.user._id, workspaceId); - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); + await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!membership) throw UnauthorizedRequestError(); + + if (membership.role !== "admin") + throw UnauthorizedRequestError({ message: "User must be an admin" }); + } const secretsWithoutBlindIndex = await Secret.countDocuments({ workspace: new Types.ObjectId(workspaceId), @@ -41,9 +54,22 @@ export const getWorkspaceSecrets = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceSecretsV3, req); - const { membership } = await getUserProjectPermissions(req.user._id, workspaceId); - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); + await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!membership) throw UnauthorizedRequestError(); + + if (membership.role !== "admin") + throw UnauthorizedRequestError({ message: "User must be an admin" }); + } const secrets = await Secret.find({ workspace: new Types.ObjectId(workspaceId) @@ -65,9 +91,22 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => { body: { secretsToUpdate } } = await validateRequest(reqValidator.NameWorkspaceSecretsV3, req); - const { membership } = await getUserProjectPermissions(req.user._id, workspaceId); - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); + await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + + if (req.authData.authPayload instanceof User) { + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!membership) throw UnauthorizedRequestError(); + + if (membership.role !== "admin") + throw UnauthorizedRequestError({ message: "User must be an admin" }); + } // get secret blind index salt const salt = await SecretService.getSecretBlindIndexSalt({ diff --git a/backend/src/ee/controllers/v1/roleController.ts b/backend/src/ee/controllers/v1/roleController.ts index edc31273a..cef14e79f 100644 --- a/backend/src/ee/controllers/v1/roleController.ts +++ b/backend/src/ee/controllers/v1/roleController.ts @@ -1,4 +1,6 @@ import { Request, Response } from "express"; +import { Types } from "mongoose"; +import { Membership, User } from "../../../models"; import { CreateRoleSchema, DeleteRoleSchema, @@ -11,7 +13,7 @@ import { ProjectPermissionActions, ProjectPermissionSub, adminProjectPermissions, - getUserProjectPermissions, + getAuthDataProjectPermissions, memberProjectPermissions, viewerProjectPermission } from "../../services/ProjectRoleService"; @@ -39,7 +41,10 @@ export const createRole = async (req: Request, res: Response) => { throw BadRequestError({ message: "user doesn't have the permission." }); } } else { - const { permission } = await getUserProjectPermissions(req.user.id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); if (permission.cannot(ProjectPermissionActions.Create, ProjectPermissionSub.Role)) { throw BadRequestError({ message: "User doesn't have the permission." }); } @@ -82,7 +87,11 @@ export const updateRole = async (req: Request, res: Response) => { throw BadRequestError({ message: "User doesn't have the org permission." }); } } else { - const { permission } = await getUserProjectPermissions(req.user.id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + if (permission.cannot(ProjectPermissionActions.Edit, ProjectPermissionSub.Role)) { throw BadRequestError({ message: "User doesn't have the workspace permission." }); } @@ -134,7 +143,11 @@ export const deleteRole = async (req: Request, res: Response) => { throw BadRequestError({ message: "User doesn't have the org permission." }); } } else { - const { permission } = await getUserProjectPermissions(req.user.id, role.workspace.toString()); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: role.workspace + }); + if (permission.cannot(ProjectPermissionActions.Delete, ProjectPermissionSub.Role)) { throw BadRequestError({ message: "User doesn't have the workspace permission." }); } @@ -162,7 +175,11 @@ export const getRoles = async (req: Request, res: Response) => { throw BadRequestError({ message: "User doesn't have the org permission." }); } } else { - const { permission } = await getUserProjectPermissions(req.user.id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + if (permission.cannot(ProjectPermissionActions.Read, ProjectPermissionSub.Role)) { throw BadRequestError({ message: "User doesn't have the workspace permission." }); } @@ -227,7 +244,19 @@ export const getUserWorkspacePermissions = async (req: Request, res: Response) = const { params: { workspaceId } } = await validateRequest(GetUserProjectPermission, req); - const { permission, membership } = await getUserProjectPermissions(req.user._id, workspaceId); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + + let membership; + if (req.authData.authPayload instanceof User) { + membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }) + } res.status(200).json({ data: { diff --git a/backend/src/ee/controllers/v1/secretApprovalPolicyController.ts b/backend/src/ee/controllers/v1/secretApprovalPolicyController.ts index eb2f48718..11f1ed557 100644 --- a/backend/src/ee/controllers/v1/secretApprovalPolicyController.ts +++ b/backend/src/ee/controllers/v1/secretApprovalPolicyController.ts @@ -1,10 +1,11 @@ +import { Types } from "mongoose"; import { ForbiddenError, subject } from "@casl/ability"; import { Request, Response } from "express"; import { nanoid } from "nanoid"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { validateRequest } from "../../../helpers/validation"; import { SecretApprovalPolicy } from "../../models/secretApprovalPolicy"; @@ -19,7 +20,11 @@ export const createSecretApprovalPolicy = async (req: Request, res: Response) => body: { approvals, secretPath, approvers, environment, workspaceId, name } } = await validateRequest(reqValidator.CreateSecretApprovalRule, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval @@ -49,10 +54,11 @@ export const updateSecretApprovalPolicy = async (req: Request, res: Response) => const secretApproval = await SecretApprovalPolicy.findById(id); if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND; - const { permission } = await getUserProjectPermissions( - req.user._id, - secretApproval.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: secretApproval.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval @@ -78,10 +84,11 @@ export const deleteSecretApprovalPolicy = async (req: Request, res: Response) => const secretApproval = await SecretApprovalPolicy.findById(id); if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND; - const { permission } = await getUserProjectPermissions( - req.user._id, - secretApproval.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: secretApproval.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval @@ -99,7 +106,11 @@ export const getSecretApprovalPolicy = async (req: Request, res: Response) => { query: { workspaceId } } = await validateRequest(reqValidator.GetSecretApprovalRuleList, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval @@ -117,7 +128,11 @@ export const getSecretApprovalPolicyOfBoard = async (req: Request, res: Response query: { workspaceId, environment, secretPath } } = await validateRequest(reqValidator.GetSecretApprovalPolicyOfABoard, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { secretPath, environment }) diff --git a/backend/src/ee/controllers/v1/secretApprovalRequestsController.ts b/backend/src/ee/controllers/v1/secretApprovalRequestsController.ts index be2e59202..915df5d7c 100644 --- a/backend/src/ee/controllers/v1/secretApprovalRequestsController.ts +++ b/backend/src/ee/controllers/v1/secretApprovalRequestsController.ts @@ -1,7 +1,6 @@ import { Request, Response } from "express"; -import { getUserProjectPermissions } from "../../services/ProjectRoleService"; import { validateRequest } from "../../../helpers/validation"; -import { Folder } from "../../../models"; +import { Folder, Membership, User } from "../../../models"; import { ApprovalStatus, SecretApprovalRequest } from "../../models/secretApprovalRequest"; import * as reqValidator from "../../validation/secretApprovalRequest"; import { getFolderWithPathFromId } from "../../../services/FolderService"; @@ -17,7 +16,15 @@ export const getSecretApprovalRequestCount = async (req: Request, res: Response) query: { workspaceId } } = await validateRequest(reqValidator.getSecretApprovalRequestCount, req); - const { membership } = await getUserProjectPermissions(req.user._id, workspaceId); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!membership) throw UnauthorizedRequestError(); + const approvalRequestCount = await SecretApprovalRequest.aggregate([ { $match: { @@ -65,7 +72,14 @@ export const getSecretApprovalRequests = async (req: Request, res: Response) => query: { status, committer, workspaceId, environment, limit, offset } } = await validateRequest(reqValidator.getSecretApprovalRequests, req); - const { membership } = await getUserProjectPermissions(req.user._id, workspaceId); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!membership) throw UnauthorizedRequestError(); const query = { workspace: new Types.ObjectId(workspaceId), @@ -148,10 +162,15 @@ export const getSecretApprovalRequestDetails = async (req: Request, res: Respons if (!secretApprovalRequest) throw BadRequestError({ message: "Secret approval request not found" }); - const { membership } = await getUserProjectPermissions( - req.user._id, - secretApprovalRequest.workspace.toString() - ); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: secretApprovalRequest.workspace + }); + + if (!membership) throw UnauthorizedRequestError(); + // allow to fetch only if its admin or is the committer or approver if ( membership.role !== "admin" && @@ -190,10 +209,15 @@ export const updateSecretApprovalReviewStatus = async (req: Request, res: Respon if (!secretApprovalRequest) throw BadRequestError({ message: "Secret approval request not found" }); - const { membership } = await getUserProjectPermissions( - req.user._id, - secretApprovalRequest.workspace.toString() - ); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: secretApprovalRequest.workspace + }); + + if (!membership) throw UnauthorizedRequestError(); + if ( membership.role !== "admin" && secretApprovalRequest.committer !== membership.id && @@ -227,10 +251,15 @@ export const mergeSecretApprovalRequest = async (req: Request, res: Response) => if (!secretApprovalRequest) throw BadRequestError({ message: "Secret approval request not found" }); - const { membership } = await getUserProjectPermissions( - req.user._id, - secretApprovalRequest.workspace.toString() - ); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: secretApprovalRequest.workspace + }); + + if (!membership) throw UnauthorizedRequestError(); + if ( membership.role !== "admin" && secretApprovalRequest.committer !== membership.id && @@ -272,10 +301,14 @@ export const updateSecretApprovalRequestStatus = async (req: Request, res: Respo if (!secretApprovalRequest) throw BadRequestError({ message: "Secret approval request not found" }); - const { membership } = await getUserProjectPermissions( - req.user._id, - secretApprovalRequest.workspace.toString() - ); + if (!(req.authData.authPayload instanceof User)) return; + + const membership = await Membership.findOne({ + user: req.authData.authPayload._id, + workspace: secretApprovalRequest.workspace + }); + + if (!membership) throw UnauthorizedRequestError(); if ( membership.role !== "admin" && diff --git a/backend/src/ee/controllers/v1/secretController.ts b/backend/src/ee/controllers/v1/secretController.ts index 42aa07f3d..ec6018929 100644 --- a/backend/src/ee/controllers/v1/secretController.ts +++ b/backend/src/ee/controllers/v1/secretController.ts @@ -5,7 +5,7 @@ import { Folder, Secret } from "../../../models"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { BadRequestError } from "../../../utils/errors"; import * as reqValidator from "../../../validation"; @@ -74,7 +74,11 @@ export const getSecretVersions = async (req: Request, res: Response) => { throw BadRequestError({ message: "Failed to find secret" }); } - const { permission } = await getUserProjectPermissions(req.user._id, secret.workspace.toString()); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: secret.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback @@ -157,10 +161,12 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => { if (!toBeUpdatedSec) { throw BadRequestError({ message: "Failed to find secret" }); } - const { permission } = await getUserProjectPermissions( - req.user._id, - toBeUpdatedSec.workspace.toString() - ); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: toBeUpdatedSec.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback diff --git a/backend/src/ee/controllers/v1/secretRotationController.ts b/backend/src/ee/controllers/v1/secretRotationController.ts index a8a2fd2dd..f2f80ffe6 100644 --- a/backend/src/ee/controllers/v1/secretRotationController.ts +++ b/backend/src/ee/controllers/v1/secretRotationController.ts @@ -1,11 +1,12 @@ import { Request, Response } from "express"; +import { Types } from "mongoose"; import { validateRequest } from "../../../helpers/validation"; import * as reqValidator from "../../validation/secretRotation"; import * as secretRotationService from "../../secretRotation/service"; import { - getUserProjectPermissions, ProjectPermissionActions, - ProjectPermissionSub + ProjectPermissionSub, + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; @@ -23,7 +24,11 @@ export const createSecretRotation = async (req: Request, res: Response) => { } } = await validateRequest(reqValidator.createSecretRotationV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRotation @@ -49,7 +54,12 @@ export const restartSecretRotations = async (req: Request, res: Response) => { } = await validateRequest(reqValidator.restartSecretRotationV1, req); const doc = await secretRotationService.getSecretRotationById({ id }); - const { permission } = await getUserProjectPermissions(req.user._id, doc.workspace.toString()); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: doc.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation @@ -65,7 +75,12 @@ export const deleteSecretRotations = async (req: Request, res: Response) => { } = await validateRequest(reqValidator.removeSecretRotationV1, req); const doc = await secretRotationService.getSecretRotationById({ id }); - const { permission } = await getUserProjectPermissions(req.user._id, doc.workspace.toString()); + + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: doc.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation @@ -80,7 +95,11 @@ export const getSecretRotations = async (req: Request, res: Response) => { query: { workspaceId } } = await validateRequest(reqValidator.getSecretRotationV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation diff --git a/backend/src/ee/controllers/v1/secretRotationProviderController.ts b/backend/src/ee/controllers/v1/secretRotationProviderController.ts index 7d62ed5f5..5e66c40b2 100644 --- a/backend/src/ee/controllers/v1/secretRotationProviderController.ts +++ b/backend/src/ee/controllers/v1/secretRotationProviderController.ts @@ -1,11 +1,12 @@ import { Request, Response } from "express"; +import { Types } from "mongoose"; import { validateRequest } from "../../../helpers/validation"; import * as reqValidator from "../../validation/secretRotationProvider"; import * as secretRotationProviderService from "../../secretRotation/service"; import { - getUserProjectPermissions, ProjectPermissionActions, - ProjectPermissionSub + ProjectPermissionSub, + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; @@ -14,7 +15,11 @@ export const getProviderTemplates = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(reqValidator.getSecretRotationProvidersV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation diff --git a/backend/src/ee/controllers/v1/secretSnapshotController.ts b/backend/src/ee/controllers/v1/secretSnapshotController.ts index e39555929..34a1a6ef2 100644 --- a/backend/src/ee/controllers/v1/secretSnapshotController.ts +++ b/backend/src/ee/controllers/v1/secretSnapshotController.ts @@ -4,7 +4,7 @@ import { validateRequest } from "../../../helpers/validation"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import * as reqValidator from "../../../validation/secretSnapshot"; import { ISecretVersion, SecretSnapshot, TFolderRootVersionSchema } from "../../models"; @@ -33,10 +33,11 @@ export const getSecretSnapshot = async (req: Request, res: Response) => { if (!secretSnapshot) throw new Error("Failed to find secret snapshot"); - const { permission } = await getUserProjectPermissions( - req.user._id, - secretSnapshot.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: secretSnapshot.workspace + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts index b26882d74..7d5caae9b 100644 --- a/backend/src/ee/controllers/v1/workspaceController.ts +++ b/backend/src/ee/controllers/v1/workspaceController.ts @@ -27,7 +27,6 @@ import { } from "../../models"; import { EESecretService } from "../../services"; import { getLatestSecretVersionIds } from "../../helpers/secretVersion"; -// import Folder, { TFolderSchema } from "../../../models/folder"; import { getFolderByPath, searchByFolderId } from "../../../services/FolderService"; import { EEAuditLogService, EELicenseService } from "../../services"; import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip"; @@ -46,7 +45,7 @@ import { import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { BadRequestError } from "../../../utils/errors"; @@ -107,7 +106,11 @@ export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) = query: { environment, directory, offset, limit } } = await validateRequest(GetWorkspaceSecretSnapshotsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback @@ -148,7 +151,11 @@ export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Respon query: { environment, directory } } = await validateRequest(GetWorkspaceSecretSnapshotsCountV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback @@ -238,7 +245,11 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons body: { directory, environment, version } } = await validateRequest(RollbackWorkspaceSecretSnapshotV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback @@ -572,7 +583,11 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(GetWorkspaceAuditLogsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs @@ -632,7 +647,11 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res params: { workspaceId } } = await validateRequest(GetWorkspaceAuditLogActorFilterOptsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs @@ -700,7 +719,11 @@ export const getWorkspaceTrustedIps = async (req: Request, res: Response) => { params: { workspaceId } } = await validateRequest(GetWorkspaceTrustedIpsV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList @@ -726,7 +749,11 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => { body: { comment, isActive, ipAddress: ip } } = await validateRequest(AddWorkspaceTrustedIpV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList @@ -792,7 +819,11 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => { body: { ipAddress: ip, comment } } = await validateRequest(UpdateWorkspaceTrustedIpV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, ProjectPermissionSub.IpAllowList @@ -884,7 +915,11 @@ export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => { params: { workspaceId, trustedIpId } } = await validateRequest(DeleteWorkspaceTrustedIpV1, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.IpAllowList diff --git a/backend/src/ee/controllers/v3/serviceTokenDataController.ts b/backend/src/ee/controllers/v3/serviceTokenDataController.ts index bbd960912..3e97c9afe 100644 --- a/backend/src/ee/controllers/v3/serviceTokenDataController.ts +++ b/backend/src/ee/controllers/v3/serviceTokenDataController.ts @@ -20,7 +20,7 @@ import { createToken } from "../../../helpers/auth"; import { ProjectPermissionActions, ProjectPermissionSub, - getUserProjectPermissions + getAuthDataProjectPermissions } from "../../services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; import { BadRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../../utils/errors"; @@ -170,7 +170,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => { nonce, // for ServiceTokenDataV3Key } } = await validateRequest(reqValidator.CreateServiceTokenV3, req); - const { permission } = await getUserProjectPermissions(req.user._id, workspaceId); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: new Types.ObjectId(workspaceId) + }); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens @@ -303,10 +307,10 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { message: "Service token not found" }); - const { permission } = await getUserProjectPermissions( - req.user._id, - serviceTokenData.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: serviceTokenData.workspace + }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, @@ -422,10 +426,10 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => { message: "Service token not found" }); - const { permission } = await getUserProjectPermissions( - req.user._id, - serviceTokenData.workspace.toString() - ); + const { permission } = await getAuthDataProjectPermissions({ + authData: req.authData, + workspaceId: serviceTokenData.workspace + }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, diff --git a/backend/src/ee/models/auditLog/enums.ts b/backend/src/ee/models/auditLog/enums.ts index 6f9971ef7..48147db08 100644 --- a/backend/src/ee/models/auditLog/enums.ts +++ b/backend/src/ee/models/auditLog/enums.ts @@ -2,7 +2,7 @@ export enum ActorType { USER = "user", SERVICE = "service", SERVICE_V3 = "service-v3", - Machine = "machine" + // Machine = "machine" } export enum UserAgentType { diff --git a/backend/src/ee/models/auditLog/types.ts b/backend/src/ee/models/auditLog/types.ts index d628773ca..bae4e2f96 100644 --- a/backend/src/ee/models/auditLog/types.ts +++ b/backend/src/ee/models/auditLog/types.ts @@ -26,11 +26,11 @@ export interface ServiceActorV3 { metadata: ServiceActorMetadata; } -export interface MachineActor { - type: ActorType.Machine; -} +// export interface MachineActor { +// type: ActorType.Machine; +// } -export type Actor = UserActor | ServiceActor | ServiceActorV3 | MachineActor; +export type Actor = UserActor | ServiceActor | ServiceActorV3; interface GetSecretsEvent { type: EventType.GET_SECRETS; diff --git a/backend/src/ee/services/ProjectRoleService.ts b/backend/src/ee/services/ProjectRoleService.ts index ac71121de..4e882d00f 100644 --- a/backend/src/ee/services/ProjectRoleService.ts +++ b/backend/src/ee/services/ProjectRoleService.ts @@ -1,3 +1,4 @@ +import { Types } from "mongoose"; import { AbilityBuilder, ForcedSubject, @@ -6,11 +7,14 @@ import { buildMongoQueryMatcher, createMongoAbility } from "@casl/ability"; -import { Membership } from "../../models"; -import { IRole } from "../models/role"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; +import { UnauthorizedRequestError } from "../../utils/errors"; import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js"; import picomatch from "picomatch"; +import { AuthData } from "../../interfaces/middleware"; +import { ActorType, IRole } from "../models"; +import { Membership, ServiceTokenData, ServiceTokenDataV3 } from "../../models"; +import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables"; +import { checkIPAgainstBlocklist } from "../../utils/ip"; const $glob: FieldInstruction = { type: "field", @@ -239,31 +243,89 @@ const buildViewerPermission = () => { export const viewerProjectPermission = buildViewerPermission(); -export const getUserProjectPermissions = async (userId: string, workspaceId: string) => { - // TODO(akhilmhdh): speed this up by pulling from cache later - const membership = await Membership.findOne({ - user: userId, - workspace: workspaceId - }) - .populate<{ - customRole: IRole & { permissions: RawRuleOf>[] }; - }>("customRole") - .exec(); +/** + * Return permissions for user/service pertaining to workspace with id [workspaceId] + * + * Note: should not rely on this function for ST V2 authorization logic + * b/c ST V2 does not support role-based access control + */ +export const getAuthDataProjectPermissions = async ({ + authData, + workspaceId +}: { + authData: AuthData; + workspaceId: Types.ObjectId; +}) => { + let role: "admin" | "member" | "viewer" | "custom"; + let customRole; + + switch (authData.actor.type) { + case ActorType.USER: { + const membership = await Membership.findOne({ + user: authData.authPayload._id, + workspace: workspaceId + }) + .populate<{ + customRole: IRole & { permissions: RawRuleOf>[] }; + }>("customRole") + .exec(); + + if (!membership || (membership.role === "custom" && !membership.customRole)) { + throw UnauthorizedRequestError(); + } + + role = membership.role; + customRole = membership.customRole; + break; + } + case ActorType.SERVICE: { + const serviceTokenData = await ServiceTokenData.findById(authData.authPayload._id); + if (!serviceTokenData || !serviceTokenData.workspace.equals(workspaceId)) throw UnauthorizedRequestError(); + role = "viewer"; + break; + } + case ActorType.SERVICE_V3: { + const serviceTokenData = await ServiceTokenDataV3 + .findById(authData.authPayload._id) + .populate<{ + customRole: IRole & { permissions: RawRuleOf>[] }; + }>("customRole") + .exec(); + + if (!serviceTokenData || (serviceTokenData.role === "custom" && !serviceTokenData.customRole)) { + throw UnauthorizedRequestError(); + } - if (!membership || (membership.role === "custom" && !membership.customRole)) { - throw UnauthorizedRequestError({ message: "User doesn't belong to organization" }); + checkIPAgainstBlocklist({ + ipAddress: authData.ipAddress, + trustedIps: serviceTokenData.trustedIps + }); + + role = serviceTokenData.role; + customRole = serviceTokenData.customRole; + break; + } + default: + throw UnauthorizedRequestError(); } - if (membership.role === "admin") return { permission: adminProjectPermissions, membership }; - if (membership.role === "member") return { permission: memberProjectPermissions, membership }; - if (membership.role === "viewer") return { permission: viewerProjectPermission, membership }; - - if (membership.role === "custom") { - const permission = createMongoAbility(membership.customRole.permissions, { - conditionsMatcher - }); - return { permission, membership }; + switch (role) { + case ADMIN: + return { permission: adminProjectPermissions }; + case MEMBER: + return { permission: memberProjectPermissions }; + case VIEWER: + return { permission: viewerProjectPermission }; + case CUSTOM: { + if (!customRole) throw UnauthorizedRequestError(); + return { + permission: createMongoAbility( + customRole.permissions, + { conditionsMatcher } + ) + }; + } + default: + throw UnauthorizedRequestError(); } - - throw BadRequestError({ message: "User role not found" }); -}; +} diff --git a/backend/src/routes/v2/environment.ts b/backend/src/routes/v2/environment.ts index 9682a04a2..49e1786b3 100644 --- a/backend/src/routes/v2/environment.ts +++ b/backend/src/routes/v2/environment.ts @@ -36,12 +36,4 @@ router.delete( environmentController.deleteWorkspaceEnvironment ); -router.get( - "/:workspaceId/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - environmentController.getAllAccessibleEnvironmentsOfWorkspace -); - export default router; diff --git a/backend/src/validation/serviceTokenDataV3.ts b/backend/src/validation/serviceTokenDataV3.ts index bfbd795e8..e4b2603b3 100644 --- a/backend/src/validation/serviceTokenDataV3.ts +++ b/backend/src/validation/serviceTokenDataV3.ts @@ -1,65 +1,6 @@ -import { Types } from "mongoose"; -import { IServiceTokenDataV3 } from "../models"; import { z } from "zod"; -import { UnauthorizedRequestError } from "../utils/errors"; -import { AuthData } from "../interfaces/middleware"; -import { checkIPAgainstBlocklist } from "../utils/ip"; import { MEMBER } from "../variables"; -/** - * Validate that service token (client) can access workspace - * with id [workspaceId] and its environment [environment] with required permissions - * [requiredPermissions] - * @param {Object} obj - * @param {ServiceTokenData} obj.serviceTokenData - service token client - * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against - * @param {String} environment - (optional) environment in workspace to validate against - * @param {String[]} acceptedPermissions - accepted permissions as part of the endpoint - */ - export const validateServiceTokenDataV3ClientForWorkspace = async ({ - authData, - serviceTokenData, - workspaceId, - environment, - // secretPath = "/", -}: { - authData: AuthData; - serviceTokenData: IServiceTokenDataV3; - workspaceId: Types.ObjectId; - environment?: string; - // secretPath?: string; -}) => { - - // validate ST V3 IP address - checkIPAgainstBlocklist({ - ipAddress: authData.ipAddress, - trustedIps: serviceTokenData.trustedIps - }); - - if (!serviceTokenData.workspace.equals(workspaceId)) { - // case: invalid workspaceId passed - throw UnauthorizedRequestError({ - message: "Failed service token authorization for the given workspace" - }); - } - - if (environment) { - - // TODO: validation fun for ST V3 - - // const isValid = isValidScopeV3({ - // authPayload: serviceTokenData, - // environment, - // secretPath, - // requiredPermissions - // }); - - // if (!isValid) throw UnauthorizedRequestError({ - // message: "Failed service token authorization for the given workspace" - // }); - } -}; - export const RefreshTokenV3 = z.object({ body: z.object({ refresh_token: z.string().trim() diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index f7b749156..cef88fede 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -7,7 +7,6 @@ export { useDeleteWsEnvironment, useGetUserWorkspaceMemberships, useGetUserWorkspaces, - useGetUserWsEnvironments, useGetWorkspaceAuthorizations, useGetWorkspaceById, useGetWorkspaceIndexStatus, diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 5b185b97a..ede931de8 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -12,14 +12,12 @@ import { CreateWorkspaceDTO, DeleteEnvironmentDTO, DeleteWorkspaceDTO, - GetWsEnvironmentDTO, NameWorkspaceSecretsDTO, RenameWorkspaceDTO, ReorderEnvironmentsDTO, ToggleAutoCapitalizationDTO, UpdateEnvironmentDTO, - Workspace, - WorkspaceEnv + Workspace } from "./types"; export const workspaceKeys = { @@ -31,7 +29,6 @@ export const workspaceKeys = { getWorkspaceAuthorization: (workspaceId: string) => [{ workspaceId }, "workspace-authorizations"], getWorkspaceIntegrations: (workspaceId: string) => [{ workspaceId }, "workspace-integrations"], getAllUserWorkspace: ["workspaces"] as const, - getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const, getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const, getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const, getWorkspaceServiceTokenDataV3: (workspaceId: string) => @@ -103,21 +100,6 @@ const fetchUserWorkspaceMemberships = async (orgId: string) => { return data; }; -const fetchUserWsEnvironments = async (workspaceId: string) => { - const { data } = await apiRequest.get<{ accessibleEnvironments: WorkspaceEnv[] }>( - `/api/v2/workspace/${workspaceId}/environments` - ); - return data.accessibleEnvironments; -}; - -export const useGetUserWsEnvironments = ({ workspaceId, onSuccess }: GetWsEnvironmentDTO) => - useQuery({ - enabled: Boolean(workspaceId), - onSuccess, - queryKey: workspaceKeys.getUserWsEnvironments(workspaceId), - queryFn: () => fetchUserWsEnvironments(workspaceId) - }); - // to get all userids in an org with the workspace they are part of export const useGetUserWorkspaceMemberships = (orgId: string) => useQuery({ diff --git a/frontend/src/hooks/api/workspace/types.ts b/frontend/src/hooks/api/workspace/types.ts index f6728988a..bebf5119f 100644 --- a/frontend/src/hooks/api/workspace/types.ts +++ b/frontend/src/hooks/api/workspace/types.ts @@ -30,11 +30,6 @@ export type CreateWorkspaceDTO = { organizationId: string; }; -export type GetWsEnvironmentDTO = { - workspaceId: string; - onSuccess?: (data: WorkspaceEnv[]) => void; -}; - export type RenameWorkspaceDTO = { workspaceID: string; newWorkspaceName: string }; export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean };