From 454826fbb64b2616e3cb2cfe20b1fba8fbe71d8a Mon Sep 17 00:00:00 2001 From: Alfonso Hernandez Date: Thu, 18 Jul 2024 02:25:53 +0200 Subject: [PATCH] feat(frontend): accept soft approvals on access requests --- .../src/hooks/api/accessApproval/types.ts | 1 + .../AccessApprovalRequest.tsx | 32 +++++++++++++++---- .../components/ReviewAccessModal.tsx | 32 +++++++++++++++++-- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/frontend/src/hooks/api/accessApproval/types.ts b/frontend/src/hooks/api/accessApproval/types.ts index f023b3555..e17fe41d0 100644 --- a/frontend/src/hooks/api/accessApproval/types.ts +++ b/frontend/src/hooks/api/accessApproval/types.ts @@ -53,6 +53,7 @@ export type TAccessApprovalRequest = { approvers: string[]; secretPath?: string | null; envId: string; + enforcementLevel: EnforcementLevel; }; reviewers: { diff --git a/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx index 235737d4a..6851fdb6c 100644 --- a/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx +++ b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx @@ -40,6 +40,7 @@ import { useGetAccessRequestsCount } from "@app/hooks/api/accessApproval/queries"; import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types"; +import { EnforcementLevel } from "@app/hooks/api/policies/enums"; import { ApprovalStatus, TWorkspaceUser } from "@app/hooks/api/types"; import { queryClient } from "@app/reactQuery"; @@ -80,7 +81,12 @@ export const AccessApprovalRequest = ({ projectId: string; }) => { const [selectedRequest, setSelectedRequest] = useState< - (TAccessApprovalRequest & { user: TWorkspaceUser["user"] | null }) | null + (TAccessApprovalRequest & { + user: TWorkspaceUser["user"] | null; + isRequestedByCurrentUser: boolean; + isApprover: boolean; + }) + | null >(null); const { handlePopUpOpen, popUp, handlePopUpClose } = usePopUp([ @@ -141,6 +147,8 @@ export const AccessApprovalRequest = ({ ); const isApprover = request.policy.approvers.indexOf(membership.id || "") !== -1; const isAccepted = request.isApproved; + const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft; + const isRequestedByCurrentUser = request.requestedBy === membership.id; const userReviewStatus = request.reviewers.find( ({ member }) => member === membership.id @@ -178,7 +186,9 @@ export const AccessApprovalRequest = ({ isRejectedByAnyone, isApprover, userReviewStatus, - isAccepted + isAccepted, + isSoftEnforcement, + isRequestedByCurrentUser }; }; @@ -331,16 +341,24 @@ export const AccessApprovalRequest = ({ tabIndex={0} onClick={() => { if ( - !details.isApprover || - details.isReviewedByUser || - details.isRejectedByAnyone || - details.isAccepted + ( + !details.isApprover + || details.isReviewedByUser + || details.isRejectedByAnyone + || details.isAccepted + ) && !( + details.isSoftEnforcement + && details.isRequestedByCurrentUser + && !details.isAccepted + ) ) return; setSelectedRequest({ ...request, - user: membersGroupById?.[request.requestedBy].user! + user: membersGroupById?.[request.requestedBy].user!, + isRequestedByCurrentUser: details.isRequestedByCurrentUser, + isApprover: details.isApprover }); handlePopUpOpen("reviewRequest"); }} diff --git a/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx index 481cee9c6..10d0f2a8d 100644 --- a/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx +++ b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx @@ -2,11 +2,12 @@ import { useCallback, useMemo, useState } from "react"; import ms from "ms"; import { createNotification } from "@app/components/notifications"; -import { Button, Modal, ModalContent } from "@app/components/v2"; +import { Button, Checkbox, Modal, ModalContent } from "@app/components/v2"; import { Badge } from "@app/components/v2/Badge"; 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 { TWorkspaceUser } from "@app/hooks/api/types"; export const ReviewAccessRequestModal = ({ @@ -19,12 +20,18 @@ export const ReviewAccessRequestModal = ({ }: { isOpen: boolean; onOpenChange: (isOpen: boolean) => void; - request: TAccessApprovalRequest & { user: TWorkspaceUser["user"] | null }; + request: TAccessApprovalRequest & { + user: TWorkspaceUser["user"] | null; + isRequestedByCurrentUser: boolean; + isApprover: boolean; + }; projectSlug: string; selectedRequester: string | undefined; selectedEnvSlug: string | undefined; }) => { const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null); + const [byPassApproval, setByPassApproval] = useState(false); + const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft; const accessDetails = { env: request.environmentName, @@ -134,10 +141,14 @@ export const ReviewAccessRequestModal = ({
@@ -151,6 +162,21 @@ export const ReviewAccessRequestModal = ({ Reject Request
+ {isSoftEnforcement && request.isRequestedByCurrentUser && !request.isApprover && ( +
+ setByPassApproval(checked === true)} + isChecked={byPassApproval} + id="byPassApproval" + checkIndicatorBg="text-white" + className={byPassApproval ? "bg-red hover:bg-red-600 border-red" : ""} + > + + Approve without waiting for requirements to be met (by pass secrets protection) + + +
+ )}