diff --git a/frontend/src/views/SecretApprovalPage/SecretApprovalPage.tsx b/frontend/src/views/SecretApprovalPage/SecretApprovalPage.tsx index df0ba55f9..8d7282130 100644 --- a/frontend/src/views/SecretApprovalPage/SecretApprovalPage.tsx +++ b/frontend/src/views/SecretApprovalPage/SecretApprovalPage.tsx @@ -4,13 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { Badge } from "@app/components/v2/Badge"; -import { Divider } from "@app/components/v2/Divider"; import { useWorkspace } from "@app/context"; import { useGetAccessRequestsCount, useGetSecretApprovalRequestCount } from "@app/hooks/api"; import { AccessApprovalRequest } from "./components/AccessApprovalRequest"; import { ApprovalPolicyList } from "./components/ApprovalPolicyList"; -import { SecretApprovalPolicyList } from "./components/SecretApprovalPolicyList"; import { SecretApprovalRequest } from "./components/SecretApprovalRequest"; enum TabSection { @@ -59,17 +57,12 @@ export const SecretApprovalPage = () => { Secret Requests {Boolean(secretApprovalReqCount?.open) && ({secretApprovalReqCount?.open})} - Secret Policies - Access Requests {Boolean(accessApprovalRequestCount?.pendingCount) && {accessApprovalRequestCount?.pendingCount}} Policies - - - diff --git a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/SecretApprovalPolicyList.tsx b/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/SecretApprovalPolicyList.tsx deleted file mode 100644 index f377f44e9..000000000 --- a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/SecretApprovalPolicyList.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { faFileShield, faPlus } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { createNotification } from "@app/components/notifications"; -import { ProjectPermissionCan } from "@app/components/permissions"; -import { - Button, - DeleteActionModal, - EmptyState, - Modal, - ModalContent, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr, - UpgradePlanModal -} from "@app/components/v2"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - useProjectPermission, - useSubscription -} from "@app/context"; -import { usePopUp } from "@app/hooks"; -import { - useDeleteSecretApprovalPolicy, - useGetSecretApprovalPolicies, - useGetWorkspaceUsers -} from "@app/hooks/api"; -import { TSecretApprovalPolicy } from "@app/hooks/api/types"; - -import { SecretApprovalPolicyRow } from "./components/SecretApprovalPolicyRow"; -import { SecretPolicyForm } from "./components/SecretPolicyForm"; - -type Props = { - workspaceId: string; -}; - -export const SecretApprovalPolicyList = ({ workspaceId }: Props) => { - const { handlePopUpToggle, handlePopUpOpen, handlePopUpClose, popUp } = usePopUp([ - "secretPolicyForm", - "deletePolicy", - "upgradePlan" - ] as const); - const { permission } = useProjectPermission(); - const { subscription } = useSubscription(); - - const { data: members } = useGetWorkspaceUsers(workspaceId); - const { data: policies, isLoading: isPoliciesLoading } = useGetSecretApprovalPolicies({ - workspaceId, - options: { - enabled: permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval) - } - }); - - const { mutateAsync: deleteSecretApprovalPolicy } = useDeleteSecretApprovalPolicy(); - - const handleDeletePolicy = async () => { - const { id } = popUp.deletePolicy.data as TSecretApprovalPolicy; - try { - await deleteSecretApprovalPolicy({ - workspaceId, - id - }); - createNotification({ - type: "success", - text: "Successfully deleted policy" - }); - handlePopUpClose("deletePolicy"); - } catch (err) { - console.log(err); - createNotification({ - type: "error", - text: "Failed to delete policy" - }); - } - }; - - return ( -
-
-
- Approval Policies -
- Implement policies to prevent unauthorized secret changes. -
-
-
- - {(isAllowed) => ( - - )} - -
-
- - - - - - - - - - - - - {isPoliciesLoading && ( - - )} - {!isPoliciesLoading && !policies?.length && ( - - - - )} - {policies?.map((policy) => ( - handlePopUpOpen("secretPolicyForm", policy)} - onDelete={() => handlePopUpOpen("deletePolicy", policy)} - /> - ))} - -
NameEnvironmentSecret PathEligible ApproversApproval Required
- -
-
- handlePopUpToggle("secretPolicyForm", isOpen)} - > - - handlePopUpToggle("secretPolicyForm", isOpen)} - members={members} - editValues={popUp.secretPolicyForm.data as TSecretApprovalPolicy} - /> - - - handlePopUpToggle("deletePolicy", isOpen)} - onDeleteApproved={handleDeletePolicy} - /> - handlePopUpToggle("upgradePlan", isOpen)} - text="You can add secret approval policy if you switch to Infisical's Enterprise plan." - /> -
- ); -}; diff --git a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretApprovalPolicyRow.tsx b/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretApprovalPolicyRow.tsx deleted file mode 100644 index 9f8a08c44..000000000 --- a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretApprovalPolicyRow.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { useState } from "react"; -import { faCheckCircle, faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { ProjectPermissionCan } from "@app/components/permissions"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuTrigger, - IconButton, - Input, - Td, - Tr -} from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; -import { useUpdateSecretApprovalPolicy } from "@app/hooks/api"; -import { TSecretApprovalPolicy } from "@app/hooks/api/types"; -import { TWorkspaceUser } from "@app/hooks/api/users/types"; - -type Props = { - policy: TSecretApprovalPolicy; - members?: TWorkspaceUser[]; - workspaceId: string; - onEdit: () => void; - onDelete: () => void; -}; - -export const SecretApprovalPolicyRow = ({ - policy, - members = [], - workspaceId, - onEdit, - onDelete -}: Props) => { - const [selectedApprovers, setSelectedApprovers] = useState([]); - const { mutate: updateSecretApprovalPolicy, isLoading } = useUpdateSecretApprovalPolicy(); - const { permission } = useProjectPermission(); - - return ( - - {policy.name} - {policy.environment.slug} - {policy.secretPath || "*"} - - { - if (!isOpen) { - updateSecretApprovalPolicy( - { - workspaceId, - id: policy.id, - approvers: selectedApprovers - }, - { - onSettled: () => { - setSelectedApprovers([]); - } - } - ); - } else { - setSelectedApprovers(policy.userApprovers.map(({ userId }) => userId)); - } - }} - > - - - - - - Select members that are allowed to approve changes - - {members?.map(({ user }) => { - const isChecked = selectedApprovers.includes(user.id); - return ( - { - evt.preventDefault(); - setSelectedApprovers((state) => - isChecked ? state.filter((el) => el !== user.id) : [...state, user.id] - ); - }} - key={`create-policy-members-${user.id}`} - iconPos="right" - icon={isChecked && } - > - {user.username} - - ); - })} - - - - {policy.approvals} - -
- - {(isAllowed) => ( - - - - )} - - - {(isAllowed) => ( - - - - )} - -
- - - ); -}; diff --git a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretPolicyForm.tsx b/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretPolicyForm.tsx deleted file mode 100644 index 18b0b87c8..000000000 --- a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/components/SecretPolicyForm.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { faCheckCircle } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; - -import { createNotification } from "@app/components/notifications"; -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuTrigger, - FormControl, - Input, - Select, - SelectItem -} from "@app/components/v2"; -import { SecretPathInput } from "@app/components/v2/SecretPathInput"; -import { useWorkspace } from "@app/context"; -import { useCreateSecretApprovalPolicy, useUpdateSecretApprovalPolicy } from "@app/hooks/api"; -import { TSecretApprovalPolicy } from "@app/hooks/api/types"; -import { TWorkspaceUser } from "@app/hooks/api/users/types"; - -type Props = { - isOpen?: boolean; - onToggle: (isOpen: boolean) => void; - members?: TWorkspaceUser[]; - workspaceId: string; - editValues?: TSecretApprovalPolicy; -}; - -const formSchema = z - .object({ - environment: z.string(), - name: z.string().optional(), - secretPath: z.string().optional().nullable(), - approvals: z.number().min(1), - approvers: z.string().array().min(1) - }) - .refine((data) => data.approvals <= data.approvers.length, { - path: ["approvals"], - message: "The number of approvals should be lower than the number of approvers." - }); - -type TFormSchema = z.infer; - -export const SecretPolicyForm = ({ - onToggle, - members = [], - workspaceId, - editValues -}: Props) => { - const { - control, - handleSubmit, - watch, - formState: { isSubmitting } - } = useForm({ - resolver: zodResolver(formSchema), - values: editValues - ? { - ...editValues, - approvers: editValues.userApprovers.map(({ userId }) => userId), - environment: editValues.environment.slug - } - : undefined - }); - const { currentWorkspace } = useWorkspace(); - const selectedEnvironment = watch("environment"); - - const environments = currentWorkspace?.environments || []; - - const isEditMode = Boolean(editValues); - - const { mutateAsync: createSecretApprovalPolicy } = useCreateSecretApprovalPolicy(); - const { mutateAsync: updateSecretApprovalPolicy } = useUpdateSecretApprovalPolicy(); - - const handleCreatePolicy = async (data: TFormSchema) => { - try { - await createSecretApprovalPolicy({ - ...data, - workspaceId - }); - createNotification({ - type: "success", - text: "Successfully created policy" - }); - onToggle(false); - } catch (err) { - console.log(err); - createNotification({ - type: "error", - text: "Failed to create policy" - }); - } - }; - - const handleUpdatePolicy = async (data: TFormSchema) => { - if (!editValues?.id) return; - try { - await updateSecretApprovalPolicy({ - id: editValues?.id, - ...data, - secretPath: data.secretPath || null, - workspaceId - }); - createNotification({ - type: "success", - text: "Successfully updated policy" - }); - onToggle(false); - } catch (err) { - console.log(err); - createNotification({ - type: "error", - text: "failed to update policy" - }); - } - }; - - const handleFormSubmit = async (data: TFormSchema) => { - if (isEditMode) { - await handleUpdatePolicy(data); - } else { - await handleCreatePolicy(data); - } - }; - - return ( -
- ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - - - - - Select members that are allowed to approve changes - - {members.map(({ user }) => { - const isChecked = value?.includes(user.id); - return ( - { - evt.preventDefault(); - onChange( - isChecked - ? value?.filter((el) => el !== user.id) - : [...(value || []), user.id] - ); - }} - key={`create-policy-members-${user.id}`} - iconPos="right" - icon={isChecked && } - > - {user.username} - - ); - })} - - - - )} - /> - ( - - field.onChange(parseInt(el.target.value, 10))} - /> - - )} - /> -
- - -
- - - ); -}; diff --git a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/index.tsx b/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/index.tsx deleted file mode 100644 index f204264b4..000000000 --- a/frontend/src/views/SecretApprovalPage/components/SecretApprovalPolicyList/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { SecretApprovalPolicyList } from "./SecretApprovalPolicyList";