feat(secret-rotation): implemented frontend ui for secret rotation

This commit is contained in:
Akhil Mohan
2023-11-01 22:21:31 +05:30
parent 82e924baff
commit 97e4338335
24 changed files with 1250 additions and 1 deletions
+1
View File
@@ -12,6 +12,7 @@ export * from "./secretApproval";
export * from "./secretApprovalRequest";
export * from "./secretFolders";
export * from "./secretImports";
export * from "./secretRotation";
export * from "./secrets";
export * from "./secretSnapshots";
export * from "./serviceAccounts";
@@ -0,0 +1,6 @@
export {
useCreateSecretRotation,
useDeleteSecretRotation,
useRestartSecretRotation
} from "./mutation";
export { useGetSecretRotationProviders, useGetSecretRotations } from "./queries";
@@ -0,0 +1,52 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { secretRotationKeys } from "./queries";
import {
TCreateSecretRotationDTO,
TDeleteSecretRotationDTO,
TRestartSecretRotationDTO
} from "./types";
export const useCreateSecretRotation = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TCreateSecretRotationDTO>({
mutationFn: async (dto) => {
const { data } = await apiRequest.post("/api/v1/secret-rotations", dto);
return data;
},
onSuccess: (_, { workspaceId }) => {
queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId }));
}
});
};
export const useDeleteSecretRotation = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TDeleteSecretRotationDTO>({
mutationFn: async (dto) => {
const { data } = await apiRequest.delete(`/api/v1/secret-rotations/${dto.id}`);
return data;
},
onSuccess: (_, { workspaceId }) => {
queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId }));
}
});
};
export const useRestartSecretRotation = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TRestartSecretRotationDTO>({
mutationFn: async (dto) => {
const { data } = await apiRequest.post("/api/v1/secret-rotations/restart", { id: dto.id });
return data;
},
onSuccess: (_, { workspaceId }) => {
queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId }));
}
});
};
@@ -0,0 +1,110 @@
import { useCallback } from "react";
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import {
decryptAssymmetric,
decryptSymmetric
} from "@app/components/utilities/cryptography/crypto";
import { apiRequest } from "@app/config/request";
import {
TGetSecretRotationList,
TGetSecretRotationProviders,
TSecretRotation,
TSecretRotationProviderList
} from "./types";
export const secretRotationKeys = {
listProviders: ({ workspaceId }: TGetSecretRotationProviders) => [
{ workspaceId },
"secret-rotation-providers"
],
list: ({ workspaceId }: Omit<TGetSecretRotationList, "decryptFileKey">) =>
[{ workspaceId }, "secret-rotations"] as const
};
const fetchSecretRotationProviders = async ({ workspaceId }: TGetSecretRotationProviders) => {
const { data } = await apiRequest.get<TSecretRotationProviderList>(
`/api/v1/secret-rotation-providers/${workspaceId}`
);
return data;
};
export const useGetSecretRotationProviders = ({
workspaceId,
options = {}
}: TGetSecretRotationProviders & {
options?: Omit<
UseQueryOptions<
TSecretRotationProviderList,
unknown,
TSecretRotationProviderList,
ReturnType<typeof secretRotationKeys.listProviders>
>,
"queryKey" | "queryFn"
>;
}) =>
useQuery({
...options,
queryKey: secretRotationKeys.listProviders({ workspaceId }),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
queryFn: async () => fetchSecretRotationProviders({ workspaceId })
});
const fetchSecretRotations = async ({
workspaceId
}: Omit<TGetSecretRotationList, "decryptFileKey">) => {
const { data } = await apiRequest.get<{ secretRotations: TSecretRotation[] }>(
"/api/v1/secret-rotations",
{ params: { workspaceId } }
);
return data.secretRotations;
};
export const useGetSecretRotations = ({
workspaceId,
decryptFileKey,
options = {}
}: TGetSecretRotationList & {
options?: Omit<
UseQueryOptions<
TSecretRotation[],
unknown,
TSecretRotation<{ key: string }>[],
ReturnType<typeof secretRotationKeys.list>
>,
"queryKey" | "queryFn"
>;
}) =>
useQuery({
...options,
queryKey: secretRotationKeys.list({ workspaceId }),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
queryFn: async () => fetchSecretRotations({ workspaceId }),
select: useCallback(
(data: TSecretRotation[]) => {
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
const decryptKey = decryptAssymmetric({
ciphertext: decryptFileKey.encryptedKey,
nonce: decryptFileKey.nonce,
publicKey: decryptFileKey.sender.publicKey,
privateKey: PRIVATE_KEY
});
return data.map((el) => ({
...el,
outputs: el.outputs.map(({ key, secret }) => ({
key,
secret: {
key: decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: decryptKey
})
}
}))
}));
},
[decryptFileKey]
)
});
@@ -0,0 +1,133 @@
import { UserWsKeyPair } from "../keys/types";
import { EncryptedSecret } from "../secrets/types";
export enum TProviderFunctionTypes {
HTTP = "http",
DB = "database"
}
export enum TDbProviderClients {
// postgres, cockroack db, amazon red shift
Pg = "pg",
// mysql and maria db
Sql = "sql"
}
export enum TAssignOp {
Direct = "direct",
JmesPath = "jmesopath"
}
export type TJmesPathAssignOp = {
assign: TAssignOp.JmesPath;
path: string;
};
export type TDirectAssignOp = {
assign: TAssignOp.Direct;
value: string;
};
export type TAssignFunction = TJmesPathAssignOp | TDirectAssignOp;
export type THttpProviderFunction = {
type: TProviderFunctionTypes.HTTP;
url: string;
method: string;
header?: Record<string, string>;
query?: Record<string, string>;
body?: Record<string, unknown>;
setter?: Record<string, TAssignFunction>;
pre?: Record<string, TDirectAssignOp>;
};
export type TDbProviderFunction = {
type: TProviderFunctionTypes.DB;
client: TDbProviderClients;
username: string;
password: string;
host: string;
database: string;
port: string;
query: string;
setter?: Record<string, TAssignFunction>;
pre?: Record<string, TDirectAssignOp>;
};
export type TProviderFunction = THttpProviderFunction | TDbProviderFunction;
export type TProviderTemplate = {
inputs: {
properties: Record<string, { type: string; helperText?: string; defaultValue?: string }>;
type: "object";
required: string[];
};
outputs: Record<string, unknown>;
functions: {
set: TProviderFunction;
remove?: TProviderFunction;
test: TProviderFunction;
};
};
export type TSecretRotation<T extends unknown = EncryptedSecret> = {
_id: string;
interval: number;
provider: string;
customProvider: string;
workspace: string;
environment: string;
secretPath: string;
outputs: Array<{
key: string;
secret: T;
}>;
status?: "success" | "failed";
lastRotatedAt?: string;
statusMessage?: string;
algorithm: string;
keyEncoding: string;
};
export type TSecretRotationProvider = {
name: string;
image: string;
title: string;
description: string;
template: TProviderTemplate;
};
export type TSecretRotationProviderList = {
custom: TSecretRotationProvider[];
providers: TSecretRotationProvider[];
};
export type TGetSecretRotationProviders = {
workspaceId: string;
};
export type TGetSecretRotationList = {
workspaceId: string;
decryptFileKey: UserWsKeyPair;
};
export type TCreateSecretRotationDTO = {
workspaceId: string;
secretPath: string;
environment: string;
interval: number;
provider: string;
customProvider?: string;
inputs: Record<string, unknown>;
outputs: Record<string, string>;
};
export type TDeleteSecretRotationDTO = {
id: string;
workspaceId: string;
};
export type TRestartSecretRotationDTO = {
id: string;
workspaceId: string;
};
@@ -12,6 +12,7 @@ export type SubscriptionPlan = {
secretVersioning: boolean;
slug: string;
secretApproval: string;
secretRotation: string;
tier: number;
workspaceLimit: number;
workspacesUsed: number;
+6
View File
@@ -13,6 +13,12 @@ export type {
export { ApprovalStatus, CommitType } from "./secretApprovalRequest/types";
export type { TSecretFolder } from "./secretFolders/types";
export type { TImportedSecrets, TSecretImports } from "./secretImports/types";
export type {
TGetSecretRotationProviders,
TProviderTemplate,
TSecretRotationProvider,
TSecretRotationProviderList
} from "./secretRotation/types";
export * from "./secrets/types";
export type { CreateServiceTokenDTO, ServiceToken } from "./serviceTokens/types";
export type { SubscriptionPlan } from "./subscriptions/types";