feat(frontend): Adding accessType on secret sharing

This commit is contained in:
Alfonso Hernandez
2024-07-19 03:17:47 +02:00
parent e45585a909
commit eb82fc0d9a
6 changed files with 80 additions and 16 deletions

View File

@@ -21,7 +21,7 @@ export const EmptyState = ({
}: Props) => (
<div
className={twMerge(
"flex w-full flex-col items-center bg-mineshaft-800 px-2 pt-6 text-bunker-300",
"flex w-full flex-col items-center bg-mineshaft-800 px-2 pt-4 text-bunker-300",
className
)}
>

View File

@@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TSharedSecret, TViewSharedSecretResponse } from "./types";
import { SecretSharingAccessType, TSharedSecret, TViewSharedSecretResponse } from "./types";
export const useGetSharedSecrets = () => {
return useQuery({
@@ -17,7 +17,7 @@ export const useGetSharedSecrets = () => {
export const useGetActiveSharedSecretByIdAndHashedHex = (id: string, hashedHex: string) => {
return useQuery<TViewSharedSecretResponse, [string]>({
queryFn: async () => {
if(!id || !hashedHex) return Promise.resolve({ encryptedValue: "", iv: "", tag: "" });
if(!id || !hashedHex) return Promise.resolve({ encryptedValue: "", iv: "", tag: "", accessType: SecretSharingAccessType.Organization, orgName: "" });
const { data } = await apiRequest.get<TViewSharedSecretResponse>(
`/api/v1/secret-sharing/public/${id}?hashedHex=${hashedHex}`
);
@@ -25,6 +25,8 @@ export const useGetActiveSharedSecretByIdAndHashedHex = (id: string, hashedHex:
encryptedValue: data.encryptedValue,
iv: data.iv,
tag: data.tag,
accessType: data.accessType,
orgName: data.orgName
};
}
});

View File

@@ -13,14 +13,22 @@ export type TCreateSharedSecretRequest = {
hashedHex: string;
expiresAt: Date;
expiresAfterViews: number;
accessType: SecretSharingAccessType;
};
export type TViewSharedSecretResponse = {
encryptedValue: string;
iv: string;
tag: string;
accessType: SecretSharingAccessType;
orgName?: string;
};
export type TDeleteSharedSecretRequest = {
sharedSecretId: string;
};
export enum SecretSharingAccessType {
Anyone = "anyone",
Organization = "organization"
}

View File

@@ -7,13 +7,14 @@ import * as yup from "yup";
import { createNotification } from "@app/components/notifications";
import { encryptSymmetric } from "@app/components/utilities/cryptography/crypto";
import { Button, Checkbox, FormControl, Input, ModalClose, Select, SelectItem } from "@app/components/v2";
import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api/secretSharing";
import { SecretSharingAccessType, useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api/secretSharing";
const schema = yup.object({
value: yup.string().max(10000).required().label("Shared Secret Value"),
expiresAfterSingleView: yup.boolean().required().label("Expires After Views"),
expiresInValue: yup.number().min(1).required().label("Expiration Value"),
expiresInUnit: yup.string().required().label("Expiration Unit")
expiresInUnit: yup.string().required().label("Expiration Unit"),
accessType: yup.string().required().label("General Access")
});
export type FormData = yup.InferType<typeof schema>;
@@ -65,7 +66,8 @@ export const AddShareSecretForm = ({
value,
expiresInValue,
expiresInUnit,
expiresAfterSingleView
expiresAfterSingleView,
accessType
}: FormData) => {
try {
const key = crypto.randomBytes(16).toString("hex");
@@ -89,7 +91,8 @@ export const AddShareSecretForm = ({
tag,
hashedHex,
expiresAt,
expiresAfterViews: expiresAfterSingleView ? 1 : 1000
expiresAfterViews: expiresAfterSingleView ? 1 : 1000,
accessType: accessType as SecretSharingAccessType
});
setNewSharedSecret(
`${window.location.origin}/shared/secret/${id}?key=${encodeURIComponent(
@@ -143,7 +146,7 @@ export const AddShareSecretForm = ({
)}
/>
</div>
<div className="flex w-full flex-col md:flex-row justify-start">
<div className="flex w-full flex-col md:flex-row justify-strech">
<div className="flex justify-start">
<div className="flex justify-start">
<div className="flex w-full justify-center pr-2">
@@ -188,8 +191,8 @@ export const AddShareSecretForm = ({
</div>
</div>
</div>
<div className="sm:w-1/7 mx-auto items-center justify-center px-6 hidden md:flex">
<p className="px-4 mt-2 text-sm text-gray-400">AND</p>
<div className="sm:w-1/7 mx-auto items-center justify-center hidden md:flex">
<p className="mt-2 text-sm text-gray-400">AND</p>
</div>
<div className="items-center pb-4 md:pb-0 md:pt-2 flex">
<Controller
@@ -227,7 +230,23 @@ export const AddShareSecretForm = ({
</div>
</div>
</div>
<div className={`flex items-center ${!inModal && "justify-start pt-2"}`}>
<Controller
control={control}
name="accessType"
defaultValue="organization"
render={({ field: { onChange, ...field } }) => (
<FormControl label="General Access">
<Select
{...field}
onValueChange={(e) => onChange(e)}
>
<SelectItem value="organization">People within your organization</SelectItem>
<SelectItem value="anyone">Anyone</SelectItem>
</Select>
</FormControl>
)}
/>
<div className={`flex items-center space-x-4 mt-6 ${!inModal && "justify-start pt-2"}`}>
<Button className="mr-0" type="submit" isDisabled={isSubmitting} isLoading={isSubmitting}>
{inModal ? "Create" : "Share Secret"}
</Button>

View File

@@ -20,12 +20,15 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
const [hashedHex, key] = urlEncodedPublicKey
? urlEncodedPublicKey.toString().split("-")
: ["", ""];
const publicKey = decodeURIComponent(urlEncodedPublicKey as string);
const { isLoading, data } = useGetActiveSharedSecretByIdAndHashedHex(
id as string,
hashedHex as string
);
console.log(data);
const accessType = data?.accessType;
const orgName = data?.orgName;
const decryptedSecret = useMemo(() => {
if (data && data.encryptedValue && publicKey) {
@@ -87,6 +90,8 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
decryptedSecret={decryptedSecret}
isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard}
accessType={accessType}
orgName={orgName}
/>
)}
</div>

View File

@@ -1,14 +1,17 @@
import { faCheck, faCopy, faEye, faEyeSlash, faKey } from "@fortawesome/free-solid-svg-icons";
import { faArrowRight, faCheck, faCopy, faEye, faEyeSlash, faKey } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { EmptyState, IconButton, Td, Tr } from "@app/components/v2";
import { Button, EmptyState, IconButton, Td, Tr } from "@app/components/v2";
import { useToggle } from "@app/hooks";
import { SecretSharingAccessType } from "@app/hooks/api/secretSharing/types";
type Props = {
isLoading: boolean;
decryptedSecret: string;
isUrlCopied: boolean;
copyUrlToClipboard: () => void;
accessType?: SecretSharingAccessType;
orgName?: string;
};
const replaceContentWithDot = (str: string) => {
@@ -24,20 +27,47 @@ export const SecretTable = ({
isLoading,
decryptedSecret,
isUrlCopied,
copyUrlToClipboard
copyUrlToClipboard,
accessType,
orgName
}: Props) => {
const [isVisible, setIsVisible] = useToggle(false);
const title = (<p>You need to be logged into <strong>{orgName}</strong> to access this secret</p>);
return (
<div className="flex w-full items-center justify-center rounded-md border border-solid border-mineshaft-700 bg-mineshaft-800 p-2">
{isLoading && <div className="bg-mineshaft-800 text-center text-bunker-400">Loading...</div>}
{!isLoading && !decryptedSecret && (
{!isLoading && !decryptedSecret && accessType !== SecretSharingAccessType.Organization && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
<EmptyState title="Secret has either expired or does not exist!" icon={faKey} />
</Td>
</Tr>
)}
{!isLoading && !decryptedSecret && accessType === SecretSharingAccessType.Organization && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-4000">
<EmptyState title={title} icon={faKey}>
<div className="flex flex-1 flex-col items-center justify-center pt-6">
<a
href="/login"
target="_blank"
rel="noopener noreferrer"
>
<Button
colorSchema="primary"
size="sm"
onClick={() => {}}
rightIcon={<FontAwesomeIcon icon={faArrowRight} className="ml-2" />}
>
Login to view this secret
</Button>
</a>
</div>
</EmptyState>
</Td>
</Tr>
)}
{!isLoading && decryptedSecret && (
<div className="dark relative flex h-full w-full items-center overflow-y-auto rounded-md border border-mineshaft-700 bg-mineshaft-900 p-2 pr-2 md:p-3">
<div