mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(frontend): accept soft approvals on access requests
This commit is contained in:
@@ -53,6 +53,7 @@ export type TAccessApprovalRequest = {
|
|||||||
approvers: string[];
|
approvers: string[];
|
||||||
secretPath?: string | null;
|
secretPath?: string | null;
|
||||||
envId: string;
|
envId: string;
|
||||||
|
enforcementLevel: EnforcementLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
reviewers: {
|
reviewers: {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
useGetAccessRequestsCount
|
useGetAccessRequestsCount
|
||||||
} from "@app/hooks/api/accessApproval/queries";
|
} from "@app/hooks/api/accessApproval/queries";
|
||||||
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
|
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 { ApprovalStatus, TWorkspaceUser } from "@app/hooks/api/types";
|
||||||
import { queryClient } from "@app/reactQuery";
|
import { queryClient } from "@app/reactQuery";
|
||||||
|
|
||||||
@@ -80,7 +81,12 @@ export const AccessApprovalRequest = ({
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedRequest, setSelectedRequest] = useState<
|
const [selectedRequest, setSelectedRequest] = useState<
|
||||||
(TAccessApprovalRequest & { user: TWorkspaceUser["user"] | null }) | null
|
(TAccessApprovalRequest & {
|
||||||
|
user: TWorkspaceUser["user"] | null;
|
||||||
|
isRequestedByCurrentUser: boolean;
|
||||||
|
isApprover: boolean;
|
||||||
|
})
|
||||||
|
| null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
const { handlePopUpOpen, popUp, handlePopUpClose } = usePopUp([
|
const { handlePopUpOpen, popUp, handlePopUpClose } = usePopUp([
|
||||||
@@ -141,6 +147,8 @@ export const AccessApprovalRequest = ({
|
|||||||
);
|
);
|
||||||
const isApprover = request.policy.approvers.indexOf(membership.id || "") !== -1;
|
const isApprover = request.policy.approvers.indexOf(membership.id || "") !== -1;
|
||||||
const isAccepted = request.isApproved;
|
const isAccepted = request.isApproved;
|
||||||
|
const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
|
const isRequestedByCurrentUser = request.requestedBy === membership.id;
|
||||||
|
|
||||||
const userReviewStatus = request.reviewers.find(
|
const userReviewStatus = request.reviewers.find(
|
||||||
({ member }) => member === membership.id
|
({ member }) => member === membership.id
|
||||||
@@ -178,7 +186,9 @@ export const AccessApprovalRequest = ({
|
|||||||
isRejectedByAnyone,
|
isRejectedByAnyone,
|
||||||
isApprover,
|
isApprover,
|
||||||
userReviewStatus,
|
userReviewStatus,
|
||||||
isAccepted
|
isAccepted,
|
||||||
|
isSoftEnforcement,
|
||||||
|
isRequestedByCurrentUser
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,16 +341,24 @@ export const AccessApprovalRequest = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (
|
if (
|
||||||
!details.isApprover ||
|
(
|
||||||
details.isReviewedByUser ||
|
!details.isApprover
|
||||||
details.isRejectedByAnyone ||
|
|| details.isReviewedByUser
|
||||||
details.isAccepted
|
|| details.isRejectedByAnyone
|
||||||
|
|| details.isAccepted
|
||||||
|
) && !(
|
||||||
|
details.isSoftEnforcement
|
||||||
|
&& details.isRequestedByCurrentUser
|
||||||
|
&& !details.isAccepted
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setSelectedRequest({
|
setSelectedRequest({
|
||||||
...request,
|
...request,
|
||||||
user: membersGroupById?.[request.requestedBy].user!
|
user: membersGroupById?.[request.requestedBy].user!,
|
||||||
|
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
||||||
|
isApprover: details.isApprover
|
||||||
});
|
});
|
||||||
handlePopUpOpen("reviewRequest");
|
handlePopUpOpen("reviewRequest");
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { useCallback, useMemo, useState } from "react";
|
|||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
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 { Badge } from "@app/components/v2/Badge";
|
||||||
import { ProjectPermissionActions } from "@app/context";
|
import { ProjectPermissionActions } from "@app/context";
|
||||||
import { useReviewAccessRequest } from "@app/hooks/api";
|
import { useReviewAccessRequest } from "@app/hooks/api";
|
||||||
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
|
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
|
||||||
|
import { EnforcementLevel } from "@app/hooks/api/policies/enums";
|
||||||
import { TWorkspaceUser } from "@app/hooks/api/types";
|
import { TWorkspaceUser } from "@app/hooks/api/types";
|
||||||
|
|
||||||
export const ReviewAccessRequestModal = ({
|
export const ReviewAccessRequestModal = ({
|
||||||
@@ -19,12 +20,18 @@ export const ReviewAccessRequestModal = ({
|
|||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
request: TAccessApprovalRequest & { user: TWorkspaceUser["user"] | null };
|
request: TAccessApprovalRequest & {
|
||||||
|
user: TWorkspaceUser["user"] | null;
|
||||||
|
isRequestedByCurrentUser: boolean;
|
||||||
|
isApprover: boolean;
|
||||||
|
};
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
selectedRequester: string | undefined;
|
selectedRequester: string | undefined;
|
||||||
selectedEnvSlug: string | undefined;
|
selectedEnvSlug: string | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null);
|
const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null);
|
||||||
|
const [byPassApproval, setByPassApproval] = useState(false);
|
||||||
|
const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
|
|
||||||
const accessDetails = {
|
const accessDetails = {
|
||||||
env: request.environmentName,
|
env: request.environmentName,
|
||||||
@@ -134,10 +141,14 @@ export const ReviewAccessRequestModal = ({
|
|||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<Button
|
<Button
|
||||||
isLoading={isLoading === "approved"}
|
isLoading={isLoading === "approved"}
|
||||||
isDisabled={!!isLoading}
|
isDisabled={
|
||||||
|
!!isLoading ||
|
||||||
|
(!request.isApprover && !byPassApproval && isSoftEnforcement)
|
||||||
|
}
|
||||||
onClick={() => handleReview("approved")}
|
onClick={() => handleReview("approved")}
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
colorSchema={!request.isApprover && isSoftEnforcement ? "danger" : "primary"}
|
||||||
>
|
>
|
||||||
Approve Request
|
Approve Request
|
||||||
</Button>
|
</Button>
|
||||||
@@ -151,6 +162,21 @@ export const ReviewAccessRequestModal = ({
|
|||||||
Reject Request
|
Reject Request
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{isSoftEnforcement && request.isRequestedByCurrentUser && !request.isApprover && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<Checkbox
|
||||||
|
onCheckedChange={(checked) => setByPassApproval(checked === true)}
|
||||||
|
isChecked={byPassApproval}
|
||||||
|
id="byPassApproval"
|
||||||
|
checkIndicatorBg="text-white"
|
||||||
|
className={byPassApproval ? "bg-red hover:bg-red-600 border-red" : ""}
|
||||||
|
>
|
||||||
|
<span className="text-red text-sm">
|
||||||
|
Approve without waiting for requirements to be met (by pass secrets protection)
|
||||||
|
</span>
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
Reference in New Issue
Block a user