mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 04:14:38 +00:00
feat(secret-approval): implemented the base
This commit is contained in:
@@ -8,6 +8,7 @@ export * from "./keys";
|
||||
export * from "./organization";
|
||||
export * from "./roles";
|
||||
export * from "./secretApproval";
|
||||
export * from "./secretApprovalRequest";
|
||||
export * from "./secretFolders";
|
||||
export * from "./secretImports";
|
||||
export * from "./secrets";
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export { useUpdateSecretApprovalRequestStatus } from "./mutation";
|
||||
export { useGetSecretApprovalRequestDetails, useGetSecretApprovalRequests } from "./queries";
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { secretApprovalRequestKeys } from "./queries";
|
||||
import { TUpdateSecretApprovalRequestStatusDTO } from "./types";
|
||||
|
||||
export const useUpdateSecretApprovalRequestStatus = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<{}, {}, TUpdateSecretApprovalRequestStatusDTO>({
|
||||
mutationFn: async ({ id, status }) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}`, { status });
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries(secretApprovalRequestKeys.detail({ id }));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { UserWsKeyPair } from "../keys/types";
|
||||
import { decryptSecrets } from "../secrets/queries";
|
||||
import { DecryptedSecret } from "../secrets/types";
|
||||
import {
|
||||
TGetSecretApprovalRequestDetails,
|
||||
TGetSecretApprovalRequestList,
|
||||
TSecretApprovalRequest,
|
||||
TSecretApprovalSecChange,
|
||||
TSecretApprovalSecChangeData
|
||||
} from "./types";
|
||||
|
||||
export const secretApprovalRequestKeys = {
|
||||
list: ({ workspaceId, environment }: TGetSecretApprovalRequestList) =>
|
||||
[{ workspaceId, environment }, "secret-approval-requests"] as const,
|
||||
detail: ({ id }: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) =>
|
||||
[{ id }, "secret-approval-request-detail"] as const
|
||||
};
|
||||
|
||||
export const decryptSecretApprovalSecret = (
|
||||
encSecret: TSecretApprovalSecChangeData,
|
||||
decryptFileKey: UserWsKeyPair
|
||||
) => {
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: decryptFileKey.encryptedKey,
|
||||
nonce: decryptFileKey.nonce,
|
||||
publicKey: decryptFileKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const secretKey = decryptSymmetric({
|
||||
ciphertext: encSecret.secretKeyCiphertext,
|
||||
iv: encSecret.secretKeyIV,
|
||||
tag: encSecret.secretKeyTag,
|
||||
key
|
||||
});
|
||||
|
||||
const secretValue = decryptSymmetric({
|
||||
ciphertext: encSecret.secretValueCiphertext,
|
||||
iv: encSecret.secretValueIV,
|
||||
tag: encSecret.secretValueTag,
|
||||
key
|
||||
});
|
||||
|
||||
const secretComment = decryptSymmetric({
|
||||
ciphertext: encSecret.secretCommentCiphertext,
|
||||
iv: encSecret.secretCommentIV,
|
||||
tag: encSecret.secretCommentTag,
|
||||
key
|
||||
});
|
||||
return {
|
||||
_id: encSecret._id,
|
||||
version: encSecret.version,
|
||||
secretKey,
|
||||
secretValue,
|
||||
secretComment,
|
||||
tags: encSecret.tags
|
||||
};
|
||||
};
|
||||
|
||||
const fetchSecretApprovalRequestList = async ({
|
||||
workspaceId,
|
||||
environment
|
||||
}: TGetSecretApprovalRequestList) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequest[] }>(
|
||||
"/api/v1/secret-approval-requests",
|
||||
{
|
||||
params: {
|
||||
workspaceId,
|
||||
environment
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return data.approvals;
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalRequests = ({
|
||||
workspaceId,
|
||||
environment,
|
||||
options = {}
|
||||
}: TGetSecretApprovalRequestList & {
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TSecretApprovalRequest[],
|
||||
unknown,
|
||||
TSecretApprovalRequest[],
|
||||
ReturnType<typeof secretApprovalRequestKeys.list>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>;
|
||||
}) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalRequestKeys.list({ workspaceId, environment }),
|
||||
queryFn: () => fetchSecretApprovalRequestList({ workspaceId, environment }),
|
||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
||||
});
|
||||
|
||||
const fetchSecretApprovalRequestDetails = async ({
|
||||
id
|
||||
}: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) => {
|
||||
const { data } = await apiRequest.get<{ approval: TSecretApprovalRequest }>(
|
||||
`/api/v1/secret-approval-requests/${id}`
|
||||
);
|
||||
|
||||
return data.approval;
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalRequestDetails = ({
|
||||
id,
|
||||
decryptKey,
|
||||
options = {}
|
||||
}: TGetSecretApprovalRequestDetails & {
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TSecretApprovalRequest,
|
||||
unknown,
|
||||
TSecretApprovalRequest<TSecretApprovalSecChange, DecryptedSecret>,
|
||||
ReturnType<typeof secretApprovalRequestKeys.detail>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>;
|
||||
}) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalRequestKeys.detail({ id }),
|
||||
queryFn: () => fetchSecretApprovalRequestDetails({ id }),
|
||||
select: (data) => ({
|
||||
...data,
|
||||
commits: data.commits.map(({ secret, op, newVersion }) => ({
|
||||
op,
|
||||
secret: secret ? decryptSecrets([secret], decryptKey)[0] : undefined,
|
||||
newVersion: newVersion ? decryptSecretApprovalSecret(newVersion, decryptKey) : undefined
|
||||
}))
|
||||
}),
|
||||
enabled: Boolean(id && decryptKey) && (options?.enabled ?? true)
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import { UserWsKeyPair } from "../keys/types";
|
||||
import { TSecretApprovalPolicy } from "../secretApproval/types";
|
||||
import { EncryptedSecret } from "../secrets/types";
|
||||
import { WsTag } from "../tags/types";
|
||||
|
||||
export enum ApprovalStatus {
|
||||
PENDING = "pending",
|
||||
APPROVED = "approved",
|
||||
REJECTED = "rejected"
|
||||
}
|
||||
|
||||
export enum CommitType {
|
||||
DELETE = "delete",
|
||||
UPDATE = "update",
|
||||
CREATE = "create"
|
||||
}
|
||||
|
||||
export type TSecretApprovalSecChangeData = {
|
||||
_id: string;
|
||||
version: number;
|
||||
secretKeyCiphertext: string;
|
||||
secretKeyIV: string;
|
||||
secretKeyTag: string;
|
||||
secretValueCiphertext: string;
|
||||
secretValueIV: string;
|
||||
secretValueTag: string;
|
||||
secretCommentIV: string;
|
||||
secretCommentTag: string;
|
||||
secretCommentCiphertext: string;
|
||||
skipMultilineEncoding?: boolean;
|
||||
algorithm: "aes-256-gcm";
|
||||
keyEncoding: "utf8" | "base64";
|
||||
tags?: WsTag[];
|
||||
};
|
||||
|
||||
export type TSecretApprovalSecChange = {
|
||||
_id: string;
|
||||
version: number;
|
||||
secretKey: string;
|
||||
secretValue: string;
|
||||
secretComment: string;
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
export type TSecretApprovalRequest<
|
||||
T extends unknown = TSecretApprovalSecChangeData,
|
||||
J extends unknown = EncryptedSecret
|
||||
> = {
|
||||
_id: string;
|
||||
committer: string;
|
||||
reviewers: {
|
||||
member: string;
|
||||
status: ApprovalStatus;
|
||||
}[];
|
||||
workspace: string;
|
||||
environment: string;
|
||||
folderId: string;
|
||||
hasMerged: boolean;
|
||||
status: "open" | "close";
|
||||
policy: TSecretApprovalPolicy;
|
||||
commits: {
|
||||
// if there is no secret means it was creation
|
||||
secret?: J;
|
||||
// if there is no new version its for Delete
|
||||
newVersion?: T;
|
||||
op: CommitType;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalRequestList = {
|
||||
workspaceId: string;
|
||||
environment?: string;
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalRequestDetails = {
|
||||
id: string;
|
||||
decryptKey: UserWsKeyPair;
|
||||
};
|
||||
|
||||
export type TUpdateSecretApprovalRequestStatusDTO = {
|
||||
status: ApprovalStatus;
|
||||
id: string;
|
||||
};
|
||||
@@ -26,7 +26,10 @@ export const secretKeys = {
|
||||
getSecretVersion: (secretId: string) => [{ secretId }, "secret-versions"] as const
|
||||
};
|
||||
|
||||
const decryptSecrets = (encryptedSecrets: EncryptedSecret[], decryptFileKey: UserWsKeyPair) => {
|
||||
export const decryptSecrets = (
|
||||
encryptedSecrets: EncryptedSecret[],
|
||||
decryptFileKey: UserWsKeyPair
|
||||
) => {
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: decryptFileKey.encryptedKey,
|
||||
|
||||
@@ -5,13 +5,19 @@ export type { TCloudIntegration, TIntegration } from "./integrations/types";
|
||||
export type { UserWsKeyPair } from "./keys/types";
|
||||
export type { Organization } from "./organization/types";
|
||||
export type { TSecretApprovalPolicy } from "./secretApproval/types";
|
||||
export type {
|
||||
TGetSecretApprovalRequestDetails,
|
||||
TSecretApprovalRequest,
|
||||
TSecretApprovalSecChange
|
||||
} from "./secretApprovalRequest/types";
|
||||
export { ApprovalStatus, CommitType } from "./secretApprovalRequest/types";
|
||||
export type { TSecretFolder } from "./secretFolders/types";
|
||||
export type { TImportedSecrets, TSecretImports } from "./secretImports/types";
|
||||
export * from "./secrets/types";
|
||||
export type { CreateServiceTokenDTO, ServiceToken } from "./serviceTokens/types";
|
||||
export type { SubscriptionPlan } from "./subscriptions/types";
|
||||
export type { WsTag } from "./tags/types";
|
||||
export type { AddUserToWsDTO, AddUserToWsRes, OrgUser, User } from "./users/types";
|
||||
export type { AddUserToWsDTO, AddUserToWsRes, OrgUser, TWorkspaceUser, User } from "./users/types";
|
||||
export type { TWebhook } from "./webhooks/types";
|
||||
export type {
|
||||
CreateEnvironmentDTO,
|
||||
|
||||
Reference in New Issue
Block a user