feat: finished up client cert generation

This commit is contained in:
Sheen Capadngan
2025-02-07 01:24:32 +08:00
parent 0269f57768
commit a3acfa65a2
21 changed files with 593 additions and 14 deletions
@@ -0,0 +1,147 @@
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import {
Button,
FormControl,
Input,
Modal,
ModalClose,
ModalContent,
Select,
SelectItem
} from "@app/components/v2";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
import { useGenerateKmipClientCertificate } from "@app/hooks/api/kmip";
import { KmipClientCertificate, TKmipClient } from "@app/hooks/api/kmip/types";
const formSchema = z.object({
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
ttl: z.string()
});
export type FormData = z.infer<typeof formSchema>;
type Props = {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
kmipClient?: TKmipClient | null;
displayNewClientCertificate: (certificate: KmipClientCertificate) => void;
};
type FormProps = Pick<Props, "kmipClient" | "displayNewClientCertificate"> & {
onComplete: () => void;
};
const KmipClientCertificateForm = ({
displayNewClientCertificate,
kmipClient,
onComplete
}: FormProps) => {
const { mutateAsync: createKmipClientCertificate } = useGenerateKmipClientCertificate();
const {
control,
handleSubmit,
formState: { isSubmitting }
} = useForm<FormData>({
resolver: zodResolver(formSchema)
});
const handleKmipClientSubmit = async (payload: FormData) => {
if (!kmipClient) {
return;
}
const certificate = await createKmipClientCertificate({
...payload,
clientId: kmipClient?.id
});
createNotification({
text: "Successfully created KMIP client certificate",
type: "success"
});
displayNewClientCertificate(certificate);
onComplete();
};
return (
<form onSubmit={handleSubmit(handleKmipClientSubmit)}>
<Controller
control={control}
name="ttl"
render={({ field, fieldState: { error } }) => (
<FormControl label="TTL" isError={Boolean(error)} errorText={error?.message} isRequired>
<Input {...field} placeholder="2 days, 1d, 2h, 1y, ..." />
</FormControl>
)}
/>
<Controller
control={control}
name="keyAlgorithm"
defaultValue={CertKeyAlgorithm.RSA_2048}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Key Algorithm"
errorText={error?.message}
isError={Boolean(error)}
helperText="This defines the key algorithm to use for signing the client certificate."
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{certKeyAlgorithms.map(({ label, value }) => (
<SelectItem value={String(value || "")} key={label}>
{label}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<div className="flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
Generate Client Certificate
</Button>
<ModalClose asChild>
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</ModalClose>
</div>
</form>
);
};
export const CreateKmipClientCertificateModal = ({
isOpen,
onOpenChange,
kmipClient,
displayNewClientCertificate
}: Props) => {
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent title="Generate KMIP client certificate">
<KmipClientCertificateForm
onComplete={() => onOpenChange(false)}
displayNewClientCertificate={displayNewClientCertificate}
kmipClient={kmipClient}
/>
</ModalContent>
</Modal>
);
};
@@ -0,0 +1,19 @@
import { Modal, ModalContent } from "@app/components/v2";
import { KmipClientCertificate } from "@app/hooks/api/kmip/types";
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificatesTab/components/CertificateContent";
type Props = {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
certificate: KmipClientCertificate;
};
export const KmipClientCertificateModal = ({ isOpen, onOpenChange, certificate }: Props) => {
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent title="KMIP client certificate">
<CertificateContent {...certificate} />
</ModalContent>
</Modal>
);
};
@@ -2,6 +2,7 @@ import {
faArrowDown,
faArrowUp,
faArrowUpRightFromSquare,
faCertificate,
faEdit,
faEllipsis,
faMagnifyingGlass,
@@ -45,7 +46,9 @@ import { OrderByDirection } from "@app/hooks/api/generic/types";
import { useGetKmipClientsByProjectId } from "@app/hooks/api/kmip";
import { KmipClientOrderBy, TKmipClient } from "@app/hooks/api/kmip/types";
import { CreateKmipClientCertificateModal } from "./CreateKmipClientCertificateModal";
import { DeleteKmipClientModal } from "./DeleteKmipClientModal";
import { KmipClientCertificateModal } from "./KmipClientCertificateModal";
import { KmipClientModal } from "./KmipClientModal";
export const KmipClientTable = () => {
@@ -88,7 +91,9 @@ export const KmipClientTable = () => {
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"upsertKmipClient",
"deleteKmipClient"
"deleteKmipClient",
"generateKmipClientCert",
"displayKmipClientCert"
] as const);
const handleSort = () => {
@@ -107,6 +112,11 @@ export const KmipClientTable = () => {
ProjectPermissionSub.Kmip
);
const cannotGenerateKmipClientCertificate = permission.cannot(
ProjectPermissionKmipActions.GenerateClientCertificates,
ProjectPermissionSub.Kmip
);
return (
<motion.div
key="kmip-clients-tab"
@@ -206,6 +216,25 @@ export const KmipClientTable = () => {
</IconButton>
</DropdownMenuTrigger>
<DropdownMenuContent className="min-w-[160px]">
<Tooltip
content={
cannotGenerateKmipClientCertificate ? "Access Restricted" : ""
}
position="left"
>
<div>
<DropdownMenuItem
onClick={() =>
handlePopUpOpen("generateKmipClientCert", kmipClient)
}
icon={<FontAwesomeIcon icon={faCertificate} />}
iconPos="left"
isDisabled={cannotGenerateKmipClientCertificate}
>
Generate Certificate
</DropdownMenuItem>
</div>
</Tooltip>
<Tooltip
content={cannotEditKmipClient ? "Access Restricted" : ""}
position="left"
@@ -274,6 +303,19 @@ export const KmipClientTable = () => {
onOpenChange={(isOpen) => handlePopUpToggle("upsertKmipClient", isOpen)}
kmipClient={popUp.upsertKmipClient.data as TKmipClient | null}
/>
<CreateKmipClientCertificateModal
isOpen={popUp.generateKmipClientCert.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("generateKmipClientCert", isOpen)}
kmipClient={popUp.generateKmipClientCert.data as TKmipClient | null}
displayNewClientCertificate={(certificate) =>
handlePopUpOpen("displayKmipClientCert", certificate)
}
/>
<KmipClientCertificateModal
isOpen={popUp.displayKmipClientCert.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("displayKmipClientCert", isOpen)}
certificate={popUp.displayKmipClientCert.data}
/>
</div>
</motion.div>
);