mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 07:30:20 +00:00
feat: ui changes for approval to work
This commit is contained in:
@@ -47,12 +47,11 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.max(100, "Cannot have more than 100 bypassers")
|
||||
.optional(),
|
||||
approvalsRequired: z
|
||||
.record(
|
||||
z.number().int(),
|
||||
z.object({
|
||||
numberOfApprovals: z.number().int()
|
||||
})
|
||||
)
|
||||
.object({
|
||||
numberOfApprovals: z.number().int(),
|
||||
stepNumber: z.number().int()
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
approvals: z.number().min(1).default(1),
|
||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||
@@ -95,7 +94,12 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
approvals: sapPubSchema
|
||||
.extend({
|
||||
approvers: z
|
||||
.object({ type: z.nativeEnum(ApproverType), id: z.string().nullable().optional() })
|
||||
.object({
|
||||
type: z.nativeEnum(ApproverType),
|
||||
id: z.string().nullable().optional(),
|
||||
sequence: z.number().nullable().optional(),
|
||||
approvalsRequired: z.number().nullable().optional()
|
||||
})
|
||||
.array()
|
||||
.nullable()
|
||||
.optional(),
|
||||
@@ -169,8 +173,17 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.transform((val) => (val === "" ? "/" : val)),
|
||||
approvers: z
|
||||
.discriminatedUnion("type", [
|
||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
||||
z.object({
|
||||
type: z.literal(ApproverType.Group),
|
||||
id: z.string(),
|
||||
sequence: z.number().int().default(1)
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(ApproverType.User),
|
||||
id: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
sequence: z.number().int().default(1)
|
||||
})
|
||||
])
|
||||
.array()
|
||||
.min(1, { message: "At least one approver should be provided" })
|
||||
@@ -187,12 +200,11 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||
allowedSelfApprovals: z.boolean().default(true),
|
||||
approvalsRequired: z
|
||||
.record(
|
||||
z.number().int(),
|
||||
z.object({
|
||||
numberOfApprovals: z.number().int()
|
||||
})
|
||||
)
|
||||
.object({
|
||||
numberOfApprovals: z.number().int(),
|
||||
stepNumber: z.number().int()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
@@ -260,7 +272,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.object({
|
||||
type: z.nativeEnum(ApproverType),
|
||||
id: z.string().nullable().optional(),
|
||||
name: z.string().nullable().optional()
|
||||
name: z.string().nullable().optional(),
|
||||
approvalsRequired: z.number().nullable().optional()
|
||||
})
|
||||
.array()
|
||||
.nullable()
|
||||
|
||||
@@ -112,7 +112,15 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
approvals: z.number(),
|
||||
approvers: z.string().array(),
|
||||
approvers: z
|
||||
.object({
|
||||
userId: z.string().nullable().optional(),
|
||||
sequence: z.number().nullable().optional(),
|
||||
approvalsRequired: z.number().nullable().optional(),
|
||||
email: z.string().nullable().optional(),
|
||||
username: z.string().nullable().optional()
|
||||
})
|
||||
.array(),
|
||||
bypassers: z.string().array(),
|
||||
secretPath: z.string().nullish(),
|
||||
envId: z.string(),
|
||||
|
||||
@@ -48,6 +48,9 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
.select(tx.ref("username").withSchema("bypasserUsers").as("bypasserUsername"))
|
||||
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(tx.ref("approverGroupId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(tx.ref("sequence").withSchema(TableName.AccessApprovalPolicyApprover).as("approverSequence"))
|
||||
.select(tx.ref("approvalsRequired").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(tx.ref("approverGroupId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(tx.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||
.select(tx.ref("bypasserGroupId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||
@@ -80,17 +83,21 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverUserId: id }) => ({
|
||||
mapper: ({ approverUserId: id, approverSequence, approvalsRequired }) => ({
|
||||
id,
|
||||
type: "user"
|
||||
type: "user",
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
},
|
||||
{
|
||||
key: "approverGroupId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverGroupId: id }) => ({
|
||||
mapper: ({ approverGroupId: id, approverSequence, approvalsRequired }) => ({
|
||||
id,
|
||||
type: "group"
|
||||
type: "group",
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
}
|
||||
]
|
||||
@@ -129,18 +136,22 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverUserId: id, approverUsername }) => ({
|
||||
mapper: ({ approverUserId: id, approverUsername, approverSequence, approvalsRequired }) => ({
|
||||
id,
|
||||
type: ApproverType.User,
|
||||
name: approverUsername
|
||||
name: approverUsername,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
},
|
||||
{
|
||||
key: "approverGroupId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverGroupId: id }) => ({
|
||||
mapper: ({ approverGroupId: id, approverSequence, approvalsRequired }) => ({
|
||||
id,
|
||||
type: ApproverType.Group
|
||||
type: ApproverType.Group,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||
@@ -30,7 +31,6 @@ import {
|
||||
TListAccessApprovalPoliciesDTO,
|
||||
TUpdateAccessApprovalPolicy
|
||||
} from "./access-approval-policy-types";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
|
||||
type TAccessApprovalPolicyServiceFactoryDep = {
|
||||
projectDAL: TProjectDALFactory;
|
||||
@@ -44,7 +44,7 @@ type TAccessApprovalPolicyServiceFactoryDep = {
|
||||
userDAL: Pick<TUserDALFactory, "find">;
|
||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find">;
|
||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update">;
|
||||
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
||||
};
|
||||
|
||||
@@ -176,6 +176,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const approvalsRequiredGroupByStepNumber = groupBy(approvalsRequired || [], (i) => i.stepNumber);
|
||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||
const doc = await accessApprovalPolicyDAL.create(
|
||||
{
|
||||
@@ -195,7 +196,9 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
approverUserId: el.id,
|
||||
policyId: doc.id,
|
||||
sequence: el.sequence,
|
||||
approvalsRequired: el.sequence ? approvalsRequired?.[el.sequence]?.numberOfApprovals : approvals
|
||||
approvalsRequired: el.sequence
|
||||
? approvalsRequiredGroupByStepNumber?.[el.sequence]?.[0]?.numberOfApprovals
|
||||
: approvals
|
||||
})),
|
||||
tx
|
||||
);
|
||||
@@ -207,7 +210,9 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
approverGroupId: el.id,
|
||||
policyId: doc.id,
|
||||
sequence: el.sequence,
|
||||
approvalsRequired: el.sequence ? approvalsRequired?.[el.sequence]?.numberOfApprovals : approvals
|
||||
approvalsRequired: el.sequence
|
||||
? approvalsRequiredGroupByStepNumber?.[el.sequence]?.[0]?.numberOfApprovals
|
||||
: approvals
|
||||
})),
|
||||
tx
|
||||
);
|
||||
@@ -284,7 +289,6 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
id: string;
|
||||
sequence?: number;
|
||||
}[];
|
||||
|
||||
const userApproverNames = approvers.filter(
|
||||
(approver) => approver.type === ApproverType.User && approver.username
|
||||
) as { username: string; sequence?: number }[];
|
||||
@@ -385,6 +389,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const approvalsRequiredGroupByStepNumber = groupBy(approvalsRequired || [], (i) => i.stepNumber);
|
||||
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||
const doc = await accessApprovalPolicyDAL.updateById(
|
||||
accessApprovalPolicy.id,
|
||||
@@ -427,13 +432,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
await accessApprovalPolicyApproverDAL.insertMany(
|
||||
approverUserIds.map((el) => ({
|
||||
approverUserId: el.id,
|
||||
policyId: doc.id,
|
||||
sequence: el.sequence,
|
||||
approvalsRequired: el.sequence ? approvalsRequired?.[el.sequence]?.numberOfApprovals : approvals
|
||||
approvalsRequired: el.sequence
|
||||
? approvalsRequiredGroupByStepNumber?.[el.sequence]?.[0]?.numberOfApprovals
|
||||
: approvals
|
||||
})),
|
||||
tx
|
||||
);
|
||||
@@ -445,7 +451,9 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
approverGroupId: el.id,
|
||||
policyId: doc.id,
|
||||
sequence: el.sequence,
|
||||
approvalsRequired: el.sequence ? approvalsRequired?.[el.sequence]?.numberOfApprovals : approvals
|
||||
approvalsRequired: el.sequence
|
||||
? approvalsRequiredGroupByStepNumber?.[el.sequence]?.[0]?.numberOfApprovals
|
||||
: approvals
|
||||
})),
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -39,7 +39,7 @@ export type TCreateAccessApprovalPolicy = {
|
||||
name: string;
|
||||
enforcementLevel: EnforcementLevel;
|
||||
allowedSelfApprovals: boolean;
|
||||
approvalsRequired?: Record<number, { numberOfApprovals: number }>;
|
||||
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAccessApprovalPolicy = {
|
||||
@@ -57,7 +57,7 @@ export type TUpdateAccessApprovalPolicy = {
|
||||
name?: string;
|
||||
enforcementLevel?: EnforcementLevel;
|
||||
allowedSelfApprovals: boolean;
|
||||
approvalsRequired?: Record<number, { numberOfApprovals: number }>;
|
||||
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TDeleteAccessApprovalPolicy = {
|
||||
|
||||
@@ -39,12 +39,16 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
`${TableName.AccessApprovalRequest}.id`,
|
||||
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
||||
)
|
||||
|
||||
.leftJoin(
|
||||
TableName.AccessApprovalPolicyApprover,
|
||||
`${TableName.AccessApprovalPolicy}.id`,
|
||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
||||
)
|
||||
.leftJoin<TUsers>(
|
||||
db(TableName.Users).as("accessApprovalPolicyApproverUser"),
|
||||
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
||||
"accessApprovalPolicyApproverUser.id"
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.UserGroupMembership,
|
||||
`${TableName.AccessApprovalPolicyApprover}.approverGroupId`,
|
||||
@@ -82,13 +86,18 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"),
|
||||
db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
||||
)
|
||||
|
||||
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(db.ref("sequence").withSchema(TableName.AccessApprovalPolicyApprover).as("approverSequence"))
|
||||
.select(db.ref("approvalsRequired").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"))
|
||||
|
||||
.select(db.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||
.select(db.ref("userId").withSchema("bypasserUserGroupMembership").as("bypasserGroupUserId"))
|
||||
|
||||
.select(
|
||||
db.ref("email").withSchema("accessApprovalPolicyApproverUser").as("approverEmail"),
|
||||
db.ref("email").withSchema(TableName.Users).as("approverGroupEmail"),
|
||||
db.ref("username").withSchema("accessApprovalPolicyApproverUser").as("approverUsername"),
|
||||
db.ref("username").withSchema(TableName.Users).as("approverGroupUsername")
|
||||
)
|
||||
.select(
|
||||
db.ref("projectId").withSchema(TableName.Environment),
|
||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||
@@ -173,11 +182,33 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
label: "reviewers" as const,
|
||||
mapper: ({ reviewerUserId: userId, reviewerStatus: status }) => (userId ? { userId, status } : undefined)
|
||||
},
|
||||
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId },
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverUserId, approverSequence, approvalsRequired, approverUsername, approverEmail }) => ({
|
||||
userId: approverUserId,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired,
|
||||
email: approverEmail,
|
||||
username: approverUsername
|
||||
})
|
||||
},
|
||||
{
|
||||
key: "approverGroupUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverGroupUserId }) => approverGroupUserId
|
||||
mapper: ({
|
||||
approverGroupUserId,
|
||||
approverSequence,
|
||||
approvalsRequired,
|
||||
approverGroupEmail,
|
||||
approverGroupUsername
|
||||
}) => ({
|
||||
userId: approverGroupUserId,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired,
|
||||
email: approverGroupEmail,
|
||||
username: approverGroupUsername
|
||||
})
|
||||
},
|
||||
{ key: "bypasserUserId", label: "bypassers" as const, mapper: ({ bypasserUserId }) => bypasserUserId },
|
||||
{
|
||||
@@ -192,7 +223,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
|
||||
return formattedDocs.map((doc) => ({
|
||||
...doc,
|
||||
policy: { ...doc.policy, approvers: doc.approvers, bypassers: doc.bypassers }
|
||||
policy: {
|
||||
...doc.policy,
|
||||
approvers: doc.approvers.filter((el) => el.userId).sort((a, b) => (a.sequence || 0) - (b.sequence || 0)),
|
||||
bypassers: doc.bypassers
|
||||
}
|
||||
}));
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindRequestsWithPrivilege" });
|
||||
@@ -272,6 +307,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||
.select(
|
||||
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover),
|
||||
tx.ref("sequence").withSchema(TableName.AccessApprovalPolicyApprover).as("approverSequence"),
|
||||
tx.ref("approvalsRequired").withSchema(TableName.AccessApprovalPolicyApprover),
|
||||
tx.ref("userId").withSchema(TableName.UserGroupMembership),
|
||||
tx.ref("email").withSchema("accessApprovalPolicyApproverUser").as("approverEmail"),
|
||||
tx.ref("email").withSchema("accessApprovalPolicyGroupApproverUser").as("approverGroupEmail"),
|
||||
@@ -367,13 +404,17 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
approverEmail: email,
|
||||
approverUsername: username,
|
||||
approverLastName: lastName,
|
||||
approverFirstName: firstName
|
||||
approverFirstName: firstName,
|
||||
approverSequence,
|
||||
approvalsRequired
|
||||
}) => ({
|
||||
userId: approverUserId,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
username
|
||||
username,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -384,13 +425,17 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
approverGroupEmail: email,
|
||||
approverGroupUsername: username,
|
||||
approverGroupLastName: lastName,
|
||||
approverFirstName: firstName
|
||||
approverFirstName: firstName,
|
||||
approverSequence,
|
||||
approvalsRequired
|
||||
}) => ({
|
||||
userId,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
username
|
||||
username,
|
||||
sequence: approverSequence,
|
||||
approvalsRequired
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -434,7 +479,9 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
...formattedDoc[0],
|
||||
policy: {
|
||||
...formattedDoc[0].policy,
|
||||
approvers: formattedDoc[0].approvers,
|
||||
approvers: formattedDoc[0].approvers
|
||||
.filter((el) => el.userId)
|
||||
.sort((a, b) => (a.sequence || 0) - (b.sequence || 0)),
|
||||
bypassers: formattedDoc[0].bypassers
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import msFn from "ms";
|
||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { EnforcementLevel } from "@app/lib/types";
|
||||
@@ -358,7 +359,6 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypass);
|
||||
|
||||
const isApprover = policy.approvers.find((approver) => approver.userId === actorId);
|
||||
|
||||
// If user is (not an approver OR cant self approve) AND can't bypass policy
|
||||
if ((!isApprover || (!policy.allowedSelfApprovals && isSelfApproval)) && cannotBypassUnderSoftEnforcement) {
|
||||
throw new BadRequestError({
|
||||
@@ -383,6 +383,41 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
|
||||
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
|
||||
}
|
||||
const reviewsGroupById = groupBy(
|
||||
existingReviews.filter((review) => review.status === ApprovalStatus.APPROVED),
|
||||
(i) => i.reviewerUserId
|
||||
);
|
||||
|
||||
const approvedSequences = policy.approvers.reduce(
|
||||
(acc, curr) => {
|
||||
const hasApproved = reviewsGroupById?.[curr.userId as string]?.[0];
|
||||
if (acc?.[acc.length - 1]?.step === curr.sequence) {
|
||||
if (hasApproved) {
|
||||
acc[acc.length - 1].approvals += 1;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.push({
|
||||
step: curr.sequence || 1,
|
||||
approvals: hasApproved ? 1 : 0,
|
||||
requiredApprovals: curr.approvalsRequired || 1
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
[] as { step: number; approvals: number; requiredApprovals: number }[]
|
||||
);
|
||||
const presentSequence = approvedSequences.find((el) => el.approvals < el.requiredApprovals) || {
|
||||
step: 1,
|
||||
approvals: 0,
|
||||
requiredApprovals: 1
|
||||
};
|
||||
if (presentSequence) {
|
||||
const isApproverOfTheSequence = policy.approvers.find(
|
||||
(el) => el.sequence === presentSequence.step && el.userId === actorId
|
||||
);
|
||||
if (!isApproverOfTheSequence) throw new BadRequestError({ message: "You are not reviewer in this step" });
|
||||
}
|
||||
|
||||
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
|
||||
const isBreakGlassApprovalAttempt =
|
||||
@@ -426,11 +461,14 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
);
|
||||
}
|
||||
|
||||
const otherReviews = existingReviews.filter((er) => er.reviewerUserId !== actorId);
|
||||
const allUniqueReviews = [...otherReviews, reviewForThisActorProcessing];
|
||||
if (status === ApprovalStatus.REJECTED) {
|
||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { status: ApprovalStatus.REJECTED }, tx);
|
||||
return reviewForThisActorProcessing;
|
||||
}
|
||||
|
||||
const approvedReviews = allUniqueReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
||||
const meetsStandardApprovalThreshold = approvedReviews.length >= policy.approvals;
|
||||
const meetsStandardApprovalThreshold =
|
||||
(presentSequence?.approvals || 0) + 1 >= presentSequence.requiredApprovals &&
|
||||
approvedSequences.at(-1)?.step === presentSequence?.step;
|
||||
|
||||
if (
|
||||
reviewForThisActorProcessing.status === ApprovalStatus.APPROVED &&
|
||||
|
||||
@@ -36,12 +36,12 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
oidcSSO: false,
|
||||
scim: false,
|
||||
ldap: false,
|
||||
groups: false,
|
||||
groups: true,
|
||||
status: null,
|
||||
trial_end: null,
|
||||
has_used_trial: true,
|
||||
secretApproval: false,
|
||||
secretRotation: false,
|
||||
secretApproval: true,
|
||||
secretRotation: true,
|
||||
caCrl: false,
|
||||
instanceUserManagement: false,
|
||||
externalKms: false,
|
||||
|
||||
Reference in New Issue
Block a user