merge + final tweaks

This commit is contained in:
x032205
2025-05-21 15:46:36 -04:00
parent dc6a94ccda
commit e3725dd3ab
12 changed files with 134 additions and 92 deletions
@@ -29,7 +29,8 @@ export enum ProjectPermissionApprovalActions {
Create = "create",
Edit = "edit",
Delete = "delete",
AllowChangeBypass = "allow-change-bypass"
AllowChangeBypass = "allow-change-bypass",
AllowAccessBypass = "allow-access-bypass"
}
export enum ProjectPermissionDynamicSecretActions {
@@ -58,7 +58,8 @@ const ApprovalPolicyActionSchema = z.object({
[ProjectPermissionApprovalActions.Edit]: z.boolean().optional(),
[ProjectPermissionApprovalActions.Delete]: z.boolean().optional(),
[ProjectPermissionApprovalActions.Create]: z.boolean().optional(),
[ProjectPermissionApprovalActions.AllowChangeBypass]: z.boolean().optional()
[ProjectPermissionApprovalActions.AllowChangeBypass]: z.boolean().optional(),
[ProjectPermissionApprovalActions.AllowAccessBypass]: z.boolean().optional()
});
const CmekPolicyActionSchema = z.object({
@@ -578,6 +579,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
const canEdit = action.includes(ProjectPermissionApprovalActions.Edit);
const canRead = action.includes(ProjectPermissionApprovalActions.Read);
const canChangeBypass = action.includes(ProjectPermissionApprovalActions.AllowChangeBypass);
const canAccessBypass = action.includes(ProjectPermissionApprovalActions.AllowAccessBypass);
if (!formVal[subject]) formVal[subject] = [{}];
@@ -588,6 +590,8 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
if (canRead) formVal[subject]![0][ProjectPermissionApprovalActions.Read] = true;
if (canChangeBypass)
formVal[subject]![0][ProjectPermissionApprovalActions.AllowChangeBypass] = true;
if (canAccessBypass)
formVal[subject]![0][ProjectPermissionApprovalActions.AllowAccessBypass] = true;
return;
}
@@ -1212,7 +1216,8 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
{ label: "Create", value: ProjectPermissionApprovalActions.Create },
{ label: "Modify", value: ProjectPermissionApprovalActions.Edit },
{ label: "Remove", value: ProjectPermissionApprovalActions.Delete },
{ label: "Allow Change Bypass", value: ProjectPermissionApprovalActions.AllowChangeBypass }
{ label: "Allow Change Bypass", value: ProjectPermissionApprovalActions.AllowChangeBypass },
{ label: "Allow Access Bypass", value: ProjectPermissionApprovalActions.AllowAccessBypass }
]
},
[ProjectPermissionSub.SecretRotation]: {
@@ -25,6 +25,7 @@ import {
} from "@app/components/v2";
import { Badge } from "@app/components/v2/Badge";
import {
ProjectPermissionApprovalActions,
ProjectPermissionMemberActions,
ProjectPermissionSub,
useProjectPermission,
@@ -100,6 +101,11 @@ export const AccessApprovalRequest = ({
const { subscription } = useSubscription();
const { currentWorkspace } = useWorkspace();
const canBypassApprovalPermission = permission.can(
ProjectPermissionApprovalActions.AllowAccessBypass,
ProjectPermissionSub.SecretApproval
);
const { data: members } = useGetWorkspaceUsers(projectId, true);
const membersGroupById = members?.reduce<Record<string, TWorkspaceUser>>(
(prev, curr) => ({ ...prev, [curr.user.id]: curr }),
@@ -350,7 +356,11 @@ export const AccessApprovalRequest = ({
details.isReviewedByUser ||
details.isRejectedByAnyone ||
(!details.isApprover &&
!(details.isSoftEnforcement && details.isRequestedByCurrentUser))
!(
details.isSoftEnforcement &&
details.isRequestedByCurrentUser &&
canBypassApprovalPermission
))
)
return;
@@ -360,9 +370,11 @@ export const AccessApprovalRequest = ({
) {
setSelectedRequest({
...request,
user: details.isRequestedByCurrentUser
? user
: membersGroupById?.[request.requestedByUserId].user!,
user:
details.isRequestedByCurrentUser ||
!membersGroupById?.[request.requestedByUserId].user
? user
: membersGroupById?.[request.requestedByUserId].user,
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
isApprover: details.isApprover
});
@@ -376,7 +388,11 @@ export const AccessApprovalRequest = ({
details.isReviewedByUser ||
details.isRejectedByAnyone ||
(!details.isApprover &&
!(details.isSoftEnforcement && details.isRequestedByCurrentUser))
!(
details.isSoftEnforcement &&
details.isRequestedByCurrentUser &&
canBypassApprovalPermission
))
)
return;
@@ -387,9 +403,11 @@ export const AccessApprovalRequest = ({
) {
setSelectedRequest({
...request,
user: details.isRequestedByCurrentUser
? user
: membersGroupById?.[request.requestedByUserId].user!,
user:
details.isRequestedByCurrentUser ||
!membersGroupById?.[request.requestedByUserId].user
? user
: membersGroupById?.[request.requestedByUserId].user,
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
isApprover: details.isApprover
});
@@ -463,6 +481,7 @@ export const AccessApprovalRequest = ({
setSelectedRequest(null);
refetchRequests();
}}
canBypassApprovalPermission={canBypassApprovalPermission}
/>
)}
@@ -1,5 +1,8 @@
import { useCallback, useMemo, useState } from "react";
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import ms from "ms";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { Button, Checkbox, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
@@ -8,9 +11,6 @@ import { ProjectPermissionActions } from "@app/context";
import { useReviewAccessRequest } from "@app/hooks/api";
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
import { EnforcementLevel } from "@app/hooks/api/policies/enums";
import { twMerge } from "tailwind-merge";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
export const ReviewAccessRequestModal = ({
isOpen,
@@ -18,7 +18,8 @@ export const ReviewAccessRequestModal = ({
request,
projectSlug,
selectedRequester,
selectedEnvSlug
selectedEnvSlug,
canBypassApprovalPermission
}: {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
@@ -30,6 +31,7 @@ export const ReviewAccessRequestModal = ({
projectSlug: string;
selectedRequester: string | undefined;
selectedEnvSlug: string | undefined;
canBypassApprovalPermission: boolean;
}) => {
const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null);
const [bypassApproval, setBypassApproval] = useState(false);
@@ -182,7 +184,11 @@ export const ReviewAccessRequestModal = ({
<Button
isLoading={isLoading === "approved"}
isDisabled={
!!isLoading || (!request.isApprover && !bypassApproval && isSoftEnforcement)
!!isLoading ||
(!request.isApprover &&
!bypassApproval &&
isSoftEnforcement &&
canBypassApprovalPermission)
}
onClick={() => handleReview("approved")}
className="mt-4"
@@ -202,39 +208,42 @@ export const ReviewAccessRequestModal = ({
</Button>
</div>
{isSoftEnforcement && request.isRequestedByCurrentUser && !request.isApprover && (
<div className="mt-2 flex flex-col space-y-2">
<Checkbox
onCheckedChange={(checked) => setBypassApproval(checked === true)}
isChecked={bypassApproval}
id="byPassApproval"
checkIndicatorBg="text-white"
className={twMerge(
"mr-2",
bypassApproval ? "border-red bg-red hover:bg-red-600" : ""
)}
>
<span className="text-xs text-red">
Approve without waiting for requirements to be met (bypass policy protection)
</span>
</Checkbox>
{bypassApproval && (
<FormControl
label="Reason for bypass"
className="mt-2"
isRequired
tooltipText="Enter a reason for bypassing the secret change policy"
{isSoftEnforcement &&
request.isRequestedByCurrentUser &&
!request.isApprover &&
canBypassApprovalPermission && (
<div className="mt-2 flex flex-col space-y-2">
<Checkbox
onCheckedChange={(checked) => setBypassApproval(checked === true)}
isChecked={bypassApproval}
id="byPassApproval"
checkIndicatorBg="text-white"
className={twMerge(
"mr-2",
bypassApproval ? "border-red bg-red hover:bg-red-600" : ""
)}
>
<Input
value={bypassReason}
onChange={(e) => setBypassReason(e.currentTarget.value)}
placeholder="Enter reason for bypass (min 10 chars)"
leftIcon={<FontAwesomeIcon icon={faTriangleExclamation} />}
/>
</FormControl>
)}
</div>
)}
<span className="text-xs text-red">
Approve without waiting for requirements to be met (bypass policy protection)
</span>
</Checkbox>
{bypassApproval && (
<FormControl
label="Reason for bypass"
className="mt-2"
isRequired
tooltipText="Enter a reason for bypassing the secret change policy"
>
<Input
value={bypassReason}
onChange={(e) => setBypassReason(e.currentTarget.value)}
placeholder="Enter reason for bypass (min 10 chars)"
leftIcon={<FontAwesomeIcon icon={faTriangleExclamation} />}
/>
</FormControl>
)}
</div>
)}
</div>
</ModalContent>
</Modal>