diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts index 5a3af5aa5..ccab70d5b 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts @@ -350,6 +350,12 @@ export const accessApprovalRequestServiceFactory = ({ const canBypass = !policy.bypassers.length || policy.bypassers.some((bypasser) => bypasser.userId === actorId); const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypass); + // Calculate break glass attempt before sequence checks + const isBreakGlassApprovalAttempt = + policy.enforcementLevel === EnforcementLevel.Soft && + actorId === accessApprovalRequest.requestedByUserId && + status === ApprovalStatus.APPROVED; + 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) { @@ -409,15 +415,14 @@ export const accessApprovalRequestServiceFactory = ({ 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" }); + + // Only throw if actor is not the approver and not bypassing + if (!isApproverOfTheSequence && !isBreakGlassApprovalAttempt) { + throw new BadRequestError({ message: "You are not a reviewer in this step" }); + } } const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => { - const isBreakGlassApprovalAttempt = - policy.enforcementLevel === EnforcementLevel.Soft && - actorId === accessApprovalRequest.requestedByUserId && - status === ApprovalStatus.APPROVED; - let reviewForThisActorProcessing: { id: string; requestId: string; diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx index ee0f18ee0..f93eaa9ab 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx @@ -439,39 +439,35 @@ export const ReviewAccessRequestModal = ({ ) : ( <> - {isSoftEnforcement && - request.isRequestedByCurrentUser && - !(request.isApprover && request.isSelfApproveAllowed) && - canBypass && ( -
- setBypassApproval(checked === true)} - isChecked={bypassApproval} - id="byPassApproval" - className={twMerge("mr-2", bypassApproval ? "border-red/30 bg-red/10" : "")} + {isSoftEnforcement && request.isRequestedByCurrentUser && canBypass && ( +
+ setBypassApproval(checked === true)} + isChecked={bypassApproval} + id="byPassApproval" + className={twMerge("mr-2", bypassApproval ? "border-red/30 bg-red/10" : "")} + > + + Approve without waiting for requirements to be met (bypass policy protection) + + + {bypassApproval && ( + - - Approve without waiting for requirements to be met (bypass policy - protection) - - - {bypassApproval && ( - - setBypassReason(e.currentTarget.value)} - placeholder="Enter reason for bypass (min 10 chars)" - leftIcon={} - /> - - )} -
- )} + setBypassReason(e.currentTarget.value)} + placeholder="Enter reason for bypass (min 10 chars)" + leftIcon={} + /> + + )} +
+ )}