Fix: Validate approvers access

This commit is contained in:
Daniel Hougaard
2024-05-07 17:30:27 +02:00
parent eca36f1993
commit 5255c4075a
@@ -3,22 +3,26 @@ import { ForbiddenError } from "@casl/ability";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal"; import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal"; import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
import { verifyApprovers } from "./access-approval-policy-fns";
import { import {
TCreateAccessApprovalPolicy, TCreateAccessApprovalPolicy,
TDeleteAccessApprovalPolicy, TDeleteAccessApprovalPolicy,
TGetAccessPolicyCountByEnvironmentDTO,
TListAccessApprovalPoliciesDTO, TListAccessApprovalPoliciesDTO,
TUpdateAccessApprovalPolicy TUpdateAccessApprovalPolicy
} from "./access-approval-policy-types"; } from "./access-approval-policy-types";
type TSecretApprovalPolicyServiceFactoryDep = { type TSecretApprovalPolicyServiceFactoryDep = {
projectDAL: TProjectDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory; accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">; projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory; accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">; projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
}; };
@@ -30,6 +34,7 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalPolicyApproverDAL, accessApprovalPolicyApproverDAL,
permissionService, permissionService,
projectEnvDAL, projectEnvDAL,
projectDAL,
projectMembershipDAL projectMembershipDAL
}: TSecretApprovalPolicyServiceFactoryDep) => { }: TSecretApprovalPolicyServiceFactoryDep) => {
const createAccessApprovalPolicy = async ({ const createAccessApprovalPolicy = async ({
@@ -37,19 +42,24 @@ export const accessApprovalPolicyServiceFactory = ({
actor, actor,
actorId, actorId,
actorOrgId, actorOrgId,
secretPath,
actorAuthMethod, actorAuthMethod,
approvals, approvals,
approvers, approvers,
projectId, projectSlug,
environment environment
}: TCreateAccessApprovalPolicy) => { }: TCreateAccessApprovalPolicy) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
if (approvals > approvers.length) if (approvals > approvers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
if (!secretPath) throw new BadRequestError({ message: "Secret path is required" });
const { permission } = await permissionService.getProjectPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
projectId, project.id,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
@@ -57,13 +67,24 @@ export const accessApprovalPolicyServiceFactory = ({
ProjectPermissionActions.Create, ProjectPermissionActions.Create,
ProjectPermissionSub.SecretApproval ProjectPermissionSub.SecretApproval
); );
const env = await projectEnvDAL.findOne({ slug: environment, projectId }); const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
if (!env) throw new BadRequestError({ message: "Environment not found" }); if (!env) throw new BadRequestError({ message: "Environment not found" });
const secretApprovers = await projectMembershipDAL.find({ const secretApprovers = await projectMembershipDAL.find({
projectId, projectId: project.id,
$in: { id: approvers } $in: { id: approvers }
}); });
await verifyApprovers({
projectId: project.id,
orgId: actorOrgId,
envSlug: environment,
secretPath,
actorAuthMethod,
permissionService,
approverProjectMemberships: secretApprovers
});
if (secretApprovers.length !== approvers.length) { if (secretApprovers.length !== approvers.length) {
throw new BadRequestError({ message: "Approver not found in project" }); throw new BadRequestError({ message: "Approver not found in project" });
} }
@@ -73,6 +94,7 @@ export const accessApprovalPolicyServiceFactory = ({
{ {
envId: env.id, envId: env.id,
approvals, approvals,
secretPath,
name name
}, },
tx tx
@@ -86,7 +108,7 @@ export const accessApprovalPolicyServiceFactory = ({
); );
return doc; return doc;
}); });
return { ...accessApproval, environment: env, projectId }; return { ...accessApproval, environment: env, projectId: project.id };
}; };
const getAccessApprovalPolicyByProjectId = async ({ const getAccessApprovalPolicyByProjectId = async ({
@@ -94,24 +116,29 @@ export const accessApprovalPolicyServiceFactory = ({
actor, actor,
actorOrgId, actorOrgId,
actorAuthMethod, actorAuthMethod,
projectId projectSlug
}: TListAccessApprovalPoliciesDTO) => { }: TListAccessApprovalPoliciesDTO) => {
const { permission } = await permissionService.getProjectPermission( const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
// Anyone in the project should be able to get the policies.
/* const { permission } = */ await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
projectId, project.id,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); // ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId }); const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id });
return accessApprovalPolicies; return accessApprovalPolicies;
}; };
const updateAccessApprovalPolicy = async ({ const updateAccessApprovalPolicy = async ({
policyId, policyId,
approvers, approvers,
secretPath,
name, name,
actorId, actorId,
actor, actor,
@@ -121,7 +148,6 @@ export const accessApprovalPolicyServiceFactory = ({
}: TUpdateAccessApprovalPolicy) => { }: TUpdateAccessApprovalPolicy) => {
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId); const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
if (!accessApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); if (!accessApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
const { permission } = await permissionService.getProjectPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
@@ -129,6 +155,7 @@ export const accessApprovalPolicyServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => { const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
@@ -136,6 +163,7 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalPolicy.id, accessApprovalPolicy.id,
{ {
approvals, approvals,
secretPath,
name name
}, },
tx tx
@@ -149,6 +177,17 @@ export const accessApprovalPolicyServiceFactory = ({
}, },
{ tx } { tx }
); );
await verifyApprovers({
projectId: accessApprovalPolicy.projectId,
orgId: actorOrgId,
envSlug: accessApprovalPolicy.environment.slug,
secretPath: doc.secretPath!,
actorAuthMethod,
permissionService,
approverProjectMemberships: secretApprovers
});
if (secretApprovers.length !== approvers.length) if (secretApprovers.length !== approvers.length)
throw new BadRequestError({ message: "Approver not found in project" }); throw new BadRequestError({ message: "Approver not found in project" });
if (doc.approvals > secretApprovers.length) if (doc.approvals > secretApprovers.length)
@@ -197,7 +236,31 @@ export const accessApprovalPolicyServiceFactory = ({
return policy; return policy;
}; };
const getAccessPolicyCountByEnvSlug = async ({
actor,
actorOrgId,
actorAuthMethod,
projectSlug,
actorId,
envSlug
}: TGetAccessPolicyCountByEnvironmentDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
await permissionService.getProjectPermission(actor, actorId, project.id, actorAuthMethod, actorOrgId);
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
if (!environment) throw new BadRequestError({ message: "Environment not found" });
const policies = await accessApprovalPolicyDAL.find({ envId: environment.id, projectId: project.id });
if (!policies) throw new BadRequestError({ message: "No policies found" });
return { policyCount: policies.length };
};
return { return {
getAccessPolicyCountByEnvSlug,
createAccessApprovalPolicy, createAccessApprovalPolicy,
deleteAccessApprovalPolicy, deleteAccessApprovalPolicy,
updateAccessApprovalPolicy, updateAccessApprovalPolicy,