Merge pull request #3153 from Infisical/daniel/secret-requests

feat(secret-sharing): secret requests
This commit is contained in:
Daniel Hougaard
2025-03-04 04:04:39 +09:00
committed by GitHub
45 changed files with 1885 additions and 52 deletions
@@ -5,8 +5,13 @@ import { apiRequest } from "@app/config/request";
import { secretSharingKeys } from "./queries";
import {
TCreatedSharedSecret,
TCreateSecretRequestRequestDTO,
TCreateSharedSecretRequest,
TDeleteSharedSecretRequest,
TDeleteSecretRequestDTO,
TDeleteSharedSecretRequestDTO,
TRevealedSecretRequest,
TRevealSecretRequestValueRequest,
TSetSecretRequestValueRequest,
TSharedSecret
} from "./types";
@@ -15,7 +20,7 @@ export const useCreateSharedSecret = () => {
return useMutation({
mutationFn: async (inputData: TCreateSharedSecretRequest) => {
const { data } = await apiRequest.post<TCreatedSharedSecret>(
"/api/v1/secret-sharing",
"/api/v1/secret-sharing/shared",
inputData
);
return data;
@@ -30,7 +35,7 @@ export const useCreatePublicSharedSecret = () => {
return useMutation({
mutationFn: async (inputData: TCreateSharedSecretRequest) => {
const { data } = await apiRequest.post<TCreatedSharedSecret>(
"/api/v1/secret-sharing/public",
"/api/v1/secret-sharing/shared/public",
inputData
);
return data;
@@ -40,12 +45,50 @@ export const useCreatePublicSharedSecret = () => {
});
};
export const useCreateSecretRequest = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (inputData: TCreateSecretRequestRequestDTO) => {
const { data } = await apiRequest.post<TCreatedSharedSecret>(
"/api/v1/secret-sharing/requests",
inputData
);
return data;
},
onSuccess: () =>
queryClient.invalidateQueries({ queryKey: secretSharingKeys.allSecretRequests() })
});
};
export const useSetSecretRequestValue = () => {
return useMutation({
mutationFn: async (inputData: TSetSecretRequestValueRequest) => {
const { data } = await apiRequest.post<TSharedSecret>(
`/api/v1/secret-sharing/requests/${inputData.id}/set-value`,
inputData
);
return data;
}
});
};
export const useRevealSecretRequestValue = () => {
return useMutation({
mutationFn: async (inputData: TRevealSecretRequestValueRequest) => {
const { data } = await apiRequest.post<TRevealedSecretRequest>(
`/api/v1/secret-sharing/requests/${inputData.id}/reveal-value`,
inputData
);
return data.secretRequest;
}
});
};
export const useDeleteSharedSecret = () => {
const queryClient = useQueryClient();
return useMutation<TSharedSecret, { message: string }, { sharedSecretId: string }>({
mutationFn: async ({ sharedSecretId }: TDeleteSharedSecretRequest) => {
mutationFn: async ({ sharedSecretId }: TDeleteSharedSecretRequestDTO) => {
const { data } = await apiRequest.delete<TSharedSecret>(
`/api/v1/secret-sharing/${sharedSecretId}`
`/api/v1/secret-sharing/shared/${sharedSecretId}`
);
return data;
},
@@ -53,3 +96,19 @@ export const useDeleteSharedSecret = () => {
queryClient.invalidateQueries({ queryKey: secretSharingKeys.allSharedSecrets() })
});
};
export const useDeleteSecretRequest = () => {
const queryClient = useQueryClient();
return useMutation<TSharedSecret, unknown, TDeleteSecretRequestDTO>({
mutationFn: async ({ secretRequestId }: TDeleteSecretRequestDTO) => {
const { data } = await apiRequest.delete<TSharedSecret>(
`/api/v1/secret-sharing/requests/${secretRequestId}`
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: secretSharingKeys.allSecretRequests() });
}
});
};
@@ -2,16 +2,20 @@ import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TSharedSecret, TViewSharedSecretResponse } from "./types";
import { TGetSecretRequestByIdResponse, TSharedSecret, TViewSharedSecretResponse } from "./types";
export const secretSharingKeys = {
allSharedSecrets: () => ["sharedSecrets"] as const,
specificSharedSecrets: ({ offset, limit }: { offset: number; limit: number }) =>
[...secretSharingKeys.allSharedSecrets(), { offset, limit }] as const,
allSecretRequests: () => ["secretRequests"] as const,
specificSecretRequests: ({ offset, limit }: { offset: number; limit: number }) =>
[...secretSharingKeys.allSecretRequests(), { offset, limit }] as const,
getSecretById: (arg: { id: string; hashedHex: string | null; password?: string }) => [
"shared-secret",
arg
]
],
getSecretRequestById: (arg: { id: string }) => ["secret-request", arg] as const
};
export const useGetSharedSecrets = ({
@@ -30,7 +34,7 @@ export const useGetSharedSecrets = ({
});
const { data } = await apiRequest.get<{ secrets: TSharedSecret[]; totalCount: number }>(
"/api/v1/secret-sharing/",
"/api/v1/secret-sharing/shared",
{
params
}
@@ -40,6 +44,29 @@ export const useGetSharedSecrets = ({
});
};
export const useGetSecretRequests = ({
offset = 0,
limit = 25
}: {
offset: number;
limit: number;
}) => {
return useQuery({
queryKey: secretSharingKeys.specificSecretRequests({ offset, limit }),
queryFn: async () => {
const { data } = await apiRequest.get<{ secrets: TSharedSecret[]; totalCount: number }>(
"/api/v1/secret-sharing/requests",
{
params: {
offset: String(offset),
limit: String(limit)
}
}
);
return data;
}
});
};
export const useGetActiveSharedSecretById = ({
sharedSecretId,
hashedHex,
@@ -53,7 +80,7 @@ export const useGetActiveSharedSecretById = ({
queryKey: secretSharingKeys.getSecretById({ id: sharedSecretId, hashedHex, password }),
queryFn: async () => {
const { data } = await apiRequest.post<TViewSharedSecretResponse>(
`/api/v1/secret-sharing/public/${sharedSecretId}`,
`/api/v1/secret-sharing/shared/public/${sharedSecretId}`,
{
...(hashedHex && { hashedHex }),
password
@@ -65,3 +92,16 @@ export const useGetActiveSharedSecretById = ({
enabled: Boolean(sharedSecretId)
});
};
export const useGetSecretRequestById = ({ secretRequestId }: { secretRequestId: string }) => {
return useQuery({
queryKey: secretSharingKeys.getSecretRequestById({ id: secretRequestId }),
queryFn: async () => {
const { data } = await apiRequest.get<TGetSecretRequestByIdResponse>(
`/api/v1/secret-sharing/requests/${secretRequestId}`
);
return data.secretRequest;
}
});
};
+41 -1
View File
@@ -6,13 +6,21 @@ export type TSharedSecret = {
updatedAt: Date;
name: string | null;
lastViewedAt?: Date;
accessType: SecretSharingAccessType;
expiresAt: Date;
expiresAfterViews: number | null;
encryptedValue: string;
encryptedSecret: string;
iv: string;
tag: string;
};
export type TRevealedSecretRequest = {
secretRequest: {
secretValue: string;
} & TSharedSecret;
};
export type TCreatedSharedSecret = {
id: string;
};
@@ -26,6 +34,21 @@ export type TCreateSharedSecretRequest = {
accessType?: SecretSharingAccessType;
};
export type TCreateSecretRequestRequestDTO = {
name?: string;
accessType?: SecretSharingAccessType;
expiresAt: Date;
};
export type TSetSecretRequestValueRequest = {
secretValue: string;
id: string;
};
export type TRevealSecretRequestValueRequest = {
id: string;
};
export type TViewSharedSecretResponse = {
isPasswordProtected: boolean;
secret: {
@@ -38,10 +61,27 @@ export type TViewSharedSecretResponse = {
};
};
export type TDeleteSharedSecretRequest = {
export type TGetSecretRequestByIdResponse = {
secretRequest: {
isSecretValueSet: boolean;
accessType: SecretSharingAccessType;
requester: {
organizationName: string;
username: string;
firstName?: string;
lastName?: string;
};
};
};
export type TDeleteSharedSecretRequestDTO = {
sharedSecretId: string;
};
export type TDeleteSecretRequestDTO = {
secretRequestId: string;
};
export enum SecretSharingAccessType {
Anyone = "anyone",
Organization = "organization"