mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 11:38:55 +00:00
feature secret rotations v2
This commit is contained in:
@@ -5,5 +5,7 @@ export enum AppConnection {
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
Databricks = "databricks",
|
||||
Humanitec = "humanitec"
|
||||
Humanitec = "humanitec",
|
||||
Postgres = "postgres",
|
||||
MsSql = "mssql"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
export type TAppConnectionOptionBase = {
|
||||
name: string;
|
||||
methods: string[];
|
||||
supportsPlatformManagement?: boolean;
|
||||
};
|
||||
|
||||
export type TAwsConnectionOption = TAppConnectionOptionBase & {
|
||||
@@ -38,6 +39,14 @@ export type THumanitecConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.Humanitec;
|
||||
};
|
||||
|
||||
export type TPostgresConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.Postgres;
|
||||
};
|
||||
|
||||
export type TMsSqlConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.MsSql;
|
||||
};
|
||||
|
||||
export type TAppConnectionOption =
|
||||
| TAwsConnectionOption
|
||||
| TGitHubConnectionOption
|
||||
@@ -45,7 +54,9 @@ export type TAppConnectionOption =
|
||||
| TAzureAppConfigurationConnectionOption
|
||||
| TAzureKeyVaultConnectionOption
|
||||
| TDatabricksConnectionOption
|
||||
| THumanitecConnectionOption;
|
||||
| THumanitecConnectionOption
|
||||
| TPostgresConnectionOption
|
||||
| TMsSqlConnectionOption;
|
||||
|
||||
export type TAppConnectionOptionMap = {
|
||||
[AppConnection.AWS]: TAwsConnectionOption;
|
||||
@@ -55,4 +66,6 @@ export type TAppConnectionOptionMap = {
|
||||
[AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnectionOption;
|
||||
[AppConnection.Databricks]: TDatabricksConnectionOption;
|
||||
[AppConnection.Humanitec]: THumanitecConnectionOption;
|
||||
[AppConnection.Postgres]: TPostgresConnectionOption;
|
||||
[AppConnection.MsSql]: TMsSqlConnectionOption;
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@ import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||
|
||||
export enum HumanitecConnectionMethod {
|
||||
API_TOKEN = "api-token"
|
||||
ApiToken = "api-token"
|
||||
}
|
||||
|
||||
export type THumanitecConnection = TRootAppConnection & { app: AppConnection.Humanitec } & {
|
||||
method: HumanitecConnectionMethod.API_TOKEN;
|
||||
method: HumanitecConnectionMethod.ApiToken;
|
||||
credentials: {
|
||||
apiToken: string;
|
||||
};
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TAppConnectionOption } from "@app/hooks/api/appConnections/types/app-options";
|
||||
import { TAwsConnection } from "@app/hooks/api/appConnections/types/aws-connection";
|
||||
import { TDatabricksConnection } from "@app/hooks/api/appConnections/types/databricks-connection";
|
||||
import { TGitHubConnection } from "@app/hooks/api/appConnections/types/github-connection";
|
||||
import { THumanitecConnection } from "@app/hooks/api/appConnections/types/humanitec-connection";
|
||||
|
||||
import { AppConnection } from "../enums";
|
||||
import { TAppConnectionOption } from "./app-options";
|
||||
import { TAwsConnection } from "./aws-connection";
|
||||
import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection";
|
||||
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
|
||||
import { TDatabricksConnection } from "./databricks-connection";
|
||||
import { TGcpConnection } from "./gcp-connection";
|
||||
import { TGitHubConnection } from "./github-connection";
|
||||
import { THumanitecConnection } from "./humanitec-connection";
|
||||
import { TMsSqlConnection } from "./mssql-connection";
|
||||
import { TPostgresConnection } from "./postgres-connection";
|
||||
|
||||
export * from "./aws-connection";
|
||||
export * from "./azure-app-configuration-connection";
|
||||
export * from "./azure-key-vault-connection";
|
||||
export * from "./databricks-connection";
|
||||
export * from "./gcp-connection";
|
||||
export * from "./github-connection";
|
||||
export * from "./humanitec-connection";
|
||||
export * from "./mssql-connection";
|
||||
export * from "./postgres-connection";
|
||||
|
||||
export type TAppConnection =
|
||||
| TAwsConnection
|
||||
@@ -23,7 +27,9 @@ export type TAppConnection =
|
||||
| TAzureKeyVaultConnection
|
||||
| TAzureAppConfigurationConnection
|
||||
| TDatabricksConnection
|
||||
| THumanitecConnection;
|
||||
| THumanitecConnection
|
||||
| TPostgresConnection
|
||||
| TMsSqlConnection;
|
||||
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||
|
||||
@@ -35,11 +41,11 @@ export type TAvailableAppConnectionsResponse = { appConnections: TAvailableAppCo
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<
|
||||
TAppConnection,
|
||||
"name" | "credentials" | "method" | "app" | "description"
|
||||
"name" | "credentials" | "method" | "app" | "description" | "isPlatformManagedCredentials"
|
||||
>;
|
||||
|
||||
export type TUpdateAppConnectionDTO = Partial<
|
||||
Pick<TAppConnection, "name" | "credentials" | "description">
|
||||
Pick<TAppConnection, "name" | "credentials" | "description" | "isPlatformManagedCredentials">
|
||||
> & {
|
||||
connectionId: string;
|
||||
app: AppConnection;
|
||||
@@ -58,4 +64,6 @@ export type TAppConnectionMap = {
|
||||
[AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnection;
|
||||
[AppConnection.Databricks]: TDatabricksConnection;
|
||||
[AppConnection.Humanitec]: THumanitecConnection;
|
||||
[AppConnection.Postgres]: TPostgresConnection;
|
||||
[AppConnection.MsSql]: TMsSqlConnection;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||
|
||||
import { TBaseSqlConnectionCredentials } from "./shared";
|
||||
|
||||
export enum MsSqlConnectionMethod {
|
||||
UsernameAndPassword = "username-and-password"
|
||||
}
|
||||
|
||||
export type TMsSqlConnection = TRootAppConnection & { app: AppConnection.MsSql } & {
|
||||
method: MsSqlConnectionMethod.UsernameAndPassword;
|
||||
credentials: TBaseSqlConnectionCredentials;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||
|
||||
import { TBaseSqlConnectionCredentials } from "./shared";
|
||||
|
||||
export enum PostgresConnectionMethod {
|
||||
UsernameAndPassword = "username-and-password"
|
||||
}
|
||||
|
||||
export type TPostgresConnection = TRootAppConnection & { app: AppConnection.Postgres } & {
|
||||
method: PostgresConnectionMethod.UsernameAndPassword;
|
||||
credentials: TBaseSqlConnectionCredentials;
|
||||
};
|
||||
@@ -6,4 +6,5 @@ export type TRootAppConnection = {
|
||||
orgId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
isPlatformManagedCredentials?: boolean;
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./sql-connection";
|
||||
@@ -0,0 +1,7 @@
|
||||
export type TBaseSqlConnectionCredentials = {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
database: string;
|
||||
};
|
||||
@@ -110,7 +110,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.CREATE_APP_CONNECTION]: "Create App Connection",
|
||||
[EventType.UPDATE_APP_CONNECTION]: "Update App Connection",
|
||||
[EventType.DELETE_APP_CONNECTION]: "Delete App Connection",
|
||||
[EventType.GET_SECRET_SYNCS]: "List Secret Syncs",
|
||||
[EventType.GET_SECRET_SYNCS]: "List secret syncs",
|
||||
[EventType.GET_SECRET_SYNC]: "Get Secret Sync",
|
||||
[EventType.CREATE_SECRET_SYNC]: "Create Secret Sync",
|
||||
[EventType.UPDATE_SECRET_SYNC]: "Update Secret Sync",
|
||||
@@ -139,7 +139,15 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.KMIP_OPERATION_ACTIVATE]: "KMIP operation activate",
|
||||
[EventType.KMIP_OPERATION_REVOKE]: "KMIP operation revoke",
|
||||
[EventType.KMIP_OPERATION_LOCATE]: "KMIP operation locate",
|
||||
[EventType.KMIP_OPERATION_REGISTER]: "KMIP operation register"
|
||||
[EventType.KMIP_OPERATION_REGISTER]: "KMIP operation register",
|
||||
[EventType.GET_SECRET_ROTATIONS]: "List Secret Rotations",
|
||||
[EventType.GET_SECRET_ROTATION]: "Get Secret Rotation",
|
||||
[EventType.GET_SECRET_ROTATION_GENERATED_CREDENTIALS]:
|
||||
"Get Secret Rotation generated credentials",
|
||||
[EventType.CREATE_SECRET_ROTATION]: "Create Secret Rotation",
|
||||
[EventType.UPDATE_SECRET_ROTATION]: "Update Secret Rotation",
|
||||
[EventType.DELETE_SECRET_ROTATION]: "Delete Secret Rotation",
|
||||
[EventType.SECRET_ROTATION_ROTATE_SECRETS]: "Secret Rotation secrets rotated"
|
||||
};
|
||||
|
||||
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
|
||||
|
||||
@@ -151,5 +151,12 @@ export enum EventType {
|
||||
KMIP_OPERATION_REVOKE = "kmip-operation-revoke",
|
||||
KMIP_OPERATION_LOCATE = "kmip-operation-locate",
|
||||
KMIP_OPERATION_REGISTER = "kmip-operation-register",
|
||||
SECRET_APPROVAL_REQUEST_REVIEW = "secret-approval-request-review"
|
||||
SECRET_APPROVAL_REQUEST_REVIEW = "secret-approval-request-review",
|
||||
GET_SECRET_ROTATIONS = "get-secret-rotations",
|
||||
GET_SECRET_ROTATION = "get-secret-rotation",
|
||||
GET_SECRET_ROTATION_GENERATED_CREDENTIALS = "get-secret-rotation-generated-credentials",
|
||||
CREATE_SECRET_ROTATION = "create-secret-rotation",
|
||||
UPDATE_SECRET_ROTATION = "update-secret-rotation",
|
||||
DELETE_SECRET_ROTATION = "delete-secret-rotation",
|
||||
SECRET_ROTATION_ROTATE_SECRETS = "secret-rotation-rotate-secrets"
|
||||
}
|
||||
|
||||
@@ -132,6 +132,21 @@ export const fetchDashboardProjectSecretsByKeys = async ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const mergePersonalRotationSecrets = (secrets: (SecretV3Raw | null)[]) => {
|
||||
const actualSecrets: SecretV3Raw[] = [];
|
||||
const dummySecrets: null[] = [];
|
||||
|
||||
secrets.forEach((secret) => {
|
||||
if (secret !== null) {
|
||||
actualSecrets.push(secret);
|
||||
} else {
|
||||
dummySecrets.push(secret);
|
||||
}
|
||||
});
|
||||
|
||||
return [...mergePersonalSecrets(actualSecrets), ...dummySecrets];
|
||||
};
|
||||
|
||||
export const useGetProjectSecretsOverview = (
|
||||
{
|
||||
projectId,
|
||||
@@ -145,6 +160,7 @@ export const useGetProjectSecretsOverview = (
|
||||
includeFolders,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
environments
|
||||
}: TGetDashboardProjectSecretsOverviewDTO,
|
||||
options?: Omit<
|
||||
@@ -173,6 +189,7 @@ export const useGetProjectSecretsOverview = (
|
||||
includeFolders,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
environments
|
||||
}),
|
||||
queryFn: () =>
|
||||
@@ -188,10 +205,11 @@ export const useGetProjectSecretsOverview = (
|
||||
includeFolders,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
environments
|
||||
}),
|
||||
select: useCallback((data: Awaited<ReturnType<typeof fetchProjectSecretsOverview>>) => {
|
||||
const { secrets, ...select } = data;
|
||||
const { secrets, secretRotations, ...select } = data;
|
||||
const uniqueSecrets = secrets ? unique(secrets, (i) => i.secretKey) : [];
|
||||
|
||||
const uniqueFolders = select.folders ? unique(select.folders, (i) => i.name) : [];
|
||||
@@ -201,14 +219,22 @@ export const useGetProjectSecretsOverview = (
|
||||
: [];
|
||||
|
||||
const uniqueSecretImports = select.imports ? unique(select.imports, (i) => i.id) : [];
|
||||
const uniqueSecretRotations = secretRotations ? unique(secretRotations, (i) => i.name) : [];
|
||||
|
||||
return {
|
||||
...select,
|
||||
secrets: secrets ? mergePersonalSecrets(secrets) : undefined,
|
||||
secretRotations: secretRotations?.map((rotation) => {
|
||||
return {
|
||||
...rotation,
|
||||
secrets: mergePersonalRotationSecrets(rotation.secrets)
|
||||
};
|
||||
}),
|
||||
totalUniqueSecretsInPage: uniqueSecrets.length,
|
||||
totalUniqueDynamicSecretsInPage: uniqueDynamicSecrets.length,
|
||||
totalUniqueFoldersInPage: uniqueFolders.length,
|
||||
totalUniqueSecretImportsInPage: uniqueSecretImports.length
|
||||
totalUniqueSecretImportsInPage: uniqueSecretImports.length,
|
||||
totalUniqueSecretRotationsInPage: uniqueSecretRotations.length
|
||||
};
|
||||
}, []),
|
||||
placeholderData: (previousData) => previousData
|
||||
@@ -230,6 +256,7 @@ export const useGetProjectSecretsDetails = (
|
||||
viewSecretValue,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
tags
|
||||
}: TGetDashboardProjectSecretsDetailsDTO,
|
||||
options?: Omit<
|
||||
@@ -260,6 +287,7 @@ export const useGetProjectSecretsDetails = (
|
||||
includeFolders,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
tags
|
||||
}),
|
||||
queryFn: () =>
|
||||
@@ -277,12 +305,17 @@ export const useGetProjectSecretsDetails = (
|
||||
includeFolders,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
tags
|
||||
}),
|
||||
select: useCallback(
|
||||
(data: Awaited<ReturnType<typeof fetchProjectSecretsDetails>>) => ({
|
||||
...data,
|
||||
secrets: data.secrets ? mergePersonalSecrets(data.secrets) : undefined
|
||||
secrets: data.secrets ? mergePersonalSecrets(data.secrets) : undefined,
|
||||
secretRotations: data.secretRotations?.map((rotation) => ({
|
||||
...rotation,
|
||||
secrets: mergePersonalRotationSecrets(rotation.secrets)
|
||||
}))
|
||||
}),
|
||||
[]
|
||||
),
|
||||
@@ -371,7 +404,7 @@ export const useGetProjectSecretsQuickSearch = (
|
||||
tags
|
||||
}),
|
||||
select: useCallback((data: Awaited<ReturnType<typeof fetchProjectSecretsQuickSearch>>) => {
|
||||
const { secrets, folders, dynamicSecrets } = data;
|
||||
const { secrets, folders, dynamicSecrets, secretRotations } = data;
|
||||
|
||||
const groupedFolders = groupBy(folders, (folder) => folder.path);
|
||||
const groupedSecrets = groupBy(
|
||||
@@ -383,11 +416,16 @@ export const useGetProjectSecretsQuickSearch = (
|
||||
(dynamicSecret) =>
|
||||
`${dynamicSecret.path === "/" ? "" : dynamicSecret.path}/${dynamicSecret.name}`
|
||||
);
|
||||
const groupedRotations = groupBy(
|
||||
secretRotations,
|
||||
(rotation) => `${rotation.folder.path === "/" ? "" : rotation.folder.path}/${rotation.name}`
|
||||
);
|
||||
|
||||
return {
|
||||
folders: groupedFolders,
|
||||
secrets: groupedSecrets,
|
||||
dynamicSecrets: groupedDynamicSecrets
|
||||
dynamicSecrets: groupedDynamicSecrets,
|
||||
secretRotations: groupedRotations
|
||||
};
|
||||
}, []),
|
||||
placeholderData: (previousData) => previousData
|
||||
|
||||
@@ -3,6 +3,7 @@ import { TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { TSecretFolder } from "@app/hooks/api/secretFolders/types";
|
||||
import { TSecretImport } from "@app/hooks/api/secretImports/types";
|
||||
import { TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
import { SecretV3Raw, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
||||
|
||||
export type DashboardProjectSecretsOverviewResponse = {
|
||||
@@ -14,11 +15,16 @@ export type DashboardProjectSecretsOverviewResponse = {
|
||||
totalFolderCount?: number;
|
||||
totalDynamicSecretCount?: number;
|
||||
totalImportCount?: number;
|
||||
secretRotations?: (TSecretRotationV2 & {
|
||||
secrets: (SecretV3Raw | null)[];
|
||||
})[];
|
||||
totalSecretRotationCount?: number;
|
||||
totalCount: number;
|
||||
totalUniqueSecretsInPage: number;
|
||||
totalUniqueDynamicSecretsInPage: number;
|
||||
totalUniqueFoldersInPage: number;
|
||||
totalUniqueSecretImportsInPage: number;
|
||||
totalUniqueSecretRotationsInPage: number;
|
||||
};
|
||||
|
||||
export type DashboardProjectSecretsDetailsResponse = {
|
||||
@@ -26,10 +32,14 @@ export type DashboardProjectSecretsDetailsResponse = {
|
||||
folders?: TSecretFolder[];
|
||||
dynamicSecrets?: TDynamicSecret[];
|
||||
secrets?: SecretV3Raw[];
|
||||
secretRotations?: (TSecretRotationV2 & {
|
||||
secrets: (SecretV3Raw | null)[];
|
||||
})[];
|
||||
totalImportCount?: number;
|
||||
totalFolderCount?: number;
|
||||
totalDynamicSecretCount?: number;
|
||||
totalSecretCount?: number;
|
||||
totalSecretRotationCount?: number;
|
||||
totalCount: number;
|
||||
};
|
||||
|
||||
@@ -39,9 +49,12 @@ export type DashboardProjectSecretsByKeys = {
|
||||
|
||||
export type DashboardProjectSecretsOverview = Omit<
|
||||
DashboardProjectSecretsOverviewResponse,
|
||||
"secrets"
|
||||
"secrets" | "secretRotations"
|
||||
> & {
|
||||
secrets?: SecretV3RawSanitized[];
|
||||
secretRotations?: (TSecretRotationV2 & {
|
||||
secrets: (SecretV3RawSanitized | null)[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export type DashboardProjectSecretsDetails = Omit<
|
||||
@@ -49,6 +62,9 @@ export type DashboardProjectSecretsDetails = Omit<
|
||||
"secrets"
|
||||
> & {
|
||||
secrets?: SecretV3RawSanitized[];
|
||||
secretRotations?: (TSecretRotationV2 & {
|
||||
secrets: (SecretV3RawSanitized | null)[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export enum DashboardSecretsOrderBy {
|
||||
@@ -67,6 +83,7 @@ export type TGetDashboardProjectSecretsOverviewDTO = {
|
||||
includeFolders?: boolean;
|
||||
includeDynamicSecrets?: boolean;
|
||||
includeImports?: boolean;
|
||||
includeSecretRotations?: boolean;
|
||||
environments: string[];
|
||||
};
|
||||
|
||||
@@ -83,6 +100,7 @@ export type TGetDashboardProjectSecretsDetailsDTO = Omit<
|
||||
export type TDashboardProjectSecretsQuickSearchResponse = {
|
||||
folders: (TSecretFolder & { envId: string; path: string })[];
|
||||
dynamicSecrets: (TDynamicSecret & { environment: string; path: string })[];
|
||||
secretRotations: TSecretRotationV2[];
|
||||
secrets: SecretV3Raw[];
|
||||
};
|
||||
|
||||
@@ -90,6 +108,7 @@ export type TDashboardProjectSecretsQuickSearch = {
|
||||
folders: Record<string, TDashboardProjectSecretsQuickSearchResponse["folders"]>;
|
||||
secrets: Record<string, SecretV3RawSanitized[]>;
|
||||
dynamicSecrets: Record<string, TDashboardProjectSecretsQuickSearchResponse["dynamicSecrets"]>;
|
||||
secretRotations: Record<string, TDashboardProjectSecretsQuickSearchResponse["secretRotations"]>;
|
||||
};
|
||||
|
||||
export type TGetDashboardProjectSecretsQuickSearchDTO = {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export enum SecretRotation {
|
||||
PostgresCredentials = "postgres-credentials",
|
||||
MsSqlCredentials = "mssql-credentials"
|
||||
}
|
||||
|
||||
export enum SecretRotationStatus {
|
||||
Success = "success",
|
||||
Failed = "failed"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./enums";
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,91 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
|
||||
import {
|
||||
TCreateSecretRotationV2DTO,
|
||||
TDeleteSecretRotationV2DTO,
|
||||
TRotateSecretRotationV2DTO,
|
||||
TSecretRotationV2Response,
|
||||
TUpdateSecretRotationV2DTO
|
||||
} from "@app/hooks/api/secretRotationsV2/types";
|
||||
|
||||
export const useCreateSecretRotationV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ type, ...params }: TCreateSecretRotationV2DTO) => {
|
||||
const { data } = await apiRequest.post<TSecretRotationV2Response>(
|
||||
`/api/v2/secret-rotations/${type}`,
|
||||
params
|
||||
);
|
||||
|
||||
return data.secretRotation;
|
||||
},
|
||||
onSuccess: (_, { projectId, secretPath }) =>
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateSecretRotationV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ type, rotationId, ...params }: TUpdateSecretRotationV2DTO) => {
|
||||
const { data } = await apiRequest.patch<TSecretRotationV2Response>(
|
||||
`/api/v2/secret-rotations/${type}/${rotationId}`,
|
||||
params
|
||||
);
|
||||
|
||||
return data.secretRotation;
|
||||
},
|
||||
onSuccess: (_, { projectId, secretPath }) =>
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export const useRotateSecretRotationV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ type, rotationId }: TRotateSecretRotationV2DTO) => {
|
||||
const { data } = await apiRequest.post<TSecretRotationV2Response>(
|
||||
`/api/v2/secret-rotations/${type}/${rotationId}/rotate-secrets`
|
||||
);
|
||||
|
||||
return data.secretRotation;
|
||||
},
|
||||
onSuccess: (_, { projectId, secretPath }) =>
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
|
||||
}),
|
||||
onError: (_, { projectId, secretPath }) =>
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteSecretRotationV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
type,
|
||||
rotationId,
|
||||
deleteSecrets,
|
||||
revokeGeneratedCredentials
|
||||
}: TDeleteSecretRotationV2DTO) => {
|
||||
const { data } = await apiRequest.delete<TSecretRotationV2Response>(
|
||||
`/api/v2/secret-rotations/${type}/${rotationId}`,
|
||||
{ params: { deleteSecrets, revokeGeneratedCredentials } }
|
||||
);
|
||||
|
||||
return data.secretRotation;
|
||||
},
|
||||
onSuccess: (_, { projectId, secretPath }) =>
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
|
||||
})
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2/enums";
|
||||
import {
|
||||
TListSecretRotationV2Options,
|
||||
TSecretRotationV2Option,
|
||||
TViewSecretRotationGeneratedCredentialsResponse,
|
||||
TViewSecretRotationV2GeneratedCredentialsDTO
|
||||
} from "@app/hooks/api/secretRotationsV2/types";
|
||||
|
||||
export const secretRotationV2Keys = {
|
||||
all: ["secret-rotations-v2"] as const,
|
||||
options: () => [...secretRotationV2Keys.all, "options"] as const,
|
||||
viewGeneratedCredentials: ({ type, rotationId }: TViewSecretRotationV2GeneratedCredentialsDTO) =>
|
||||
[...secretRotationV2Keys.all, type, rotationId] as const
|
||||
};
|
||||
|
||||
export const useSecretRotationV2Options = (
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TSecretRotationV2Option[],
|
||||
unknown,
|
||||
TSecretRotationV2Option[],
|
||||
ReturnType<typeof secretRotationV2Keys.options>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: secretRotationV2Keys.options(),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TListSecretRotationV2Options>(
|
||||
"/api/v2/secret-rotations/options"
|
||||
);
|
||||
|
||||
return data.secretRotationOptions;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useSecretRotationV2Option = (type: SecretRotation) => {
|
||||
const { data: rotationOptions, isPending } = useSecretRotationV2Options();
|
||||
const rotationOption = rotationOptions?.find((option) => option.type === type);
|
||||
|
||||
return { rotationOption, isPending };
|
||||
};
|
||||
|
||||
export const useViewSecretRotationV2GeneratedCredentials = (
|
||||
{ rotationId, type }: TViewSecretRotationV2GeneratedCredentialsDTO,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TViewSecretRotationGeneratedCredentialsResponse,
|
||||
unknown,
|
||||
TViewSecretRotationGeneratedCredentialsResponse,
|
||||
ReturnType<typeof secretRotationV2Keys.viewGeneratedCredentials>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: secretRotationV2Keys.viewGeneratedCredentials({ rotationId, type }),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TViewSecretRotationGeneratedCredentialsResponse>(
|
||||
`/api/v2/secret-rotations/${type}/${rotationId}/generated-credentials`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
import {
|
||||
TMsSqlCredentialsRotation,
|
||||
TMsSqlCredentialsRotationGeneratedCredentialsResponse
|
||||
} from "@app/hooks/api/secretRotationsV2/types/mssql-credentials-rotation";
|
||||
import {
|
||||
TPostgresCredentialsRotation,
|
||||
TPostgresCredentialsRotationGeneratedCredentialsResponse
|
||||
} from "@app/hooks/api/secretRotationsV2/types/postgres-credentials-rotation";
|
||||
import { TSqlOptionTemplate } from "@app/hooks/api/secretRotationsV2/types/shared";
|
||||
import { SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
||||
import { DiscriminativePick } from "@app/types";
|
||||
|
||||
export type TSecretRotationV2 = (TPostgresCredentialsRotation | TMsSqlCredentialsRotation) & {
|
||||
secrets: (SecretV3RawSanitized | null)[];
|
||||
};
|
||||
|
||||
export type TSecretRotationV2Option = {
|
||||
name: string;
|
||||
type: SecretRotation;
|
||||
connection: AppConnection;
|
||||
template: TSqlOptionTemplate;
|
||||
};
|
||||
|
||||
export type TListSecretRotationV2Options = { secretRotationOptions: TSecretRotationV2Option[] };
|
||||
|
||||
export type TSecretRotationV2Response = { secretRotation: TSecretRotationV2 };
|
||||
|
||||
export type TViewSecretRotationGeneratedCredentialsResponse =
|
||||
| TPostgresCredentialsRotationGeneratedCredentialsResponse
|
||||
| TMsSqlCredentialsRotationGeneratedCredentialsResponse;
|
||||
|
||||
export type TCreateSecretRotationV2DTO = DiscriminativePick<
|
||||
TSecretRotationV2,
|
||||
| "name"
|
||||
| "parameters"
|
||||
| "secretsMapping"
|
||||
| "description"
|
||||
| "connectionId"
|
||||
| "type"
|
||||
| "isAutoRotationEnabled"
|
||||
| "rotationInterval"
|
||||
| "rotateAtUtc"
|
||||
> & { environment: string; secretPath: string; projectId: string };
|
||||
|
||||
export type TUpdateSecretRotationV2DTO = Partial<
|
||||
Omit<TCreateSecretRotationV2DTO, "type" | "secretPath" | "projectId">
|
||||
> & {
|
||||
type: SecretRotation;
|
||||
rotationId: string;
|
||||
// required for query invalidation
|
||||
projectId: string;
|
||||
secretPath: string;
|
||||
};
|
||||
|
||||
export type TRotateSecretRotationV2DTO = {
|
||||
rotationId: string;
|
||||
type: SecretRotation;
|
||||
// required for query invalidation
|
||||
secretPath: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export type TDeleteSecretRotationV2DTO = TRotateSecretRotationV2DTO & {
|
||||
revokeGeneratedCredentials: boolean;
|
||||
deleteSecrets: boolean;
|
||||
};
|
||||
|
||||
export type TViewSecretRotationV2GeneratedCredentialsDTO = {
|
||||
rotationId: string;
|
||||
type: SecretRotation;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
import {
|
||||
TSecretRotationV2Base,
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase,
|
||||
TSqlCredentialsRotationGeneratedCredentials,
|
||||
TSqlCredentialsRotationProperties
|
||||
} from "@app/hooks/api/secretRotationsV2/types/shared";
|
||||
|
||||
export type TMsSqlCredentialsRotation = TSecretRotationV2Base & {
|
||||
type: SecretRotation.MsSqlCredentials;
|
||||
} & TSqlCredentialsRotationProperties;
|
||||
|
||||
export type TMsSqlCredentialsRotationGeneratedCredentialsResponse =
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase<
|
||||
SecretRotation.MsSqlCredentials,
|
||||
TSqlCredentialsRotationGeneratedCredentials
|
||||
>;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
import {
|
||||
TSecretRotationV2Base,
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase,
|
||||
TSqlCredentialsRotationGeneratedCredentials,
|
||||
TSqlCredentialsRotationProperties
|
||||
} from "@app/hooks/api/secretRotationsV2/types/shared";
|
||||
|
||||
export type TPostgresCredentialsRotation = TSecretRotationV2Base & {
|
||||
type: SecretRotation.PostgresCredentials;
|
||||
} & TSqlCredentialsRotationProperties;
|
||||
|
||||
export type TPostgresCredentialsRotationGeneratedCredentialsResponse =
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase<
|
||||
SecretRotation.PostgresCredentials,
|
||||
TSqlCredentialsRotationGeneratedCredentials
|
||||
>;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./secret-rotation-base";
|
||||
export * from "./sql-credentials-rotation";
|
||||
@@ -0,0 +1,53 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretRotationStatus } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
export type TSecretRotationV2Base = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
folderId: string;
|
||||
connectionId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
rotationInterval: number;
|
||||
rotateAtUtc: {
|
||||
hours: number;
|
||||
minutes: number;
|
||||
};
|
||||
projectId: string;
|
||||
rotationStatus: SecretRotationStatus | null;
|
||||
lastRotationJobId: string | null;
|
||||
lastRotatedAt: string;
|
||||
lastRotationAttemptedAt: string;
|
||||
lastRotationMessage?: string | null;
|
||||
connection: {
|
||||
app: AppConnection;
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
environment: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
folder: {
|
||||
id: string;
|
||||
path: string;
|
||||
};
|
||||
} & (
|
||||
| {
|
||||
nextRotationAt: string;
|
||||
isAutoRotationEnabled: true;
|
||||
}
|
||||
| {
|
||||
nextRotationAt?: null;
|
||||
isAutoRotationEnabled: false;
|
||||
}
|
||||
);
|
||||
|
||||
export type TSecretRotationV2GeneratedCredentialsResponseBase<U, T> = {
|
||||
activeIndex: 0 | 1;
|
||||
generatedCredentials: [T, T | undefined];
|
||||
type: U;
|
||||
rotationId: string;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
export type TSqlCredentialsRotationProperties = {
|
||||
parameters: {
|
||||
username1: string;
|
||||
username2: string;
|
||||
};
|
||||
secretsMapping: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TSqlOptionTemplate = {
|
||||
secretsMapping: TSqlCredentialsRotationProperties["secretsMapping"];
|
||||
createUserStatement: string;
|
||||
};
|
||||
|
||||
export type TSqlCredentialsRotationGeneratedCredentials = {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
@@ -85,7 +85,9 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => {
|
||||
version: el.version,
|
||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||
path: el.secretPath,
|
||||
secretMetadata: el.secretMetadata
|
||||
secretMetadata: el.secretMetadata,
|
||||
isRotatedSecret: el.isRotatedSecret,
|
||||
rotationId: el.rotationId
|
||||
};
|
||||
|
||||
if (el.type === SecretType.Personal) {
|
||||
|
||||
@@ -54,6 +54,8 @@ export type SecretV3RawSanitized = {
|
||||
skipMultilineEncoding?: boolean;
|
||||
secretMetadata?: { key: string; value: string }[];
|
||||
isReminderEvent?: boolean;
|
||||
isRotatedSecret?: boolean;
|
||||
rotationId?: string;
|
||||
};
|
||||
|
||||
export type SecretV3Raw = {
|
||||
@@ -76,6 +78,8 @@ export type SecretV3Raw = {
|
||||
tags?: WsTag[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
isRotatedSecret?: boolean;
|
||||
rotationId?: string;
|
||||
};
|
||||
|
||||
export type SecretV3RawResponse = {
|
||||
@@ -166,7 +170,7 @@ export type TUpdateSecretsV3DTO = {
|
||||
skipMultilineEncoding?: boolean;
|
||||
newSecretName?: string;
|
||||
secretKey: string;
|
||||
secretValue: string;
|
||||
secretValue?: string;
|
||||
secretComment?: string;
|
||||
secretReminderRepeatDays?: number | null;
|
||||
secretReminderNote?: string | null;
|
||||
|
||||
@@ -72,6 +72,55 @@ export const useDynamicSecretOverview = (
|
||||
return { dynamicSecretNames, isDynamicSecretPresentInEnv };
|
||||
};
|
||||
|
||||
export const useSecretRotationOverview = (
|
||||
secretRotations: DashboardProjectSecretsOverview["secretRotations"]
|
||||
) => {
|
||||
const secretRotationNames = useMemo(() => {
|
||||
const names = new Set<string>();
|
||||
secretRotations?.forEach((secretRotation) => {
|
||||
names.add(secretRotation.name);
|
||||
});
|
||||
return [...names];
|
||||
}, [secretRotations]);
|
||||
|
||||
const isSecretRotationPresentInEnv = useCallback(
|
||||
(name: string, env: string) => {
|
||||
return Boolean(
|
||||
secretRotations?.find(
|
||||
({ name: secretRotationName, environment }) =>
|
||||
secretRotationName === name && environment.slug === env
|
||||
)
|
||||
);
|
||||
},
|
||||
[secretRotations]
|
||||
);
|
||||
|
||||
const getSecretRotationByName = useCallback(
|
||||
(env: string, name: string) => {
|
||||
const secretRotation = secretRotations?.find(
|
||||
(rotation) => rotation.environment.slug === env && rotation.name === name
|
||||
);
|
||||
return secretRotation;
|
||||
},
|
||||
[secretRotations]
|
||||
);
|
||||
|
||||
const getSecretRotationStatusesByName = useCallback(
|
||||
(name: string) =>
|
||||
secretRotations
|
||||
?.filter((rotation) => rotation.name === name)
|
||||
.map((rotation) => rotation.rotationStatus),
|
||||
[secretRotations]
|
||||
);
|
||||
|
||||
return {
|
||||
secretRotationNames,
|
||||
isSecretRotationPresentInEnv,
|
||||
getSecretRotationByName,
|
||||
getSecretRotationStatusesByName
|
||||
};
|
||||
};
|
||||
|
||||
export const useSecretOverview = (secrets: DashboardProjectSecretsOverview["secrets"]) => {
|
||||
const secKeys = useMemo(() => {
|
||||
const keys = new Set<string>();
|
||||
|
||||
Reference in New Issue
Block a user