fix: review changes

This commit is contained in:
Piyush Gupta
2025-12-05 19:39:21 +05:30
parent 58fbbe0d91
commit fd1a3d5d12
6 changed files with 309 additions and 306 deletions

View File

@@ -26,7 +26,7 @@ const AwsConnectionAssumeRoleCredentialsSchema = z.object({
.trim()
.min(1)
.optional()
.describe("AWS assume role external id for furthur security in authentication")
.describe("AWS assume role external id for further security in authentication")
});
const AwsConnectionAccessTokenCredentialsSchema = z.object({
@@ -64,7 +64,10 @@ export const SanitizedExternalKmsAwsSchema = ExternalKmsAwsSchema.extend({
}),
z.object({
type: z.literal(KmsAwsCredentialType.AssumeRole),
data: AwsConnectionAssumeRoleCredentialsSchema.pick({})
data: AwsConnectionAssumeRoleCredentialsSchema.pick({
assumeRoleArn: true,
externalId: true
})
})
])
});

View File

@@ -131,7 +131,7 @@ export type AddExternalKmsType = z.infer<typeof AddExternalKmsSchema>;
// we need separate schema for update because the credential field is not required on GCP
export const ExternalKmsUpdateInputSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal(ExternalKmsProvider.Aws), inputs: ExternalKmsAwsSchema }),
z.object({ type: z.literal(ExternalKmsProvider.Aws), inputs: ExternalKmsAwsSchema.partial() }),
z.object({
type: z.literal(ExternalKmsProvider.Gcp),
inputs: ExternalKmsGcpSchema.pick({ gcpRegion: true, keyName: true })

View File

@@ -11,7 +11,8 @@ import {
AddExternalKmsType,
ExternalKmsProvider,
Kms,
KmsAwsCredentialType
KmsAwsCredentialType,
UpdateExternalKmsSchema
} from "@app/hooks/api/kms/types";
const AWS_REGIONS = [
@@ -50,10 +51,12 @@ type Props = {
onCompleted: () => void;
onCancel: () => void;
kms?: Kms;
mode?: "full" | "credentials";
mode?: "full" | "credentials" | "details";
};
export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props) => {
const validationSchema = kms ? UpdateExternalKmsSchema : AddExternalKmsSchema;
const {
control,
handleSubmit,
@@ -61,24 +64,35 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
setValue,
formState: { isSubmitting }
} = useForm<AddExternalKmsType>({
resolver: zodResolver(AddExternalKmsSchema),
resolver: zodResolver(validationSchema),
defaultValues: {
name: kms?.name,
description: kms?.description ?? "",
configuration: {
type: ExternalKmsProvider.Aws,
inputs: {
credential: {
type: kms?.externalKms?.configuration?.credential?.type,
data: {
accessKey: kms?.externalKms?.configuration?.credential?.data?.accessKey,
secretKey: kms?.externalKms?.configuration?.credential?.data?.secretKey,
assumeRoleArn: kms?.externalKms?.configuration?.credential?.data?.assumeRoleArn,
externalId: kms?.externalKms?.configuration?.credential?.data?.externalId
}
},
awsRegion: kms?.externalKms?.configuration?.awsRegion,
kmsKeyId: kms?.externalKms?.configuration?.kmsKeyId
...(mode !== "details" &&
kms?.externalKms?.configuration?.credential?.type &&
kms.externalKms.configuration.credential.data
? {
credential: {
type: kms.externalKms.configuration.credential.type,
data: {
accessKey: kms.externalKms.configuration.credential.data?.accessKey ?? "",
secretKey: kms.externalKms.configuration.credential.data?.secretKey ?? "",
assumeRoleArn:
kms.externalKms.configuration.credential.data?.assumeRoleArn ?? "",
externalId: kms.externalKms.configuration.credential.data?.externalId ?? ""
}
}
}
: {}),
...(mode !== "credentials"
? {
awsRegion: kms?.externalKms?.configuration?.awsRegion ?? "",
kmsKeyId: kms?.externalKms?.configuration?.kmsKeyId ?? ""
}
: {})
}
}
}
@@ -97,16 +111,33 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
const { name, description, configuration } = data;
try {
if (kms) {
if (configuration.type !== ExternalKmsProvider.Aws) {
throw new Error("Invalid configuration type");
}
const awsInputs = configuration.inputs;
if (mode === "credentials") {
await updateAwsExternalKms({
kmsId: kms.id,
configuration
configuration: {
type: ExternalKmsProvider.Aws,
inputs: {
credential: { ...awsInputs.credential }
}
}
});
} else {
await updateAwsExternalKms({
kmsId: kms.id,
name,
description
description,
configuration: {
type: ExternalKmsProvider.Aws,
inputs: {
awsRegion: awsInputs.awsRegion,
kmsKeyId: awsInputs.kmsKeyId
}
}
});
}
@@ -114,7 +145,7 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
text:
mode === "credentials"
? "Successfully updated AWS External KMS credentials"
: "Successfully updated AWS External KMS",
: "Successfully updated AWS External KMS Details",
type: "success"
});
} else {
@@ -138,7 +169,7 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
return (
<form onSubmit={handleSubmit(handleAwsKmsFormSubmit)} autoComplete="off">
{mode === "full" && (
{(mode === "full" || mode === "details") && (
<>
<Controller
control={control}
@@ -160,86 +191,127 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
/>
</>
)}
<Controller
control={control}
name="configuration.inputs.credential.type"
defaultValue={KmsAwsCredentialType.AssumeRole}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Authentication Mode"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => {
setValue("configuration.inputs.credential.data.accessKey", "");
setValue("configuration.inputs.credential.data.secretKey", "");
setValue("configuration.inputs.credential.data.assumeRoleArn", "");
setValue("configuration.inputs.credential.data.externalId", "");
onChange(e);
}}
className="w-full"
>
<SelectItem value={KmsAwsCredentialType.AssumeRole}>AWS Assume Role</SelectItem>
<SelectItem value={KmsAwsCredentialType.AccessKey}>Access Key</SelectItem>
</Select>
</FormControl>
)}
/>
{selectedAwsAuthType === KmsAwsCredentialType.AccessKey ? (
{(mode === "full" || mode === "credentials") && (
<>
<Controller
control={control}
name="configuration.inputs.credential.data.accessKey"
render={({ field, fieldState: { error } }) => (
name="configuration.inputs.credential.type"
defaultValue={KmsAwsCredentialType.AssumeRole}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Access Key ID"
label="Authentication Mode"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="configuration.inputs.credential.data.secretKey"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret Access Key"
errorText={error?.message}
isError={Boolean(error)}
>
<Input type="password" autoComplete="new-password" placeholder="" {...field} />
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => {
setValue("configuration.inputs.credential.data.accessKey", "");
setValue("configuration.inputs.credential.data.secretKey", "");
setValue("configuration.inputs.credential.data.assumeRoleArn", "");
setValue("configuration.inputs.credential.data.externalId", "");
onChange(e);
}}
className="w-full"
>
<SelectItem value={KmsAwsCredentialType.AssumeRole}>AWS Assume Role</SelectItem>
<SelectItem value={KmsAwsCredentialType.AccessKey}>Access Key</SelectItem>
</Select>
</FormControl>
)}
/>
{selectedAwsAuthType === KmsAwsCredentialType.AccessKey ? (
<>
<Controller
control={control}
name="configuration.inputs.credential.data.accessKey"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Key ID"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="configuration.inputs.credential.data.secretKey"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret Access Key"
errorText={error?.message}
isError={Boolean(error)}
>
<Input type="password" autoComplete="new-password" placeholder="" {...field} />
</FormControl>
)}
/>
</>
) : (
<>
<Controller
control={control}
name="configuration.inputs.credential.data.assumeRoleArn"
render={({ field, fieldState: { error } }) => (
<FormControl
label="IAM Role ARN For Role Assumption"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="configuration.inputs.credential.data.externalId"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Assume Role External ID"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="" {...field} />
</FormControl>
)}
/>
</>
)}
</>
) : (
)}
{(mode === "full" || mode === "details") && (
<>
<Controller
control={control}
name="configuration.inputs.credential.data.assumeRoleArn"
render={({ field, fieldState: { error } }) => (
<FormControl
label="IAM Role ARN For Role Assumption"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="" {...field} />
name="configuration.inputs.awsRegion"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl label="AWS Region" errorText={error?.message} isError={Boolean(error)}>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full border border-mineshaft-500"
>
{AWS_REGIONS.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`kms-aws-region-${awsRegion.slug}`}>
{awsRegion.name} <Badge variant="neutral">{awsRegion.slug}</Badge>
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="configuration.inputs.credential.data.externalId"
name="configuration.inputs.kmsKeyId"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Assume Role External ID"
label="AWS KMS Key ID"
errorText={error?.message}
isError={Boolean(error)}
>
@@ -249,35 +321,6 @@ export const AwsKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
/>
</>
)}
<Controller
control={control}
name="configuration.inputs.awsRegion"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl label="AWS Region" errorText={error?.message} isError={Boolean(error)}>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full border border-mineshaft-500"
>
{AWS_REGIONS.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`kms-aws-region-${awsRegion.slug}`}>
{awsRegion.name} <Badge variant="neutral">{awsRegion.slug}</Badge>
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="configuration.inputs.kmsKeyId"
render={({ field, fieldState: { error } }) => (
<FormControl label="AWS KMS Key ID" errorText={error?.message} isError={Boolean(error)}>
<Input placeholder="" {...field} />
</FormControl>
)}
/>
<div className="mt-6 flex items-center space-x-4">
<Button type="submit" isLoading={isSubmitting}>
{mode === "credentials" ? "Update Credentials" : "Save"}

View File

@@ -1,4 +1,4 @@
import { ContentLoader, Modal, ModalContent } from "@app/components/v2";
import { Modal, ModalContent } from "@app/components/v2";
import { useGetExternalKmsById } from "@app/hooks/api";
import { ExternalKmsProvider } from "@app/hooks/api/kms/types";
@@ -18,7 +18,7 @@ export const EditExternalKmsCredentialsModal = ({
provider,
onOpenChange
}: Props) => {
const { data: externalKms, isPending } = useGetExternalKmsById({ kmsId, provider });
const { data: kms } = useGetExternalKmsById({ kmsId, provider });
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
@@ -26,18 +26,17 @@ export const EditExternalKmsCredentialsModal = ({
subTitle="Update the credentials for this KMS."
bodyClassName="overflow-visible"
>
{isPending && <ContentLoader />}
{externalKms?.externalKms?.provider === ExternalKmsProvider.Aws && (
{kms?.externalKms?.provider === ExternalKmsProvider.Aws && (
<AwsKmsForm
kms={externalKms}
kms={kms}
mode="credentials"
onCancel={() => onOpenChange(false)}
onCompleted={() => onOpenChange(false)}
/>
)}
{externalKms?.externalKms?.provider === ExternalKmsProvider.Gcp && (
{kms?.externalKms?.provider === ExternalKmsProvider.Gcp && (
<GcpKmsForm
kms={externalKms}
kms={kms}
mode="credentials"
onCancel={() => onOpenChange(false)}
onCompleted={() => onOpenChange(false)}

View File

@@ -1,20 +1,9 @@
import { FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Modal, ModalContent } from "@app/components/v2";
import { useGetExternalKmsById } from "@app/hooks/api";
import { ExternalKmsProvider } from "@app/hooks/api/kms/types";
import { createNotification } from "@app/components/notifications";
import {
Button,
ContentLoader,
FormControl,
Input,
Modal,
ModalClose,
ModalContent
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useGetExternalKmsById, useUpdateExternalKms } from "@app/hooks/api";
import { ExternalKmsProvider, Kms } from "@app/hooks/api/kms/types";
import { AwsKmsForm } from "./AwsKmsForm";
import { GcpKmsForm } from "./GcpKmsForm";
type Props = {
isOpen: boolean;
@@ -23,85 +12,32 @@ type Props = {
provider: ExternalKmsProvider;
};
const formSchema = z.object({
name: z.string().min(1).trim(),
description: z.string().trim().optional()
});
type FormData = z.infer<typeof formSchema>;
type ContentProps = { kms: Kms; provider: ExternalKmsProvider; onComplete: () => void };
const Content = ({ kms, onComplete, provider }: ContentProps) => {
const { currentOrg } = useOrganization();
const { mutateAsync: updateExternalKms, isPending } = useUpdateExternalKms(
currentOrg.id,
provider
);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
name: kms.name,
description: kms.description ?? ""
}
});
const {
handleSubmit,
formState: { isDirty }
} = form;
const onSubmit = async (formData: FormData) => {
if (!kms) return;
await updateExternalKms({
kmsId: kms.id,
name: formData.name,
description: formData.description
});
createNotification({
text: "Successfully updated KMS details",
type: "success"
});
onComplete();
};
return (
<FormProvider {...form}>
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl label="Alias">
<Input {...form.register("name")} />
</FormControl>
<FormControl label="Description">
<Input {...form.register("description")} />
</FormControl>
<div className="mt-6 flex items-center space-x-4">
<Button type="submit" isLoading={isPending} isDisabled={!isDirty}>
Update Details
</Button>
<ModalClose asChild>
<Button variant="outline_bg">Cancel</Button>
</ModalClose>
</div>
</form>
</FormProvider>
);
};
export const EditExternalKmsDetailsModal = ({ isOpen, onOpenChange, kmsId, provider }: Props) => {
const { data: kms, isPending } = useGetExternalKmsById({ kmsId, provider });
const { data: kms } = useGetExternalKmsById({ kmsId, provider });
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
className="max-w-2xl"
title="Edit KMS Details"
subTitle="Update the name and description for this KMS."
bodyClassName="overflow-visible"
>
{isPending && <ContentLoader />}
{kms && <Content kms={kms} provider={provider} onComplete={() => onOpenChange(false)} />}
{kms?.externalKms?.provider === ExternalKmsProvider.Aws && (
<AwsKmsForm
kms={kms}
mode="details"
onCancel={() => onOpenChange(false)}
onCompleted={() => onOpenChange(false)}
/>
)}
{kms?.externalKms?.provider === ExternalKmsProvider.Gcp && (
<GcpKmsForm
kms={kms}
mode="details"
onCancel={() => onOpenChange(false)}
onCompleted={() => onOpenChange(false)}
/>
)}
</ModalContent>
</Modal>
);

View File

@@ -24,7 +24,7 @@ type Props = {
onCompleted: () => void;
onCancel: () => void;
kms?: Kms;
mode?: "full" | "credentials";
mode?: "full" | "credentials" | "details";
};
const GCP_REGIONS = [
@@ -145,53 +145,70 @@ export const GcpKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
// handles the form submission
const handleGcpKmsFormSubmit = async (data: AddExternalKmsGcpFormSchemaType) => {
const { name, description, gcpRegion: gcpRegionObject, keyObject } = data;
const gcpRegion = gcpRegionObject.value;
if (!keys.find((k) => k.value === keyObject?.value)) {
setError("keyObject", {
message: "Please select a valid key."
});
resetField("keyObject");
return;
}
try {
if (kms) {
if (mode === "credentials") {
await updateGcpExternalKms({
kmsId: kms.id,
name: kms.name,
description: kms.description,
configuration: {
type: ExternalKmsProvider.Gcp,
inputs: {
gcpRegion,
keyName: keyObject?.value
}
}
});
} else {
if (mode === "details") {
await updateGcpExternalKms({
kmsId: kms.id,
name,
description,
description
});
createNotification({
text: "Successfully updated GCP External KMS Details",
type: "success"
});
} else if (mode === "credentials") {
const gcpRegion = gcpRegionObject?.value;
if (!gcpRegion) {
setError("gcpRegion", {
message: "Please select a GCP region."
});
return;
}
if (keyObject && !keys.find((k) => k.value === keyObject.value)) {
setError("keyObject", {
message: "Please select a valid key."
});
resetField("keyObject");
return;
}
await updateGcpExternalKms({
kmsId: kms.id,
configuration: {
type: ExternalKmsProvider.Gcp,
inputs: {
gcpRegion,
keyName: keyObject?.value
keyName: keyObject?.value ?? kms.externalKms.configuration.keyName
}
}
});
createNotification({
text: "Successfully updated GCP External KMS configuration",
type: "success"
});
}
} else {
const gcpRegion = gcpRegionObject?.value;
if (!gcpRegion) {
setError("gcpRegion", {
message: "Please select a GCP region."
});
return;
}
if (!keys.find((k) => k.value === keyObject?.value)) {
setError("keyObject", {
message: "Please select a valid key."
});
resetField("keyObject");
return;
}
createNotification({
text:
mode === "credentials"
? "Successfully updated GCP External KMS configuration"
: "Successfully updated GCP External KMS",
type: "success"
});
} else {
const credentialJson = await getCredentialFileJson();
if (!credentialJson) {
return;
@@ -230,8 +247,9 @@ export const GcpKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
if (!kms && !credentialJson) {
return;
}
const gcpRegion = getValues("gcpRegion").value;
if (!gcpRegion.length) {
const gcpRegionObject = getValues("gcpRegion");
const gcpRegion = gcpRegionObject?.value;
if (!gcpRegion) {
setError("gcpRegion", {
message: "Please select a GCP region to fetch GCP Keys."
});
@@ -284,7 +302,7 @@ export const GcpKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
return (
<form onSubmit={handleSubmit(handleGcpKmsFormSubmit)} autoComplete="off">
{mode === "full" && (
{(mode === "full" || mode === "details") && (
<>
<Controller
control={control}
@@ -306,75 +324,79 @@ export const GcpKmsForm = ({ onCompleted, onCancel, kms, mode = "full" }: Props)
/>
</>
)}
<Controller
control={control}
name="gcpRegion"
render={({ field, fieldState: { error } }) => (
<FormControl label="GCP Region" errorText={error?.message} isError={Boolean(error)}>
<FilterableSelect
className="w-full"
placeholder="Select a GCP region"
name="gcpRegion"
options={GCP_REGIONS}
value={field.value}
onChange={(e) => {
resetField("keyObject");
field.onChange(e);
fetchGCPKeys();
}}
formatOptionLabel={formatOptionLabel}
{(mode === "full" || mode === "credentials") && (
<>
<Controller
control={control}
name="gcpRegion"
render={({ field, fieldState: { error } }) => (
<FormControl label="GCP Region" errorText={error?.message} isError={Boolean(error)}>
<FilterableSelect
className="w-full"
placeholder="Select a GCP region"
name="gcpRegion"
options={GCP_REGIONS}
value={field.value}
onChange={(e) => {
resetField("keyObject");
field.onChange(e);
fetchGCPKeys();
}}
formatOptionLabel={formatOptionLabel}
/>
</FormControl>
)}
/>
{!kms && (
<Controller
control={control}
name="credentialFile"
render={({ field: { value, onChange, ref, ...rest }, fieldState: { error } }) => (
<FormControl
label="Service Account Credential JSON"
errorText={error?.message}
isError={Boolean(error)}
>
<Input
{...rest}
ref={ref}
type="file"
accept=".json"
placeholder=""
value={value?.filename}
onChange={(e) => {
onChange(e.target.files);
fetchGCPKeys();
}}
/>
</FormControl>
)}
/>
</FormControl>
)}
/>
{!kms && (
<Controller
control={control}
name="credentialFile"
render={({ field: { value, onChange, ref, ...rest }, fieldState: { error } }) => (
<FormControl
label="Service Account Credential JSON"
errorText={error?.message}
isError={Boolean(error)}
>
<Input
{...rest}
ref={ref}
type="file"
accept=".json"
placeholder=""
value={value?.filename}
onChange={(e) => {
onChange(e.target.files);
fetchGCPKeys();
}}
/>
</FormControl>
)}
/>
)}
<Controller
control={control}
name="keyObject"
render={({ field, fieldState: { error } }) => (
<FormControl label="GCP Key Name" errorText={error?.message} isError={Boolean(error)}>
<FilterableSelect
className="w-full"
placeholder={getPlaceholderText()}
isDisabled={!isCredentialValid || !keys.length}
name="key"
options={keys}
value={field.value}
onChange={field.onChange}
/>
</FormControl>
)}
/>
{kms && (
<span className="text-xs text-mineshaft-300">
To change your GCP credentials, create a new external KMS and assign it to project you
want to use it with.
</span>
<Controller
control={control}
name="keyObject"
render={({ field, fieldState: { error } }) => (
<FormControl label="GCP Key Name" errorText={error?.message} isError={Boolean(error)}>
<FilterableSelect
className="w-full"
placeholder={getPlaceholderText()}
isDisabled={!isCredentialValid || !keys.length}
name="key"
options={keys}
value={field.value}
onChange={field.onChange}
/>
</FormControl>
)}
/>
{kms && (
<span className="text-xs text-mineshaft-300">
To change your GCP credentials, create a new external KMS and assign it to project you
want to use it with.
</span>
)}
</>
)}
<div className="mt-6 flex items-center space-x-4">
<Button type="submit" isLoading={isSubmitting}>