feat: allow sharing of secrets publicly

This commit is contained in:
ShubhamPalriwala
2024-06-10 07:59:58 +05:30
parent 6c596092b0
commit 5df53a25fc
11 changed files with 195 additions and 61 deletions
@@ -15,13 +15,23 @@ export const useCreateSharedSecret = () => {
});
};
export const useCreatePublicSharedSecret = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (inputData: TCreateSharedSecretRequest) => {
const { data } = await apiRequest.post<TSharedSecret>(
"/api/v1/secret-sharing/public",
inputData
);
return data;
},
onSuccess: () => queryClient.invalidateQueries(["sharedSecrets"])
});
};
export const useDeleteSharedSecret = () => {
const queryClient = useQueryClient();
return useMutation<
TSharedSecret,
{ message: string },
{ sharedSecretId: string }
>({
return useMutation<TSharedSecret, { message: string }, { sharedSecretId: string }>({
mutationFn: async ({ sharedSecretId }: TDeleteSharedSecretRequest) => {
const { data } = await apiRequest.delete<TSharedSecret>(
`/api/v1/secret-sharing/${sharedSecretId}`
@@ -22,9 +22,8 @@ import {
Select,
SelectItem
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useCreateSharedSecret } from "@app/hooks/api/secretSharing";
import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api/secretSharing";
import { UsePopUpState } from "@app/hooks/usePopUp";
const expirationUnitsAndActions = [
@@ -65,9 +64,10 @@ type Props = {
popUpName: keyof UsePopUpState<["createSharedSecret"]>,
state?: boolean
) => void;
isPublic: boolean;
};
export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic }: Props) => {
const {
control,
reset,
@@ -76,8 +76,10 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
} = useForm<FormData>({
resolver: yupResolver(schema)
});
const createSharedSecret = useCreateSharedSecret();
const { currentOrg } = useOrganization();
const publicSharedSecretCreator = useCreatePublicSharedSecret();
const privateSharedSecretCreator = useCreateSharedSecret();
const createSharedSecret = isPublic ? publicSharedSecretCreator : privateSharedSecretCreator;
const [newSharedSecret, setnewSharedSecret] = useState("");
const hasSharedSecret = Boolean(newSharedSecret);
const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({
@@ -101,7 +103,6 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
expiresAfterViews
}: FormData) => {
try {
if (!currentOrg?.id) return;
const key = crypto.randomBytes(16).toString("hex");
const hashedHex = crypto.createHash("sha256").update(key).digest("hex");
const { ciphertext, iv, tag } = encryptSymmetric({
@@ -180,7 +181,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
<SecretInput
isVisible={false}
{...field}
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[100px]"
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[70px]"
/>
</FormControl>
)}
@@ -22,7 +22,7 @@ export const ShareSecretSection = () => {
const onDeleteApproved = async () => {
try {
deleteSharedSecret.mutateAsync({
sharedSecretId: (popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.id,
sharedSecretId: (popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.id
});
createNotification({
text: "Successfully deleted shared secret",
@@ -40,7 +40,6 @@ export const ShareSecretSection = () => {
};
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<Head>
<title>Secret Sharing</title>
@@ -60,14 +59,13 @@ export const ShareSecretSection = () => {
Share Secret
</Button>
</div>
<ShareSecretsTable
handlePopUpOpen={handlePopUpOpen}
/>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<ShareSecretsTable handlePopUpOpen={handlePopUpOpen} />
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} isPublic={false} />
<DeleteActionModal
isOpen={popUp.deleteSharedSecretConfirmation.isOpen}
title={`Delete ${(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name || " "
} shared secret?`}
title={`Delete ${
(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name || " "
} shared secret?`}
onChange={(isOpen) => handlePopUpToggle("deleteSharedSecretConfirmation", isOpen)}
deleteKey={(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name}
onClose={() => handlePopUpClose("deleteSharedSecretConfirmation")}
@@ -75,4 +73,4 @@ export const ShareSecretSection = () => {
/>
</div>
);
};
};
@@ -1,13 +1,18 @@
import { useEffect, useMemo } from "react";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { decryptSymmetric } from "@app/components/utilities/cryptography/crypto";
import { useTimedReset } from "@app/hooks";
import { Button } from "@app/components/v2";
import { usePopUp, useTimedReset } from "@app/hooks";
import { useGetActiveSharedSecretByIdAndHashedHex } from "@app/hooks/api/secretSharing";
import { DragonMainImage, SecretTable } from "./components";
import { AddShareSecretModal } from "../ShareSecretPage/components/AddShareSecretModal";
import { SecretTable } from "./components";
export const ShareSecretPublicPage = () => {
const router = useRouter();
@@ -53,36 +58,92 @@ export const ShareSecretPublicPage = () => {
navigator.clipboard.writeText(decryptedSecret);
setIsUrlCopied(true);
};
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp(["createSharedSecret"] as const);
return (
<div className="flex flex-col justify-between bg-bunker-800 text-gray-200 md:h-screen">
<div className="flex h-screen flex-col bg-bunker-800 text-gray-200">
<Head>
<title>Secret Shared | Infisical</title>
<link rel="icon" href="/infisical.ico" />
</Head>
<div className="my-4 flex justify-center md:my-8">
<Image src="/images/biglogo.png" height={180} width={240} alt="Infisical logo" />
<div className="flex items-center justify-center p-4">
<Link href="https://infisical.com">
<Image
src="/images/biglogo.png"
height={60}
width={80}
alt="Infisical logo"
className="cursor-pointer"
/>
</Link>
</div>
<p className="mb-6 px-8 text-center text-xl md:px-0 md:text-3xl">
A secret has been shared with you securely via Infisical
</p>
<div className="flex min-h-screen w-full flex-col md:flex-row">
<DragonMainImage />
<div className="m-4 flex flex-1 flex-col items-center justify-start md:m-0">
<p className="mt-8 mb-2 text-xl font-semibold text-mineshaft-100 md:mt-20">
Shared Secret
<div className="flex flex-1 flex-col items-center justify-center px-4">
<div className="flex w-full max-w-4xl flex-col items-center gap-4 md:gap-20">
<p className="text-center text-xl font-semibold text-gray-200 md:text-3xl">
Secret Shared via Infisical
</p>
<div className="mb-4 rounded-lg md:p-2">
<SecretTable
isLoading={isLoading}
decryptedSecret={decryptedSecret}
isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard}
/>
<div className="flex w-full flex-grow flex-col gap-6 md:flex-row md:gap-12">
<div className="flex-1 self-center pt-4 text-center md:pt-0 md:text-left">
<p className="pb-2 font-semibold text-mineshaft-100 md:pb-4 md:text-xl">
Safe & Secure
</p>
<p className="md:text-md text-sm">
Infisical uses <span className="text-primary">Zero Knowledge</span> to ensure that
your secrets are truly private (even from us).
</p>
</div>
<div className="flex-1 rounded-lg p-2">
<SecretTable
isLoading={isLoading}
decryptedSecret={decryptedSecret}
isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard}
/>
</div>
<div className="flex-1 self-center text-center md:text-right">
<p className="pb-2 font-semibold text-mineshaft-100 md:pb-4 md:text-xl">
Open Source
</p>
<p className="md:text-md text-sm">
Infisical is open source. <br className="hidden md:inline" />
Check us out on{" "}
<a
href="https://github.com/infisical/infisical"
target="_blank"
rel="noopener noreferrer"
className="text-primary"
>
GitHub
</a>
.
</p>
</div>
</div>
<Button
className="mt-4 max-w-[600px] md:mt-0"
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
handlePopUpOpen("createSharedSecret");
}}
>
Share your own Secret
</Button>
</div>
</div>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} isPublic />
<div className="flex items-center justify-center p-4">
<p className="text-center text-sm text-gray-200">
Developed by{" "}
<a className="text-primary" href="https://infisical.com">
Infisical
</a>
<br />
Open Source Secret Management{" "}
</p>
</div>
</div>
);
};
@@ -1,14 +0,0 @@
import Image from "next/image";
export const DragonMainImage = () => {
return (
<div className="hidden flex-1 flex-col items-center justify-center md:block md:items-start md:p-4">
<Image
src="/images/dragon-book.svg"
height={1000}
width={1413}
alt="Infisical Dragon - Came to send you a secret!"
/>
</div>
);
};
@@ -1,2 +1 @@
export { DragonMainImage } from "./MainImage";
export { SecretTable } from "./SecretTable";