/* eslint-disable no-nested-ternary */ /* eslint-disable react/jsx-no-useless-fragment */ import { useMemo, useState } from "react"; import { faCheck, faCheckCircle, faChevronDown, faLock, faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { formatDistance } from "date-fns"; import { AnimatePresence, motion } from "framer-motion"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, EmptyState, Tooltip } from "@app/components/v2"; import { Badge } from "@app/components/v2/Badge"; import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission, useSubscription, useUser, useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; import { useGetWorkspaceUsers } from "@app/hooks/api"; import { accessApprovalKeys, useGetAccessApprovalPolicies, useGetAccessApprovalRequests, useGetAccessRequestsCount } from "@app/hooks/api/accessApproval/queries"; import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types"; import { EnforcementLevel } from "@app/hooks/api/policies/enums"; import { queryClient } from "@app/hooks/api/reactQuery"; import { ApprovalStatus, TWorkspaceUser } from "@app/hooks/api/types"; import { RequestAccessModal } from "./components/RequestAccessModal"; import { ReviewAccessRequestModal } from "./components/ReviewAccessModal"; const generateRequestText = (request: TAccessApprovalRequest, userId: string) => { const { isTemporary } = request; return (
Requested {isTemporary ? "temporary" : "permanent"} access to{" "} {request.policy.secretPath} in {request.environmentName}
{request.requestedByUserId === userId && ( Requested By You )}
); }; export const AccessApprovalRequest = ({ projectSlug, projectId }: { projectSlug: string; projectId: string; }) => { const [selectedRequest, setSelectedRequest] = useState< | (TAccessApprovalRequest & { user: TWorkspaceUser["user"] | null; isRequestedByCurrentUser: boolean; isApprover: boolean; }) | null >(null); const { handlePopUpOpen, popUp, handlePopUpClose } = usePopUp([ "requestAccess", "reviewRequest", "upgradePlan" ] as const); const { permission } = useProjectPermission(); const { user } = useUser(); const { subscription } = useSubscription(); const { currentWorkspace } = useWorkspace(); const { data: members } = useGetWorkspaceUsers(projectId, true); const membersGroupById = members?.reduce>( (prev, curr) => ({ ...prev, [curr.user.id]: curr }), {} ); const [statusFilter, setStatusFilter] = useState<"open" | "close">("open"); const [requestedByFilter, setRequestedByFilter] = useState(undefined); const [envFilter, setEnvFilter] = useState(undefined); const { data: requestCount } = useGetAccessRequestsCount({ projectSlug }); const { data: policies, isPending: policiesLoading } = useGetAccessApprovalPolicies({ projectSlug }); const { data: requests } = useGetAccessApprovalRequests({ projectSlug, authorProjectMembershipId: requestedByFilter, envSlug: envFilter }); const filteredRequests = useMemo(() => { if (statusFilter === "open") return requests?.filter( (request) => !request.policy.deletedAt && !request.isApproved && !request.reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED) ); if (statusFilter === "close") return requests?.filter( (request) => request.policy.deletedAt || request.isApproved || request.reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED) ); return requests; }, [requests, statusFilter, requestedByFilter, envFilter]); const generateRequestDetails = (request: TAccessApprovalRequest) => { const isReviewedByUser = request.reviewers.findIndex(({ member }) => member === user.id) !== -1; const isRejectedByAnyone = request.reviewers.some( ({ status }) => status === ApprovalStatus.REJECTED ); const isApprover = request.policy.approvers.indexOf(user.id || "") !== -1; const isAccepted = request.isApproved; const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft; const isRequestedByCurrentUser = request.requestedByUserId === user.id; const userReviewStatus = request.reviewers.find(({ member }) => member === user.id)?.status; let displayData: { label: string; type: "primary" | "danger" | "success" } = { label: "", type: "primary" }; const isExpired = request.privilege && request.isApproved && new Date() > new Date(request.privilege.temporaryAccessEndTime || ("" as string)); if (isExpired) displayData = { label: "Access Expired", type: "danger" }; else if (isAccepted) displayData = { label: "Access Granted", type: "success" }; else if (isRejectedByAnyone) displayData = { label: "Rejected", type: "danger" }; else if (userReviewStatus === ApprovalStatus.APPROVED) { displayData = { label: `Pending ${request.policy.approvals - request.reviewers.length} review${ request.policy.approvals - request.reviewers.length > 1 ? "s" : "" }`, type: "primary" }; } else if (!isReviewedByUser) displayData = { label: "Review Required", type: "primary" }; return { displayData, isReviewedByUser, isRejectedByAnyone, isApprover, userReviewStatus, isAccepted, isSoftEnforcement, isRequestedByCurrentUser }; }; return (
Access Requests
Request access to secrets in sensitive environments and folders.
setStatusFilter("open")} onKeyDown={(evt) => { if (evt.key === "Enter") setStatusFilter("open"); }} className={ statusFilter === "close" ? "text-gray-500 duration-100 hover:text-gray-400" : "" } > {!!requestCount && requestCount?.pendingCount} Pending
setStatusFilter("close")} onKeyDown={(evt) => { if (evt.key === "Enter") setStatusFilter("close"); }} > {!!requestCount && requestCount.finalizedCount} Completed
Select an environment {currentWorkspace?.environments.map(({ slug, name }) => ( setEnvFilter((state) => (state === slug ? undefined : slug))} key={`request-filter-${slug}`} icon={envFilter === slug && } iconPos="right" > {name} ))} {!!permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Member) && ( Select an author {members?.map(({ user: membershipUser, id }) => ( setRequestedByFilter((state) => (state === id ? undefined : id)) } key={`request-filter-member-${id}`} icon={requestedByFilter === id && } iconPos="right" > {membershipUser.username} ))} )}
{filteredRequests?.length === 0 && (
)} {!!filteredRequests?.length && filteredRequests?.map((request) => { const details = generateRequestDetails(request); return (
{ if ( (!details.isApprover || details.isReviewedByUser || details.isRejectedByAnyone || details.isAccepted) && !( details.isSoftEnforcement && details.isRequestedByCurrentUser && !details.isAccepted ) ) return; if (membersGroupById?.[request.requestedByUserId].user) { setSelectedRequest({ ...request, user: membersGroupById?.[request.requestedByUserId].user, isRequestedByCurrentUser: details.isRequestedByCurrentUser, isApprover: details.isApprover }); } handlePopUpOpen("reviewRequest"); }} onKeyDown={(evt) => { if ( !details.isApprover || details.isAccepted || details.isReviewedByUser || details.isRejectedByAnyone ) return; if (evt.key === "Enter") { if (membersGroupById?.[request.requestedByUserId].user) { setSelectedRequest({ ...request, user: membersGroupById?.[request.requestedByUserId].user, isRequestedByCurrentUser: details.isRequestedByCurrentUser, isApprover: details.isApprover }); } handlePopUpOpen("reviewRequest"); } }} >
{generateRequestText(request, user.id)}
{membersGroupById?.[request.requestedByUserId]?.user && ( <> Requested {formatDistance(new Date(request.createdAt), new Date())}{" "} ago by{" "} {membersGroupById?.[request.requestedByUserId]?.user?.firstName}{" "} {membersGroupById?.[request.requestedByUserId]?.user?.lastName} ( {membersGroupById?.[request.requestedByUserId]?.user?.email}){" "} )}
{details.isApprover && ( {details.displayData.label} )}
); })}
{!!policies && ( { queryClient.invalidateQueries({ queryKey: accessApprovalKeys.getAccessApprovalRequests( projectSlug, envFilter, requestedByFilter ) }); handlePopUpClose("requestAccess"); }} /> )} {!!selectedRequest && ( { handlePopUpClose("reviewRequest"); setSelectedRequest(null); }} /> )} handlePopUpClose("upgradePlan")} />
); };