mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: added option to share secret directly from main page
This commit is contained in:
@@ -61,6 +61,7 @@ type Props = {
|
|||||||
onCreateTag: () => void;
|
onCreateTag: () => void;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
|
handleSecretShare: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SecretItem = memo(
|
export const SecretItem = memo(
|
||||||
@@ -75,7 +76,8 @@ export const SecretItem = memo(
|
|||||||
onCreateTag,
|
onCreateTag,
|
||||||
onToggleSecretSelect,
|
onToggleSecretSelect,
|
||||||
environment,
|
environment,
|
||||||
secretPath
|
secretPath,
|
||||||
|
handleSecretShare
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
@@ -420,8 +422,9 @@ export const SecretItem = memo(
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
secretReminderRepeatDays && secretReminderRepeatDays > 0
|
secretReminderRepeatDays && secretReminderRepeatDays > 0
|
||||||
? `Every ${secretReminderRepeatDays} day${Number(secretReminderRepeatDays) > 1 ? "s" : ""
|
? `Every ${secretReminderRepeatDays} day${
|
||||||
}
|
Number(secretReminderRepeatDays) > 1 ? "s" : ""
|
||||||
|
}
|
||||||
`
|
`
|
||||||
: "Reminder"
|
: "Reminder"
|
||||||
}
|
}
|
||||||
@@ -461,6 +464,20 @@ export const SecretItem = memo(
|
|||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
<IconButton
|
||||||
|
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6"
|
||||||
|
variant="plain"
|
||||||
|
size="md"
|
||||||
|
ariaLabel="share-secret"
|
||||||
|
onClick={handleSecretShare}
|
||||||
|
>
|
||||||
|
<Tooltip content="Share Secret">
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.ShareSecret}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
className="w-auto border border-mineshaft-600 bg-mineshaft-800 p-2 drop-shadow-2xl"
|
className="w-auto border border-mineshaft-600 bg-mineshaft-800 p-2 drop-shadow-2xl"
|
||||||
sticky="always"
|
sticky="always"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { secretKeys } from "@app/hooks/api/secrets/queries";
|
|||||||
import { DecryptedSecret, SecretType } from "@app/hooks/api/secrets/types";
|
import { DecryptedSecret, SecretType } from "@app/hooks/api/secrets/types";
|
||||||
import { secretSnapshotKeys } from "@app/hooks/api/secretSnapshots/queries";
|
import { secretSnapshotKeys } from "@app/hooks/api/secretSnapshots/queries";
|
||||||
import { UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
import { UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
||||||
|
import { AddShareSecretModal } from "@app/views/ShareSecretPage/components/AddShareSecretModal";
|
||||||
|
|
||||||
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
||||||
import { Filter, GroupBy, SortDir } from "../../SecretMainPage.types";
|
import { Filter, GroupBy, SortDir } from "../../SecretMainPage.types";
|
||||||
@@ -95,7 +96,8 @@ export const SecretListView = ({
|
|||||||
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
"deleteSecret",
|
"deleteSecret",
|
||||||
"secretDetail",
|
"secretDetail",
|
||||||
"createTag"
|
"createTag",
|
||||||
|
"createSharedSecret"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
// strip of side effect queries
|
// strip of side effect queries
|
||||||
@@ -365,6 +367,11 @@ export const SecretListView = ({
|
|||||||
onDeleteSecret={onDeleteSecret}
|
onDeleteSecret={onDeleteSecret}
|
||||||
onDetailViewSecret={onDetailViewSecret}
|
onDetailViewSecret={onDetailViewSecret}
|
||||||
onCreateTag={onCreateTag}
|
onCreateTag={onCreateTag}
|
||||||
|
handleSecretShare={() =>
|
||||||
|
handlePopUpOpen("createSharedSecret", {
|
||||||
|
value: secret.valueOverride ?? secret.value
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -396,6 +403,12 @@ export const SecretListView = ({
|
|||||||
isOpen={popUp.createTag.isOpen}
|
isOpen={popUp.createTag.isOpen}
|
||||||
onToggle={(isOpen) => handlePopUpToggle("createTag", isOpen)}
|
onToggle={(isOpen) => handlePopUpToggle("createTag", isOpen)}
|
||||||
/>
|
/>
|
||||||
|
<AddShareSecretModal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
isPublic={false}
|
||||||
|
inModal
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
faCopy,
|
faCopy,
|
||||||
faEllipsis,
|
faEllipsis,
|
||||||
faKey,
|
faKey,
|
||||||
|
faShare,
|
||||||
faTags
|
faTags
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -66,7 +67,8 @@ export enum FontAwesomeSpriteName {
|
|||||||
Override = "secret-override",
|
Override = "secret-override",
|
||||||
Close = "close",
|
Close = "close",
|
||||||
CheckedCircle = "check-circle",
|
CheckedCircle = "check-circle",
|
||||||
ReplicatedSecretKey = "secret-replicated"
|
ReplicatedSecretKey = "secret-replicated",
|
||||||
|
ShareSecret = "share-secret"
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is an optimization technique
|
// this is an optimization technique
|
||||||
@@ -82,5 +84,6 @@ export const FontAwesomeSpriteSymbols = [
|
|||||||
{ icon: faCodeBranch, symbol: FontAwesomeSpriteName.Override },
|
{ icon: faCodeBranch, symbol: FontAwesomeSpriteName.Override },
|
||||||
{ icon: faClose, symbol: FontAwesomeSpriteName.Close },
|
{ icon: faClose, symbol: FontAwesomeSpriteName.Close },
|
||||||
{ icon: faCheckCircle, symbol: FontAwesomeSpriteName.CheckedCircle },
|
{ icon: faCheckCircle, symbol: FontAwesomeSpriteName.CheckedCircle },
|
||||||
{ icon: faClone, symbol: FontAwesomeSpriteName.ReplicatedSecretKey }
|
{ icon: faClone, symbol: FontAwesomeSpriteName.ReplicatedSecretKey },
|
||||||
|
{ icon: faShare, symbol: FontAwesomeSpriteName.ShareSecret }
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,14 +6,7 @@ import * as yup from "yup";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { encryptSymmetric } from "@app/components/utilities/cryptography/crypto";
|
import { encryptSymmetric } from "@app/components/utilities/cryptography/crypto";
|
||||||
import {
|
import { Button, FormControl, Input, ModalClose, Select, SelectItem } from "@app/components/v2";
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
ModalClose,
|
|
||||||
Select,
|
|
||||||
SelectItem
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api/secretSharing";
|
import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api/secretSharing";
|
||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
@@ -31,7 +24,8 @@ export const AddShareSecretForm = ({
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
setNewSharedSecret
|
setNewSharedSecret,
|
||||||
|
isInputDisabled
|
||||||
}: {
|
}: {
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
inModal: boolean;
|
inModal: boolean;
|
||||||
@@ -39,6 +33,7 @@ export const AddShareSecretForm = ({
|
|||||||
control: any;
|
control: any;
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
setNewSharedSecret: (value: string) => void;
|
setNewSharedSecret: (value: string) => void;
|
||||||
|
isInputDisabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const publicSharedSecretCreator = useCreatePublicSharedSecret();
|
const publicSharedSecretCreator = useCreatePublicSharedSecret();
|
||||||
const privateSharedSecretCreator = useCreateSharedSecret();
|
const privateSharedSecretCreator = useCreateSharedSecret();
|
||||||
@@ -124,12 +119,13 @@ export const AddShareSecretForm = ({
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<form className="flex w-full flex-col items-center" onSubmit={handleSubmit(onFormSubmit)}>
|
<form className="flex w-full flex-col items-center" onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<div className={`${!inModal && "border border-mineshaft-600 bg-mineshaft-800 rounded-md p-6"}`}>
|
<div
|
||||||
|
className={`${!inModal && "rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6"}`}
|
||||||
|
>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="value"
|
name="value"
|
||||||
defaultValue=""
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Shared Secret"
|
label="Shared Secret"
|
||||||
@@ -137,16 +133,17 @@ export const AddShareSecretForm = ({
|
|||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
|
disabled={isInputDisabled}
|
||||||
placeholder="Enter sensitive data to share via an encrypted link..."
|
placeholder="Enter sensitive data to share via an encrypted link..."
|
||||||
{...field}
|
{...field}
|
||||||
className="py-1.5 w-full h-40 placeholder:text-mineshaft-400 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/30 focus:border-primary-400/50 outline-none border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[70px]"
|
className="h-40 min-h-[70px] w-full rounded-md border border-mineshaft-600 bg-mineshaft-900 py-1.5 px-2 text-bunker-300 outline-none transition-all placeholder:text-mineshaft-400 hover:border-primary-400/30 focus:border-primary-400/50 group-hover:mr-2"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-row justify-center">
|
<div className="flex w-full flex-row justify-center">
|
||||||
<div className="hidden sm:block sm:w-2/6 flex">
|
<div className="flex hidden sm:block sm:w-2/6">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="expiresAfterViews"
|
name="expiresAfterViews"
|
||||||
@@ -163,12 +160,12 @@ export const AddShareSecretForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden sm:flex sm:w-1/7 items-center justify-center px-2 mx-auto">
|
<div className="sm:w-1/7 mx-auto hidden items-center justify-center px-2 sm:flex">
|
||||||
<p className="px-4 text-sm text-gray-400">OR</p>
|
<p className="px-4 text-sm text-gray-400">OR</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full sm:w-3/6 flex justify-end">
|
<div className="flex w-full justify-end sm:w-3/6">
|
||||||
<div className="flex justify-start">
|
<div className="flex justify-start">
|
||||||
<div className="flex w-full pr-2 justify-center">
|
<div className="flex w-full justify-center pr-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="expiresInValue"
|
name="expiresInValue"
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: yupResolver(schema)
|
resolver: yupResolver(schema)
|
||||||
@@ -45,6 +46,8 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
initialState: false
|
initialState: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [isSecretInputDisabled, setIsSecretInputDisabled] = useState(false);
|
||||||
|
|
||||||
const copyUrlToClipboard = () => {
|
const copyUrlToClipboard = () => {
|
||||||
navigator.clipboard.writeText(newSharedSecret);
|
navigator.clipboard.writeText(newSharedSecret);
|
||||||
setIsUrlCopied(true);
|
setIsUrlCopied(true);
|
||||||
@@ -55,6 +58,13 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
}
|
}
|
||||||
}, [isUrlCopied]);
|
}, [isUrlCopied]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (popUp.createSharedSecret.data) {
|
||||||
|
setValue("value", (popUp.createSharedSecret.data as { value: string }).value);
|
||||||
|
setIsSecretInputDisabled(true);
|
||||||
|
}
|
||||||
|
}, [popUp.createSharedSecret.data]);
|
||||||
|
|
||||||
// eslint-disable-next-line no-nested-ternary
|
// eslint-disable-next-line no-nested-ternary
|
||||||
return inModal ? (
|
return inModal ? (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -63,6 +73,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
handlePopUpToggle("createSharedSecret", open);
|
handlePopUpToggle("createSharedSecret", open);
|
||||||
reset();
|
reset();
|
||||||
setNewSharedSecret("");
|
setNewSharedSecret("");
|
||||||
|
setIsSecretInputDisabled(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
@@ -77,6 +88,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
setNewSharedSecret={setNewSharedSecret}
|
setNewSharedSecret={setNewSharedSecret}
|
||||||
|
isInputDisabled={isSecretInputDisabled}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ViewAndCopySharedSecret
|
<ViewAndCopySharedSecret
|
||||||
@@ -96,6 +108,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle, isPublic, inModa
|
|||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
setNewSharedSecret={setNewSharedSecret}
|
setNewSharedSecret={setNewSharedSecret}
|
||||||
|
isInputDisabled={isSecretInputDisabled}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ViewAndCopySharedSecret
|
<ViewAndCopySharedSecret
|
||||||
|
|||||||
Reference in New Issue
Block a user