diff --git a/backend/src/db/migrations/20240530044702_universal-text-in-secret-sharing.ts b/backend/src/db/migrations/20240530044702_universal-text-in-secret-sharing.ts index 63d9dd81c..e23d134db 100644 --- a/backend/src/db/migrations/20240530044702_universal-text-in-secret-sharing.ts +++ b/backend/src/db/migrations/20240530044702_universal-text-in-secret-sharing.ts @@ -8,8 +8,7 @@ export async function up(knex: Knex): Promise { await knex.schema.alterTable(TableName.SecretSharing, (t) => { if (!hasExpiresAfterViewsColumn) { - t.integer("expiresAfterViews").nullable(); - t.timestamp("expiresAt").nullable().alter(); + t.integer("expiresAfterViews"); } if (hasSecretNameColumn) { @@ -25,7 +24,6 @@ export async function down(knex: Knex): Promise { await knex.schema.alterTable(TableName.SecretSharing, (t) => { if (hasExpiresAfterViewsColumn) { t.dropColumn("expiresAfterViews"); - t.timestamp("expiresAt").notNullable().alter(); } if (!hasSecretNameColumn) { diff --git a/backend/src/db/schemas/secret-sharing.ts b/backend/src/db/schemas/secret-sharing.ts index 541cd1fc0..6fa104ebe 100644 --- a/backend/src/db/schemas/secret-sharing.ts +++ b/backend/src/db/schemas/secret-sharing.ts @@ -13,7 +13,7 @@ export const SecretSharingSchema = z.object({ iv: z.string(), tag: z.string(), hashedHex: z.string(), - expiresAt: z.date().nullable().optional(), + expiresAt: z.date(), userId: z.string().uuid(), orgId: z.string().uuid(), createdAt: z.date(), diff --git a/backend/src/server/routes/v1/secret-sharing-router.ts b/backend/src/server/routes/v1/secret-sharing-router.ts index f1016576c..6cb551698 100644 --- a/backend/src/server/routes/v1/secret-sharing-router.ts +++ b/backend/src/server/routes/v1/secret-sharing-router.ts @@ -84,9 +84,8 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) => hashedHex: z.string(), expiresAt: z .string() - .optional() .refine((date) => date === undefined || new Date(date) > new Date(), "Expires at should be a future date"), - expiresAfterViews: z.number().optional() + expiresAfterViews: z.number() }), response: { 200: z.object({ @@ -107,7 +106,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) => iv, tag, hashedHex, - expiresAt: expiresAt ? new Date(expiresAt) : undefined, + expiresAt: new Date(expiresAt), expiresAfterViews }); return { id: sharedSecret.id }; diff --git a/backend/src/services/secret-sharing/secret-sharing-types.ts b/backend/src/services/secret-sharing/secret-sharing-types.ts index 5af475c52..5f35b2848 100644 --- a/backend/src/services/secret-sharing/secret-sharing-types.ts +++ b/backend/src/services/secret-sharing/secret-sharing-types.ts @@ -13,8 +13,8 @@ export type TCreateSharedSecretDTO = { iv: string; tag: string; hashedHex: string; - expiresAt?: Date; - expiresAfterViews?: number; + expiresAt: Date; + expiresAfterViews: number; } & TSharedSecretPermission; export type TDeleteSharedSecretDTO = { diff --git a/docs/documentation/platform/secret-sharing.mdx b/docs/documentation/platform/secret-sharing.mdx index 525f3512e..680751820 100644 --- a/docs/documentation/platform/secret-sharing.mdx +++ b/docs/documentation/platform/secret-sharing.mdx @@ -1,11 +1,11 @@ --- title: "Secret Sharing" sidebarTitle: "Secret Sharing" -description: "Learn how to share time or view-count bound secrets securely with anyone on the internet." +description: "Learn how to share time & view-count bound secrets securely with anyone on the internet." --- Developers frequently need to share secrets with team members, contractors, or other third parties, which can be risky due to potential leaks or misuse. -Infisical offers a secure solution for sharing secrets over the internet in a time-bound manner as well as view count bound manner. +Infisical offers a secure solution for sharing secrets over the internet in a time and view count bound manner. With its zero-knowledge architecture, secrets shared via Infisical remain unreadable even to Infisical itself. @@ -21,15 +21,9 @@ With its zero-knowledge architecture, secrets shared via Infisical remain unread zero knowledge architecture. -3. Click on the **Share Secret** button. +3. Click on the **Share Secret** button. Set the secret, its expiration time as well as the number of views allowed. It expires as soon as any of the conditions are met. - a. Time-bound secret: Set the expiration time in minutes, hours, days, or weeks. - - ![Add Time-Bound Sharing Secret](../../images/platform/secret-sharing/new-time-bound-secret.png) - - b. View Count-bound secret: Set the number of views after which the secret will expire. - - ![Add View-Bound Sharing Secret](../../images/platform/secret-sharing/new-view-bound-secret.png) + ![Add View-Bound Sharing Secret](../../images/platform/secret-sharing/create-new-secret.png) Secret once set cannot be changed. This is to ensure that the secret is not diff --git a/docs/images/platform/secret-sharing/create-new-secret.png b/docs/images/platform/secret-sharing/create-new-secret.png new file mode 100644 index 000000000..335fca2b2 Binary files /dev/null and b/docs/images/platform/secret-sharing/create-new-secret.png differ diff --git a/docs/images/platform/secret-sharing/new-secret.png b/docs/images/platform/secret-sharing/new-secret.png deleted file mode 100644 index 7ac97c132..000000000 Binary files a/docs/images/platform/secret-sharing/new-secret.png and /dev/null differ diff --git a/docs/images/platform/secret-sharing/new-time-bound-secret.png b/docs/images/platform/secret-sharing/new-time-bound-secret.png deleted file mode 100644 index fb00ccbd0..000000000 Binary files a/docs/images/platform/secret-sharing/new-time-bound-secret.png and /dev/null differ diff --git a/docs/images/platform/secret-sharing/new-view-bound-secret.png b/docs/images/platform/secret-sharing/new-view-bound-secret.png deleted file mode 100644 index bc88c3bf5..000000000 Binary files a/docs/images/platform/secret-sharing/new-view-bound-secret.png and /dev/null differ diff --git a/docs/images/platform/secret-sharing/public-view.png b/docs/images/platform/secret-sharing/public-view.png index f1bd9482f..8b4077c65 100644 Binary files a/docs/images/platform/secret-sharing/public-view.png and b/docs/images/platform/secret-sharing/public-view.png differ diff --git a/frontend/src/hooks/api/secretSharing/types.ts b/frontend/src/hooks/api/secretSharing/types.ts index 272100bc6..424e3525c 100644 --- a/frontend/src/hooks/api/secretSharing/types.ts +++ b/frontend/src/hooks/api/secretSharing/types.ts @@ -11,8 +11,8 @@ export type TCreateSharedSecretRequest = { iv: string; tag: string; hashedHex: string; - expiresAt?: Date; - expiresAfterViews?: number; + expiresAt: Date; + expiresAfterViews: number; }; export type TViewSharedSecretResponse = { diff --git a/frontend/src/views/ShareSecretPage/components/AddShareSecretModal.tsx b/frontend/src/views/ShareSecretPage/components/AddShareSecretModal.tsx index 107c9beb4..5fae1067a 100644 --- a/frontend/src/views/ShareSecretPage/components/AddShareSecretModal.tsx +++ b/frontend/src/views/ShareSecretPage/components/AddShareSecretModal.tsx @@ -20,7 +20,7 @@ import { ModalContent, SecretInput, Select, - SelectItem, + SelectItem } from "@app/components/v2"; import { useOrganization } from "@app/context"; import { useTimedReset } from "@app/hooks"; @@ -62,10 +62,9 @@ const expirationUnitsAndActions = [ const schema = yup.object({ value: yup.string().max(10000).required().label("Shared Secret Value"), - expiryOption: yup.string().optional().label("Expiration Option").default("Time"), - expiresAfterViews: yup.number().min(1).optional().label("Expires After Views"), - expiresInValue: yup.number().min(1).optional().label("Expiration Value"), - expiresInUnit: yup.string().optional().label("Expiration Unit") + expiresAfterViews: yup.number().min(1).required().label("Expires After Views"), + expiresInValue: yup.number().min(1).required().label("Expiration Value"), + expiresInUnit: yup.string().required().label("Expiration Unit") }); export type FormData = yup.InferType; @@ -90,7 +89,6 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => { const createSharedSecret = useCreateSharedSecret(); const { currentOrg } = useOrganization(); const [newSharedSecret, setnewSharedSecret] = useState(""); - const [expiryOption, setExpiryOption] = useState<"Time" | "Views">("Time"); const hasSharedSecret = Boolean(newSharedSecret); const [isUrlCopied, , setIsUrlCopied] = useTimedReset({ initialState: false @@ -134,8 +132,8 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => { iv, tag, hashedHex, - expiresAt: expiryOption === "Time" ? expiresAt : undefined, - expiresAfterViews: expiryOption === "Views" ? expiresAfterViews : undefined + expiresAt, + expiresAfterViews }); setnewSharedSecret( `${window.location.origin}/shared/secret/${id}?key=${encodeURIComponent( @@ -198,98 +196,72 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => { )} />
-
+
( + name="expiresAfterViews" + defaultValue={1} + render={({ field, fieldState: { error } }) => ( - + )} />
-
- {expiryOption === "Views" ? ( - ( - - - - )} - /> - ) : ( -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
+
+

OR

+
+
+
+
+ ( + + + + )} + />
- )} +
+ ( + + + + )} + /> +
+
diff --git a/frontend/src/views/ShareSecretPage/components/ShareSecretsRow.tsx b/frontend/src/views/ShareSecretPage/components/ShareSecretsRow.tsx index f26ecd61a..f731fe19b 100644 --- a/frontend/src/views/ShareSecretPage/components/ShareSecretsRow.tsx +++ b/frontend/src/views/ShareSecretPage/components/ShareSecretsRow.tsx @@ -110,22 +110,17 @@ export const ShareSecretsRow = ({

{formatDate(row.createdAt)}

- {row.expiresAfterViews ? ( -

- Valid for {row.expiresAfterViews} more views + <> +

+ {getValidityStatusText(row.expiresAt!) + timeAgo(row.expiresAt!, currentTime)}

- ) : ( - <> -

- {getValidityStatusText(row.expiresAt!) + timeAgo(row.expiresAt!, currentTime)} -

-

{formatDate(row.expiresAt!)}

- - )} +

{formatDate(row.expiresAt!)}

+ + + +

+ {row.expiresAfterViews} +

{ const { isLoading, data = [] } = useGetSharedSecrets(); let tableData = data.filter( - (secret) => - (secret.expiresAt && new Date(secret.expiresAt) > new Date()) || - (secret.expiresAfterViews && secret.expiresAfterViews > 0) + (secret) => new Date(secret.expiresAt) > new Date() && secret.expiresAfterViews > 0 ); const handleSecretExpiration = () => { tableData = data.filter( - (secret) => - (secret.expiresAt && new Date(secret.expiresAt) > new Date()) || - (secret.expiresAfterViews && secret.expiresAfterViews > 0) + (secret) => new Date(secret.expiresAt) > new Date() && secret.expiresAfterViews > 0 ); }; @@ -50,7 +46,7 @@ export const ShareSecretsTable = ({ handlePopUpOpen }: Props) => { - + diff --git a/frontend/src/views/ShareSecretPublicPage/components/SecretTable.tsx b/frontend/src/views/ShareSecretPublicPage/components/SecretTable.tsx index 526798871..14c5092e7 100644 --- a/frontend/src/views/ShareSecretPublicPage/components/SecretTable.tsx +++ b/frontend/src/views/ShareSecretPublicPage/components/SecretTable.tsx @@ -27,7 +27,7 @@ export const SecretTable = ({ )} {!isLoading && decryptedSecret && ( <> -
+
Encrypted Secret Created Valid UntilEncrypted Secret Created Valid Until Views Left