From 32fc254ae151def91cde26aeb8030714d39fbd9c Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Fri, 19 Jul 2024 17:09:14 +0800 Subject: [PATCH] misc: added UI for load backup --- .../EncryptionTab/EncryptionTab.tsx | 219 ++++++++++++++++-- 1 file changed, 194 insertions(+), 25 deletions(-) diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EncryptionTab/EncryptionTab.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EncryptionTab/EncryptionTab.tsx index 7c8724bad..553e89218 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EncryptionTab/EncryptionTab.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EncryptionTab/EncryptionTab.tsx @@ -1,20 +1,32 @@ -import { useEffect, useState } from "react"; +import { ChangeEvent, useEffect, useRef, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { faUpload } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import FileSaver from "file-saver"; import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; -import { Button, FormControl, Select, SelectItem } from "@app/components/v2"; +import { + Button, + FormControl, + IconButton, + Modal, + ModalContent, + Select, + SelectItem +} from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useOrganization, useWorkspace } from "@app/context"; +import { usePopUp } from "@app/hooks"; import { useGetActiveProjectKms, useGetExternalKmsList, useUpdateProjectKms } from "@app/hooks/api"; import { fetchProjectKmsBackup } from "@app/hooks/api/kms/queries"; +import { Organization, Workspace } from "@app/hooks/api/types"; const formSchema = z.object({ kmsKeyId: z.string() @@ -24,6 +36,155 @@ type TForm = z.infer; const INTERNAL_KMS_KEY_ID = "internal"; +const BackupConfirmationModal = ({ + isOpen, + onOpenChange, + org, + workspace +}: { + isOpen: boolean; + onOpenChange: (state: boolean) => void; + org?: Organization; + workspace?: Workspace; +}) => { + const downloadKmsBackup = async () => { + if (!workspace || !org) { + return; + } + + const { secretManager } = await fetchProjectKmsBackup(workspace.id); + + const [, , kmsFunction] = secretManager.split("."); + const file = secretManager; + + const blob = new Blob([file], { type: "text/plain;charset=utf-8" }); + FileSaver.saveAs(blob, `kms-backup-${org.slug}-${workspace.slug}-${kmsFunction}.infisical.txt`); + }; + + return ( + + +

+ In case of interruptions with your configured external KMS, use this generated backup to + set the project's KMS back to the default Infisical KMS. +

+

+ Note: The project data key will be encrypted the organization's default Infisical + KMS. +

+ + +
+
+ ); +}; + +const LoadBackupModal = ({ + isOpen, + onOpenChange, + org, + workspace +}: { + isOpen: boolean; + onOpenChange: (state: boolean) => void; + org?: Organization; + workspace?: Workspace; +}) => { + const fileUploadRef = useRef(null); + const [backupContent, setBackupContent] = useState(""); + const [backupFileName, setBackupFileName] = useState(""); + + const uploadKmsBackup = async () => { + if (!workspace || !org) { + // eslint-disable-next-line no-useless-return + return; + } + }; + + const parseFile = (file?: File) => { + if (!file) { + createNotification({ + text: "Failed to parse uploaded file.", + type: "error" + }); + return; + } + + const reader = new FileReader(); + reader.onload = (event) => { + if (!event?.target?.result) return; + const data = event.target.result.toString(); + setBackupContent(data); + }; + + try { + reader.readAsText(file); + } catch (error) { + console.log(error); + } + }; + + const handleFileUpload = (e: ChangeEvent) => { + e.preventDefault(); + parseFile(e.target?.files?.[0]); + setBackupFileName(e.target?.files?.[0]?.name || ""); + }; + + return ( + { + setBackupContent(""); + setBackupFileName(""); + onOpenChange(state); + }} + > + +

+ By loading a backup, the project's KMS will be switched to the default Infisical KMS. +

+

+ If left unmodified, the filename of the backup should look like the following: +

{`kms-backup-${org?.slug}-${workspace?.slug}-secretManager.infisical.txt`}

+

+
+ + { + fileUploadRef?.current?.click(); + }} + > + + +
+ {backupFileName &&
{backupFileName}
} + {backupContent && ( + + )} +
+
+ ); +}; + export const EncryptionTab = () => { const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace(); @@ -32,6 +193,10 @@ export const EncryptionTab = () => { const { data: activeKms } = useGetActiveProjectKms(currentWorkspace?.id!); const { mutateAsync: updateProjectKms } = useUpdateProjectKms(currentWorkspace?.id!); + const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([ + "createBackupConfirmation", + "loadBackup" + ] as const); const [kmsKeyId, setKmsKeyId] = useState(""); const { @@ -72,23 +237,6 @@ export const EncryptionTab = () => { } }; - const downloadKmsBackup = async () => { - if (!currentWorkspace || !currentOrg) { - return; - } - - const { secretManager } = await fetchProjectKmsBackup(currentWorkspace.id); - - const [, , kmsFunction] = secretManager.split("."); - const file = secretManager; - - const blob = new Blob([file], { type: "text/plain;charset=utf-8" }); - FileSaver.saveAs( - blob, - `kms-backup-${currentOrg.slug}-${currentWorkspace.slug}-${kmsFunction}.infisical.txt` - ); - }; - return (
{ >

Key Management

-
- - -
+ {kmsKeyId !== INTERNAL_KMS_KEY_ID && ( +
+ + +
+ )}

@@ -150,6 +307,18 @@ export const EncryptionTab = () => { )} + handlePopUpToggle("createBackupConfirmation", state)} + org={currentOrg} + workspace={currentWorkspace} + /> + handlePopUpToggle("loadBackup", state)} + org={currentOrg} + workspace={currentWorkspace} + /> ); };