mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 14:27:23 +00:00
feat: allow sharing of secrets publicly
This commit is contained in:
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user