From 0e75a8f6d7a7cfe2dc5456f7e814553fb949d4d1 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Mon, 22 Jul 2024 14:17:07 +0800 Subject: [PATCH] misc: made kms hook generic --- frontend/src/hooks/api/kms/index.tsx | 4 +- frontend/src/hooks/api/kms/mutations.tsx | 80 ++--------- frontend/src/hooks/api/kms/types.ts | 64 +++++++++ .../OrgEncryptionTab/AwsKmsForm.tsx | 135 ++++++------------ 4 files changed, 117 insertions(+), 166 deletions(-) diff --git a/frontend/src/hooks/api/kms/index.tsx b/frontend/src/hooks/api/kms/index.tsx index 027fae1e3..84b238a3b 100644 --- a/frontend/src/hooks/api/kms/index.tsx +++ b/frontend/src/hooks/api/kms/index.tsx @@ -1,8 +1,8 @@ export { - useAddAwsExternalKms, + useAddExternalKms, useLoadProjectKmsBackup, useRemoveExternalKms, - useUpdateAwsExternalKms, + useUpdateExternalKms, useUpdateProjectKms } from "./mutations"; export { useGetActiveProjectKms, useGetExternalKmsById, useGetExternalKmsList } from "./queries"; diff --git a/frontend/src/hooks/api/kms/mutations.tsx b/frontend/src/hooks/api/kms/mutations.tsx index 7aace42ff..db2248a9e 100644 --- a/frontend/src/hooks/api/kms/mutations.tsx +++ b/frontend/src/hooks/api/kms/mutations.tsx @@ -3,50 +3,16 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; import { kmsKeys } from "./queries"; +import { AddExternalKmsType } from "./types"; -export const useAddAwsExternalKms = (orgId: string) => { +export const useAddExternalKms = (orgId: string) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({ - slug, - description, - credentialType, - accessKey, - secretKey, - assumeRoleArn, - externalId, - awsRegion, - kmsKeyId - }: { - slug: string; - description: string; - credentialType: string; - accessKey?: string; - secretKey?: string; - assumeRoleArn?: string; - externalId?: string; - awsRegion: string; - kmsKeyId?: string; - }) => { + mutationFn: async ({ slug, description, provider }: AddExternalKmsType) => { const { data } = await apiRequest.post("/api/v1/external-kms", { slug, description, - provider: { - type: "aws", - inputs: { - credential: { - type: credentialType, - data: { - accessKey, - secretKey, - assumeRoleArn, - externalId - } - }, - awsRegion, - kmsKeyId - } - } + provider }); return data; @@ -57,51 +23,21 @@ export const useAddAwsExternalKms = (orgId: string) => { }); }; -export const useUpdateAwsExternalKms = (orgId: string) => { +export const useUpdateExternalKms = (orgId: string) => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ kmsId, slug, description, - credentialType, - accessKey, - secretKey, - assumeRoleArn, - externalId, - awsRegion, - kmsKeyId + provider }: { kmsId: string; - slug?: string; - description?: string; - credentialType?: string; - accessKey?: string; - secretKey?: string; - assumeRoleArn?: string; - externalId?: string; - awsRegion: string; - kmsKeyId?: string; - }) => { + } & AddExternalKmsType) => { const { data } = await apiRequest.patch(`/api/v1/external-kms/${kmsId}`, { slug, description, - provider: { - type: "aws", - inputs: { - credential: { - type: credentialType, - data: { - accessKey, - secretKey, - assumeRoleArn, - externalId - } - }, - awsRegion, - kmsKeyId - } - } + provider }); return data; diff --git a/frontend/src/hooks/api/kms/types.ts b/frontend/src/hooks/api/kms/types.ts index c51c359f3..e2cfabc42 100644 --- a/frontend/src/hooks/api/kms/types.ts +++ b/frontend/src/hooks/api/kms/types.ts @@ -1,3 +1,6 @@ +import slugify from "@sindresorhus/slugify"; +import { z } from "zod"; + export type Kms = { id: string; description: string; @@ -31,3 +34,64 @@ export enum ExternalKmsProvider { } export const INTERNAL_KMS_KEY_ID = "internal"; + +export enum KmsAwsCredentialType { + AssumeRole = "assume-role", + AccessKey = "access-key" +} + +export const ExternalKmsAwsSchema = z.object({ + credential: z + .discriminatedUnion("type", [ + z.object({ + type: z.literal(KmsAwsCredentialType.AccessKey), + data: z.object({ + accessKey: z.string().trim().min(1).describe("AWS user account access key"), + secretKey: z.string().trim().min(1).describe("AWS user account secret key") + }) + }), + z.object({ + type: z.literal(KmsAwsCredentialType.AssumeRole), + data: z.object({ + assumeRoleArn: z + .string() + .trim() + .min(1) + .describe("AWS user role to be assumed by infisical"), + externalId: z + .string() + .trim() + .min(1) + .optional() + .describe("AWS assume role external id for furthur security in authentication") + }) + }) + ]) + .describe("AWS credential information to connect"), + awsRegion: z.string().min(1).trim().describe("AWS region to connect"), + kmsKeyId: z + .string() + .trim() + .optional() + .describe( + "A pre existing AWS KMS key id to be used for encryption. If not provided a kms key will be generated." + ) +}); + +export const ExternalKmsInputSchema = z.discriminatedUnion("type", [ + z.object({ type: z.literal(ExternalKmsProvider.AWS), inputs: ExternalKmsAwsSchema }) +]); + +export const AddExternalKmsSchema = z.object({ + slug: z + .string() + .trim() + .min(1) + .refine((v) => slugify(v) === v, { + message: "Alias must be a valid slug" + }), + description: z.string().trim().min(1).default(""), + provider: ExternalKmsInputSchema +}); + +export type AddExternalKmsType = z.infer; diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AwsKmsForm.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AwsKmsForm.tsx index fe7acb8e8..6bafbd909 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AwsKmsForm.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AwsKmsForm.tsx @@ -1,13 +1,17 @@ import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import slugify from "@sindresorhus/slugify"; -import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2"; import { useOrganization } from "@app/context"; -import { useAddAwsExternalKms, useUpdateAwsExternalKms } from "@app/hooks/api"; -import { Kms } from "@app/hooks/api/kms/types"; +import { useAddExternalKms, useUpdateExternalKms } from "@app/hooks/api"; +import { + AddExternalKmsSchema, + AddExternalKmsType, + ExternalKmsProvider, + Kms, + KmsAwsCredentialType +} from "@app/hooks/api/kms/types"; const AWS_REGIONS = [ { name: "US East (Ohio)", slug: "us-east-2" }, @@ -41,42 +45,6 @@ const AWS_REGIONS = [ { name: "AWS GovCloud (US-West)", slug: "us-gov-west-1" } ]; -export enum KmsAwsCredentialType { - AssumeRole = "assume-role", - AccessKey = "access-key" -} - -const formSchema = z.object({ - slug: z - .string() - .trim() - .min(1) - .refine((v) => slugify(v) === v, { - message: "Alias must be a valid slug" - }), - description: z.string().trim().min(1).default(""), - credential: z.discriminatedUnion("type", [ - z.object({ - type: z.literal(KmsAwsCredentialType.AccessKey), - data: z.object({ - accessKey: z.string().trim().min(1), - secretKey: z.string().trim().min(1) - }) - }), - z.object({ - type: z.literal(KmsAwsCredentialType.AssumeRole), - data: z.object({ - assumeRoleArn: z.string().trim().min(1), - externalId: z.string().trim().min(1).optional() - }) - }) - ]), - awsRegion: z.string().min(1).trim(), - kmsKeyId: z.string().trim().optional() -}); - -type TForm = z.infer; - type Props = { onCompleted: () => void; onCancel: () => void; @@ -90,51 +58,45 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms }: Props) => { watch, setValue, formState: { isSubmitting } - } = useForm({ - resolver: zodResolver(formSchema), + } = useForm({ + resolver: zodResolver(AddExternalKmsSchema), defaultValues: { slug: kms?.slug, description: kms?.description, - credential: { - type: kms?.external?.providerInput?.credential?.type, - data: { - accessKey: kms?.external?.providerInput?.credential?.data?.accessKey, - secretKey: kms?.external?.providerInput?.credential?.data?.secretKey, - assumeRoleArn: kms?.external?.providerInput?.credential?.data?.assumeRoleArn, - externalId: kms?.external?.providerInput?.credential?.data?.externalId + provider: { + type: ExternalKmsProvider.AWS, + inputs: { + credential: { + type: kms?.external?.providerInput?.credential?.type, + data: { + accessKey: kms?.external?.providerInput?.credential?.data?.accessKey, + secretKey: kms?.external?.providerInput?.credential?.data?.secretKey, + assumeRoleArn: kms?.external?.providerInput?.credential?.data?.assumeRoleArn, + externalId: kms?.external?.providerInput?.credential?.data?.externalId + } + }, + awsRegion: kms?.external?.providerInput?.awsRegion, + kmsKeyId: kms?.external?.providerInput?.kmsKeyId } - }, - awsRegion: kms?.external?.providerInput?.awsRegion, - kmsKeyId: kms?.external?.providerInput?.kmsKeyId + } } }); const { currentOrg } = useOrganization(); - const { mutateAsync: addAwsExternalKms } = useAddAwsExternalKms(currentOrg?.id!); - const { mutateAsync: updateAwsExternalKms } = useUpdateAwsExternalKms(currentOrg?.id!); + const { mutateAsync: addAwsExternalKms } = useAddExternalKms(currentOrg?.id!); + const { mutateAsync: updateAwsExternalKms } = useUpdateExternalKms(currentOrg?.id!); - const selectedAwsAuthType = watch("credential.type"); + const selectedAwsAuthType = watch("provider.inputs.credential.type"); - const handleAddAwsKms = async (data: TForm) => { - const { slug, description, credential, awsRegion, kmsKeyId } = data; + const handleAddAwsKms = async (data: AddExternalKmsType) => { + const { slug, description, provider } = data; try { if (kms) { await updateAwsExternalKms({ kmsId: kms.id, slug, description, - credentialType: credential.type, - awsRegion, - kmsKeyId, - ...(credential.type === KmsAwsCredentialType.AccessKey - ? { - accessKey: credential.data.accessKey, - secretKey: credential.data.secretKey - } - : { - assumeRoleArn: credential.data.assumeRoleArn, - externalId: credential.data.externalId - }) + provider }); createNotification({ @@ -145,18 +107,7 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms }: Props) => { await addAwsExternalKms({ slug, description, - credentialType: credential.type, - awsRegion, - kmsKeyId, - ...(credential.type === KmsAwsCredentialType.AccessKey - ? { - accessKey: credential.data.accessKey, - secretKey: credential.data.secretKey - } - : { - assumeRoleArn: credential.data.assumeRoleArn, - externalId: credential.data.externalId - }) + provider }); createNotification({ @@ -193,7 +144,7 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms }: Props) => { /> ( { defaultValue={field.value} {...field} onValueChange={(e) => { - setValue("credential.data.accessKey", ""); - setValue("credential.data.secretKey", ""); - setValue("credential.data.assumeRoleArn", ""); - setValue("credential.data.externalId", ""); + setValue("provider.inputs.credential.data.accessKey", ""); + setValue("provider.inputs.credential.data.secretKey", ""); + setValue("provider.inputs.credential.data.assumeRoleArn", ""); + setValue("provider.inputs.credential.data.externalId", ""); onChange(e); }} @@ -225,7 +176,7 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms }: Props) => { <> ( { /> ( { <> ( { /> ( { )} (