diff --git a/backend/src/ee/routes/v1/index.ts b/backend/src/ee/routes/v1/index.ts index 6bd9176d8..1892bd5e8 100644 --- a/backend/src/ee/routes/v1/index.ts +++ b/backend/src/ee/routes/v1/index.ts @@ -4,6 +4,7 @@ import { registerAuditLogStreamRouter } from "./audit-log-stream-router"; import { registerCaCrlRouter } from "./certificate-authority-crl-router"; import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router"; import { registerDynamicSecretRouter } from "./dynamic-secret-router"; +import { registerExternalKmsRouter } from "./external-kms-router"; import { registerGroupRouter } from "./group-router"; import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router"; import { registerLdapRouter } from "./ldap-router"; @@ -87,4 +88,8 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => { }, { prefix: "/additional-privilege" } ); + + await server.register(registerExternalKmsRouter, { + prefix: "/external-kms" + }); }; diff --git a/frontend/src/hooks/api/kms/index.tsx b/frontend/src/hooks/api/kms/index.tsx new file mode 100644 index 000000000..c702ecaf6 --- /dev/null +++ b/frontend/src/hooks/api/kms/index.tsx @@ -0,0 +1 @@ +export { useAddAwsExternalKms } from "./mutations"; diff --git a/frontend/src/hooks/api/kms/mutations.tsx b/frontend/src/hooks/api/kms/mutations.tsx new file mode 100644 index 000000000..ee778198c --- /dev/null +++ b/frontend/src/hooks/api/kms/mutations.tsx @@ -0,0 +1,53 @@ +import { useMutation } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +export const useAddAwsExternalKms = () => { + 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; + }) => { + 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 + } + } + }); + + return data; + }, + onSuccess() {} + }); +}; diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AddExternalKmsForm.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AddExternalKmsForm.tsx index f6ee93ef2..30d061028 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AddExternalKmsForm.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AddExternalKmsForm.tsx @@ -90,7 +90,7 @@ export const AddExternalKmsForm = ({ isOpen, onToggle }: Props) => { animate={{ opacity: 1, translateX: 0 }} exit={{ opacity: 0, translateX: -30 }} > - + {}} onCompleted={() => {}} /> )} diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AwsKmsForm.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AwsKmsForm.tsx index 40373572b..812637279 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AwsKmsForm.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgEncryptionTab/AddExternalKmsForm/AwsKmsForm.tsx @@ -1,8 +1,11 @@ import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; +import slugify from "@sindresorhus/slugify"; import { z } from "zod"; -import { FormControl, Input, Select, SelectItem } from "@app/components/v2"; +import { createNotification } from "@app/components/notifications"; +import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2"; +import { useAddAwsExternalKms } from "@app/hooks/api/kms"; const AWS_REGIONS = [ { name: "US East (Ohio)", slug: "us-east-2" }, @@ -42,6 +45,14 @@ export enum KmsAwsCredentialType { } const formSchema = z.object({ + slug: z + .string() + .trim() + .min(1) + .refine((v) => slugify(v) === v, { + message: "Slug must be a valid slug" + }), + description: z.string().trim().min(1).default(""), credential: z.discriminatedUnion("type", [ z.object({ type: z.literal(KmsAwsCredentialType.AccessKey), @@ -58,23 +69,78 @@ const formSchema = z.object({ }) }) ]), - awsRegion: z.string().min(1).trim().describe("AWS region to connect"), + awsRegion: z.string().min(1).trim(), kmsKeyId: z.string().trim().optional() }); type TForm = z.infer; -export const AwsKmsForm = () => { - const { control, handleSubmit, watch } = useForm({ +type Props = { + onCompleted: () => void; + onCancel: () => void; +}; + +export const AwsKmsForm = ({ onCompleted, onCancel }: Props) => { + const { + control, + handleSubmit, + watch, + formState: { isSubmitting } + } = useForm({ resolver: zodResolver(formSchema) }); const selectedAwsAuthType = watch("credential.type"); - const handleAddAwsKms = () => {}; + const { mutateAsync: addAwsExternalKms } = useAddAwsExternalKms(); + + const handleAddAwsKms = async (data: TForm) => { + const { slug, description, credential, awsRegion, kmsKeyId } = data; + 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 + }) + }); + + createNotification({ + text: "Successfully added AWS External KMS", + type: "success" + }); + + onCompleted(); + }; return (
+ ( + + + + )} + /> + ( + + + + )} + /> { )} /> +
+ + +
); };