mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 16:52:38 +00:00
improvement: finish address changes
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { faCheckCircle, faCopy, faInfoCircle, faLockOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { decodeBase64 } from "tweetnacl-util";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
Modal,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
Switch,
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { TCmek, useCmekDecrypt } from "@app/hooks/api/cmeks";
|
||||
|
||||
const formSchema = z.object({
|
||||
ciphertext: z.string()
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
cmek: TCmek;
|
||||
};
|
||||
|
||||
type FormProps = Pick<Props, "cmek">;
|
||||
|
||||
const DecryptForm = ({ cmek }: FormProps) => {
|
||||
const cmekDecrypt = useCmekDecrypt();
|
||||
const [shouldDecode, setShouldDecode] = useState(false);
|
||||
const [plaintext, setPlaintext] = useState("");
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { isSubmitting, errors }
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema)
|
||||
});
|
||||
|
||||
const [copyCiphertext, isCopyingCiphertext, setCopyCipherText] = useTimedReset<string>({
|
||||
initialState: "Copy to Clipboard"
|
||||
});
|
||||
|
||||
const handleDecryptData = async (formData: FormData) => {
|
||||
try {
|
||||
const data = await cmekDecrypt.mutateAsync({ ...formData, keyId: cmek.id });
|
||||
createNotification({
|
||||
text: "Successfully decrypted data",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
setPlaintext(
|
||||
shouldDecode ? Buffer.from(decodeBase64(data.plaintext)).toString("utf8") : data.plaintext
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to decrypt data",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const text = cmekDecrypt.data?.plaintext;
|
||||
if (!text) return;
|
||||
|
||||
setPlaintext(shouldDecode ? Buffer.from(decodeBase64(text)).toString("utf8") : text);
|
||||
}, [shouldDecode]);
|
||||
|
||||
const handleCopyToClipboard = () => {
|
||||
navigator.clipboard.writeText(plaintext ?? "");
|
||||
|
||||
setCopyCipherText("Copied to Clipboard");
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleDecryptData)}>
|
||||
{plaintext ? (
|
||||
<FormControl label="Decrypted Data (plaintext)">
|
||||
<TextArea
|
||||
className="max-h-[20rem] min-h-[10rem] min-w-full max-w-full"
|
||||
isDisabled
|
||||
value={plaintext}
|
||||
/>
|
||||
</FormControl>
|
||||
) : (
|
||||
<FormControl
|
||||
label="Encrypted Data (ciphertext)"
|
||||
errorText={errors.ciphertext?.message}
|
||||
isError={Boolean(errors.ciphertext)}
|
||||
>
|
||||
<TextArea
|
||||
{...register("ciphertext")}
|
||||
className="max-h-[20rem] min-h-[10rem] min-w-full max-w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<Switch
|
||||
id="decode-base-64"
|
||||
isChecked={shouldDecode}
|
||||
onCheckedChange={setShouldDecode}
|
||||
containerClassName="mb-6 ml-0.5 -mt-2.5"
|
||||
>
|
||||
Decode Base64{" "}
|
||||
<Tooltip content="Toggle this switch on if your data was originally plain text.">
|
||||
<FontAwesomeIcon icon={faInfoCircle} className=" text-mineshaft-400" />
|
||||
</Tooltip>
|
||||
</Switch>
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className={`mr-4 ${plaintext ? "w-44" : ""}`}
|
||||
size="sm"
|
||||
leftIcon={
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
plaintext ? (
|
||||
isCopyingCiphertext ? (
|
||||
<FontAwesomeIcon icon={faCheckCircle} />
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faCopy} />
|
||||
)
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faLockOpen} />
|
||||
)
|
||||
}
|
||||
onClick={plaintext ? handleCopyToClipboard : undefined}
|
||||
type={plaintext ? "button" : "submit"}
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting}
|
||||
>
|
||||
{plaintext ? copyCiphertext : "Decrypt"}
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
{plaintext ? "Close" : "Cancel"}
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const CmekDecryptModal = ({ isOpen, onOpenChange, cmek }: Props) => {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent
|
||||
subTitle={
|
||||
<>
|
||||
Decrypt ciphertext using <span className="font-bold">{cmek?.name}</span>. Returns Base64
|
||||
encoded plaintext.
|
||||
</>
|
||||
}
|
||||
title="Decrypt Data"
|
||||
>
|
||||
<DecryptForm cmek={cmek} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user