feat: allow secret approvals with user groups

This commit is contained in:
Meet
2024-09-20 08:51:48 +05:30
parent 009be0ded8
commit 081502848d
16 changed files with 248 additions and 21 deletions
@@ -27,13 +27,14 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.nullable()
.default("/")
.transform((val) => (val ? removeTrailingSlash(val) : val)),
approvers: z.string().array().min(1),
approvers: z.string().array().optional().default([]),
groupApprovers: z.string().array().optional().default([]),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
})
.refine((data) => data.approvals <= data.approvers.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
.refine((data) => data.approvers || data.groupApprovers, {
path: ["approvers", "groupApprovers"],
message: "At least one approver should be provided."
}),
response: {
200: z.object({
@@ -70,8 +71,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
body: z
.object({
name: z.string().optional(),
approvers: z.string().array().min(1),
approvers: z.string().array().optional().default([]),
approvals: z.number().min(1).default(1),
groupApprovers: z.string().array().optional().default([]),
secretPath: z
.string()
.optional()
@@ -80,9 +82,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.transform((val) => (val === "" ? "/" : val)),
enforcementLevel: z.nativeEnum(EnforcementLevel).optional()
})
.refine((data) => data.approvals <= data.approvers.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
.refine((data) => data.approvers.length > 0 || data.groupApprovers.length > 0, {
path: ["approvers", "groupApprovers"],
message: "At least one approver should be provided."
}),
response: {
200: z.object({
@@ -151,6 +153,11 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.object({
userId: z.string()
})
.array(),
groupApprovers: z
.object({
groupId: z.string()
})
.array()
})
.array()
@@ -46,7 +46,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
id: z.string(),
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
approvers: z
.object({
userId: z.string()
})
.array(),
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string()
}),
@@ -54,7 +58,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
environment: z.string(),
reviewers: z.object({ userId: z.string(), status: z.string() }).array(),
approvers: z.string().array()
approvers: z
.object({
userId: z.string()
})
.array()
}).array()
})
}
@@ -22,13 +22,29 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
)
.leftJoin(TableName.Users, `${TableName.SecretApprovalPolicyApprover}.approverUserId`, `${TableName.Users}.id`)
.leftJoin(
TableName.SecretApprovalPolicyGroupApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyGroupApprover}.policyId`
)
.leftJoin(
TableName.UserGroupMembership,
`${TableName.SecretApprovalPolicyGroupApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.userId`
)
.select(
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
tx.ref("email").withSchema(TableName.Users).as("approverEmail"),
tx.ref("firstName").withSchema(TableName.Users).as("approverFirstName"),
tx.ref("lastName").withSchema(TableName.Users).as("approverLastName")
)
.select(
tx.ref("approverGroupId").withSchema(TableName.SecretApprovalPolicyGroupApprover),
tx.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"),
tx.ref("email").withSchema(TableName.Users).as("approverGroupEmail"),
tx.ref("firstName").withSchema(TableName.Users).as("approverGroupFirstName"),
tx.ref("lastName").withSchema(TableName.Users).as("approverGroupLastName")
)
.select(
tx.ref("name").withSchema(TableName.Environment).as("envName"),
tx.ref("slug").withSchema(TableName.Environment).as("envSlug"),
@@ -61,6 +77,16 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
firstName: approverFirstName,
lastName: approverLastName
})
},
{
key: "approverGroupUserId",
label: "userApprovers" as const,
mapper: ({ approverGroupUserId, approverGroupEmail, approverGroupFirstName, approverGroupLastName }) => ({
userId: approverGroupUserId,
email: approverGroupEmail,
firstName: approverGroupFirstName,
lastName: approverGroupLastName
})
}
]
});
@@ -89,6 +115,13 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
mapper: ({ approverUserId }) => ({
userId: approverUserId
})
},
{
key: "approverGroupId",
label: "groupApprovers" as const,
mapper: ({ approverGroupId }) => ({
groupId: approverGroupId
})
}
]
});
@@ -0,0 +1,12 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TSecretApprovalPolicyGroupApproverDALFactory = ReturnType<
typeof secretApprovalPolicyGroupApproverDALFactory
>;
export const secretApprovalPolicyGroupApproverDALFactory = (db: TDbClient) => {
const sapGroupApproverOrm = ormify(db, TableName.SecretApprovalPolicyGroupApprover);
return sapGroupApproverOrm;
};
@@ -11,6 +11,7 @@ import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal
import { TLicenseServiceFactory } from "../license/license-service";
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
import { TSecretApprovalPolicyGroupApproverDALFactory } from "./secret-approval-policy-group-approver-dal";
import {
TCreateSapDTO,
TDeleteSapDTO,
@@ -29,6 +30,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
secretApprovalPolicyGroupApproverDAL: TSecretApprovalPolicyGroupApproverDALFactory;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
@@ -38,6 +40,7 @@ export const secretApprovalPolicyServiceFactory = ({
secretApprovalPolicyDAL,
permissionService,
secretApprovalPolicyApproverDAL,
secretApprovalPolicyGroupApproverDAL,
projectEnvDAL,
licenseService
}: TSecretApprovalPolicyServiceFactoryDep) => {
@@ -49,12 +52,16 @@ export const secretApprovalPolicyServiceFactory = ({
actorAuthMethod,
approvals,
approvers,
groupApprovers,
projectId,
secretPath,
environment,
enforcementLevel
}: TCreateSapDTO) => {
if (approvals > approvers.length)
if (!groupApprovers && !approvers)
throw new BadRequestError({ message: "Either of approvers or group approvers must be provided" });
if (!groupApprovers && approvals > approvers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
const { permission } = await permissionService.getProjectPermission(
@@ -91,6 +98,7 @@ export const secretApprovalPolicyServiceFactory = ({
},
tx
);
await secretApprovalPolicyApproverDAL.insertMany(
approvers.map((approverUserId) => ({
approverUserId,
@@ -98,6 +106,14 @@ export const secretApprovalPolicyServiceFactory = ({
})),
tx
);
await secretApprovalPolicyGroupApproverDAL.insertMany(
groupApprovers.map((approverGroupId) => ({
approverGroupId,
policyId: doc.id
})),
tx
);
return doc;
});
return { ...secretApproval, environment: env, projectId };
@@ -105,6 +121,7 @@ export const secretApprovalPolicyServiceFactory = ({
const updateSecretApprovalPolicy = async ({
approvers,
groupApprovers,
secretPath,
name,
actorId,
@@ -156,6 +173,16 @@ export const secretApprovalPolicyServiceFactory = ({
tx
);
}
if (groupApprovers) {
await secretApprovalPolicyGroupApproverDAL.delete({ policyId: doc.id }, tx);
await secretApprovalPolicyGroupApproverDAL.insertMany(
groupApprovers.map((approverGroupId) => ({
approverGroupId,
policyId: doc.id
})),
tx
);
}
return doc;
});
return {
@@ -5,6 +5,7 @@ export type TCreateSapDTO = {
secretPath?: string | null;
environment: string;
approvers: string[];
groupApprovers: string[];
projectId: string;
name: string;
enforcementLevel: EnforcementLevel;
@@ -14,7 +15,8 @@ export type TUpdateSapDTO = {
secretPolicyId: string;
approvals?: number;
secretPath?: string | null;
approvers: string[];
approvers?: string[];
groupApprovers?: string[];
name?: string;
enforcementLevel?: EnforcementLevel;
} & Omit<TProjectPermission, "projectId">;
@@ -48,16 +48,31 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalRequest}.committerUserId`,
`committerUser.id`
)
.join(
.leftJoin(
TableName.SecretApprovalPolicyApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.join<TUsers>(
.leftJoin<TUsers>(
db(TableName.Users).as("secretApprovalPolicyApproverUser"),
`${TableName.SecretApprovalPolicyApprover}.approverUserId`,
"secretApprovalPolicyApproverUser.id"
)
.leftJoin(
TableName.SecretApprovalPolicyGroupApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyGroupApprover}.policyId`
)
.leftJoin(
TableName.UserGroupMembership,
`${TableName.SecretApprovalPolicyGroupApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.groupId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("secretApprovalPolicyGroupApproverUser"),
`${TableName.UserGroupMembership}.userId`,
`secretApprovalPolicyGroupApproverUser.id`
)
.leftJoin(
TableName.SecretApprovalRequestReviewer,
`${TableName.SecretApprovalRequest}.id`,
@@ -71,10 +86,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
.select(selectAllTableCols(TableName.SecretApprovalRequest))
.select(
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
tx.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"),
tx.ref("email").withSchema("secretApprovalPolicyApproverUser").as("approverEmail"),
tx.ref("email").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupEmail"),
tx.ref("username").withSchema("secretApprovalPolicyApproverUser").as("approverUsername"),
tx.ref("username").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupUsername"),
tx.ref("firstName").withSchema("secretApprovalPolicyApproverUser").as("approverFirstName"),
tx.ref("firstName").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupFirstName"),
tx.ref("lastName").withSchema("secretApprovalPolicyApproverUser").as("approverLastName"),
tx.ref("lastName").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupLastName"),
tx.ref("email").withSchema("statusChangedByUser").as("statusChangedByUserEmail"),
tx.ref("username").withSchema("statusChangedByUser").as("statusChangedByUserUsername"),
tx.ref("firstName").withSchema("statusChangedByUser").as("statusChangedByUserFirstName"),
@@ -164,6 +184,23 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
lastName,
username
})
},
{
key: "approverGroupUserId",
label: "approvers" as const,
mapper: ({
approverGroupUserId,
approverGroupEmail: email,
approverGroupUsername: username,
approverGroupLastName: lastName,
approverGroupFirstName: firstName
}) => ({
userId: approverGroupUserId,
email,
firstName,
lastName,
username
})
}
]
});
@@ -236,11 +273,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalRequest}.policyId`,
`${TableName.SecretApprovalPolicy}.id`
)
.join(
.leftJoin(
TableName.SecretApprovalPolicyApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.leftJoin(
TableName.SecretApprovalPolicyGroupApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyGroupApprover}.policyId`
)
.leftJoin(
TableName.UserGroupMembership,
`${TableName.SecretApprovalPolicyGroupApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.groupId`
)
.join<TUsers>(
db(TableName.Users).as("committerUser"),
`${TableName.SecretApprovalRequest}.committerUserId`,
@@ -269,6 +316,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
void bd
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
.orWhere(`${TableName.UserGroupMembership}.userId`, userId)
)
.select(selectAllTableCols(TableName.SecretApprovalRequest))
.select(
@@ -289,6 +337,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
db.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"),
db.ref("email").withSchema("committerUser").as("committerUserEmail"),
db.ref("username").withSchema("committerUser").as("committerUserUsername"),
db.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
@@ -334,7 +383,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
{
key: "approverUserId",
label: "approvers" as const,
mapper: ({ approverUserId }) => approverUserId
mapper: ({ approverUserI: userId }) => ({ userId })
},
{
key: "commitId",
@@ -344,6 +393,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
id,
secretId
})
},
{
key: "approverGroupUserId",
label: "approvers" as const,
mapper: ({ approverGroupUserId: userId }) => ({ userId })
}
]
});
@@ -371,11 +425,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalRequest}.policyId`,
`${TableName.SecretApprovalPolicy}.id`
)
.join(
.leftJoin(
TableName.SecretApprovalPolicyApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.leftJoin(
TableName.SecretApprovalPolicyGroupApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyGroupApprover}.policyId`
)
.leftJoin(
TableName.UserGroupMembership,
`${TableName.SecretApprovalPolicyGroupApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.groupId`
)
.join<TUsers>(
db(TableName.Users).as("committerUser"),
`${TableName.SecretApprovalRequest}.committerUserId`,
@@ -404,6 +468,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
void bd
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
.orWhere(`${TableName.UserGroupMembership}.userId`, userId)
)
.select(selectAllTableCols(TableName.SecretApprovalRequest))
.select(
@@ -424,6 +489,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
db.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"),
db.ref("email").withSchema("committerUser").as("committerUserEmail"),
db.ref("username").withSchema("committerUser").as("committerUserUsername"),
db.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
@@ -469,7 +535,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
{
key: "approverUserId",
label: "approvers" as const,
mapper: ({ approverUserId }) => approverUserId
mapper: ({ approverUserId: userId }) => ({ userId })
},
{
key: "commitId",
@@ -479,6 +545,13 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
id,
secretId
})
},
{
key: "approverGroupUserId",
label: "approvers" as const,
mapper: ({ approverGroupUserId: userId }) => ({
userId
})
}
]
});