Changed cert-manager overview tabs to be proper routes

This commit is contained in:
x
2025-04-21 16:16:47 -04:00
parent eba12912f8
commit 6a751e720c
48 changed files with 219 additions and 116 deletions
@@ -0,0 +1,75 @@
import { useEffect, useState } from "react";
import { FormControl, Modal, ModalContent, Select, SelectItem } from "@app/components/v2";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { ExternalCaInstallForm } from "./ExternalCaInstallForm";
import { InternalCaInstallForm } from "./InternalCaInstallForm";
type Props = {
popUp: UsePopUpState<["installCaCert"]>;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["installCaCert"]>, state?: boolean) => void;
};
enum ParentCaType {
Internal = "internal",
External = "external"
}
export const CaInstallCertModal = ({ popUp, handlePopUpToggle }: Props) => {
const popupData = popUp?.installCaCert?.data;
const caId = popupData?.caId || "";
const isParentCaExternal = popupData?.isParentCaExternal || false;
const [parentCaType, setParentCaType] = useState<ParentCaType>(ParentCaType.Internal);
useEffect(() => {
if (popupData?.isParentCaExternal) {
setParentCaType(ParentCaType.External);
}
}, [popupData]);
const renderForm = (parentCaTypeInput: ParentCaType) => {
switch (parentCaTypeInput) {
case ParentCaType.Internal:
return <InternalCaInstallForm caId={caId} handlePopUpToggle={handlePopUpToggle} />;
default:
return <ExternalCaInstallForm caId={caId} handlePopUpToggle={handlePopUpToggle} />;
}
};
return (
<Modal
isOpen={popUp?.installCaCert?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("installCaCert", isOpen);
}}
>
<ModalContent
title={`${isParentCaExternal ? "Renew" : "Install"} Intermediate CA certificate`}
>
<FormControl label="Parent CA Type">
<Select
value={parentCaType}
onValueChange={(e) => setParentCaType(e as ParentCaType)}
className="w-full"
isDisabled={isParentCaExternal}
>
<SelectItem
value={ParentCaType.Internal}
key={`parent-ca-type-${ParentCaType.Internal}`}
>
Infisical CA
</SelectItem>
<SelectItem
value={ParentCaType.External}
key={`parent-ca-type-${ParentCaType.External}`}
>
External CA
</SelectItem>
</Select>
</FormControl>
{renderForm(parentCaType)}
</ModalContent>
</Modal>
);
};
@@ -0,0 +1,172 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { faCheck, faCopy, faDownload } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import FileSaver from "file-saver";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, IconButton, TextArea, Tooltip } from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useGetCaCsr, useImportCaCertificate } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = z.object({
certificate: z.string().min(1),
certificateChain: z.string().min(1)
});
export type FormData = z.infer<typeof schema>;
type Props = {
caId: string;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["installCaCert"]>, state?: boolean) => void;
};
export const ExternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace();
const [copyTextCaCsr, isCopyingCaCsr, setCopyTextCaCsr] = useTimedReset<string>({
initialState: "Copy to clipboard"
});
const {
control,
handleSubmit,
reset,
formState: { isSubmitting }
} = useForm<FormData>({
resolver: zodResolver(schema)
});
const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: importCaCertificate } = useImportCaCertificate();
useEffect(() => {
reset();
}, []);
const onFormSubmit = async ({ certificate, certificateChain }: FormData) => {
try {
if (!csr || !caId || !currentWorkspace?.slug) return;
await importCaCertificate({
caId,
projectSlug: currentWorkspace?.slug,
certificate,
certificateChain
});
reset();
createNotification({
text: "Successfully installed certificate for CA",
type: "success"
});
handlePopUpToggle("installCaCert", false);
} catch {
createNotification({
text: "Failed to install certificate for CA",
type: "error"
});
}
};
const downloadTxtFile = (filename: string, content: string) => {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
FileSaver.saveAs(blob, filename);
};
return (
<form onSubmit={handleSubmit(onFormSubmit)}>
{csr && (
<>
<div className="my-4 flex items-center justify-between">
<h2>CSR for this CA</h2>
<div className="flex">
<Tooltip content={copyTextCaCsr}>
<IconButton
ariaLabel="copy icon"
colorSchema="secondary"
className="group relative"
onClick={() => {
navigator.clipboard.writeText(csr);
setCopyTextCaCsr("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingCaCsr ? faCheck : faCopy} />
</IconButton>
</Tooltip>
<Tooltip content="Download">
<IconButton
ariaLabel="copy icon"
colorSchema="secondary"
className="group relative ml-2"
onClick={() => {
downloadTxtFile("csr.pem", csr);
}}
>
<FontAwesomeIcon icon={faDownload} />
</IconButton>
</Tooltip>
</div>
</div>
<div className="mb-8 flex items-center justify-between rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
<p className="mr-4 whitespace-pre-wrap break-all">{csr}</p>
</div>
</>
)}
<Controller
control={control}
name="certificate"
render={({ field, fieldState: { error } }) => (
<FormControl label="Certificate Body" errorText={error?.message} isError={Boolean(error)}>
<TextArea
{...field}
placeholder="PEM-encoded certificate..."
reSize="none"
className="h-48"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="certificateChain"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Certificate Chain"
errorText={error?.message}
isError={Boolean(error)}
>
<TextArea
{...field}
placeholder="PEM-encoded certificate chain..."
reSize="none"
className="h-48"
/>
</FormControl>
)}
/>
<div className="flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
Install
</Button>
<Button
colorSchema="secondary"
variant="plain"
onClick={() => handlePopUpToggle("installCaCert", false)}
>
Cancel
</Button>
</div>
</form>
);
};
@@ -0,0 +1,236 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { format } from "date-fns";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2";
import { useWorkspace } from "@app/context";
import {
CaStatus,
useGetCaById,
useGetCaCsr,
useImportCaCertificate,
useListWorkspaceCas,
useSignIntermediate
} from "@app/hooks/api";
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp";
const isValidDate = (dateString: string) => {
const date = new Date(dateString);
return !Number.isNaN(date.getTime());
};
const getMiddleDate = (date1: Date, date2: Date) => {
const timestamp1 = date1.getTime();
const timestamp2 = date2.getTime();
const middleTimestamp = (timestamp1 + timestamp2) / 2;
return new Date(middleTimestamp);
};
const schema = z.object({
parentCaId: z.string(),
notAfter: z.string().trim().refine(isValidDate, { message: "Invalid date format" }),
maxPathLength: z.string()
});
export type FormData = z.infer<typeof schema>;
type Props = {
caId: string;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["installCaCert"]>, state?: boolean) => void;
};
export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace();
const { data: cas } = useListWorkspaceCas({
projectSlug: currentWorkspace?.slug ?? "",
status: CaStatus.ACTIVE
});
const { data: ca } = useGetCaById(caId);
const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: signIntermediate } = useSignIntermediate();
const { mutateAsync: importCaCertificate } = useImportCaCertificate();
const {
control,
handleSubmit,
reset,
formState: { isSubmitting },
setValue,
watch
} = useForm<FormData>({
resolver: zodResolver(schema),
defaultValues: {
maxPathLength: "0"
}
});
useEffect(() => {
reset();
}, []);
useEffect(() => {
if (cas?.length) {
setValue("parentCaId", cas[0].id);
}
}, [cas, setValue]);
const parentCaId = watch("parentCaId");
const { data: parentCa } = useGetCaById(parentCaId);
useEffect(() => {
if (parentCa?.maxPathLength) {
setValue(
"maxPathLength",
(parentCa.maxPathLength === -1 ? 3 : parentCa.maxPathLength - 1).toString()
);
}
if (parentCa?.notAfter) {
const parentCaNotAfter = new Date(parentCa.notAfter);
const middleDate = getMiddleDate(new Date(), parentCaNotAfter);
setValue("notAfter", format(middleDate, "yyyy-MM-dd"));
}
}, [parentCa]);
const onFormSubmit = async ({ notAfter, maxPathLength }: FormData) => {
try {
if (!csr || !caId || !currentWorkspace?.slug) return;
const { certificate, certificateChain } = await signIntermediate({
caId: parentCaId,
csr,
maxPathLength: Number(maxPathLength),
notAfter,
notBefore: new Date().toISOString()
});
await importCaCertificate({
caId,
projectSlug: currentWorkspace?.slug,
certificate,
certificateChain
});
reset();
createNotification({
text: "Successfully installed certificate for CA",
type: "success"
});
handlePopUpToggle("installCaCert", false);
} catch {
createNotification({
text: "Failed to install certificate for CA",
type: "error"
});
}
};
function generatePathLengthOpts(parentCaMaxPathLength: number): number[] {
if (parentCaMaxPathLength === -1) {
return [-1, 0, 1, 2, 3];
}
return Array.from({ length: parentCaMaxPathLength }, (_, index) => index);
}
return (
<form onSubmit={handleSubmit(onFormSubmit)}>
<Controller
control={control}
name="parentCaId"
// defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Parent CA"
errorText={error?.message}
isError={Boolean(error)}
isRequired
>
<Select
// defaultValue={field.value}
{...field}
onValueChange={onChange}
className="w-full"
>
{(cas || [])
.filter((c) => {
const isParentCaNotSelf = c.id !== ca?.id;
const isParentCaActive = c.status === CaStatus.ACTIVE;
const isParentCaAllowedChildrenCas = c.maxPathLength && c.maxPathLength !== 0;
return isParentCaNotSelf && isParentCaActive && isParentCaAllowedChildrenCas;
})
.map(({ id, type, dn }) => (
<SelectItem value={id} key={`parent-ca-${id}`}>
{`${caTypeToNameMap[type]}: ${dn}`}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="notAfter"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Valid Until"
isError={Boolean(error)}
errorText={error?.message}
isRequired
>
<Input {...field} placeholder="YYYY-MM-DD" />
</FormControl>
)}
/>
<Controller
control={control}
name="maxPathLength"
// defaultValue="0"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl label="Path Length" errorText={error?.message} isError={Boolean(error)}>
<Select
// defaultValue={field.value}
{...field}
onValueChange={onChange}
className="w-full"
>
{generatePathLengthOpts(parentCa?.maxPathLength || 0).map((value) => (
<SelectItem value={String(value)} key={`ca-path-length-${value}`}>
{`${value}`}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<div className="flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
Install
</Button>
<Button
colorSchema="secondary"
variant="plain"
onClick={() => handlePopUpToggle("installCaCert", false)}
>
Cancel
</Button>
</div>
</form>
);
};
@@ -0,0 +1 @@
export { CaInstallCertModal } from "./CaInstallCertModal";