misc: migrated internal CA to use new CA endpoint

This commit is contained in:
Sheen Capadngan
2025-05-22 04:21:54 +08:00
parent 77de085ffc
commit df75b3b8d3
22 changed files with 325 additions and 331 deletions
@@ -41,7 +41,7 @@ export const ExternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
});
const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: importCaCertificate } = useImportCaCertificate();
const { mutateAsync: importCaCertificate } = useImportCaCertificate(currentWorkspace.id);
useEffect(() => {
reset();
@@ -55,7 +55,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: signIntermediate } = useSignIntermediate();
const { mutateAsync: importCaCertificate } = useImportCaCertificate();
const { mutateAsync: importCaCertificate } = useImportCaCertificate(currentWorkspace.id);
const {
control,
@@ -17,10 +17,18 @@ import {
// DatePicker
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { InternalCaType, useCreateCa, useGetCaById, useUpdateCa } from "@app/hooks/api/ca";
import {
CaStatus,
CaType,
InternalCaType,
useCreateCa,
useGetCa,
useUpdateCa
} from "@app/hooks/api/ca";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { slugSchema } from "@app/lib/schemas";
const isValidDate = (dateString: string) => {
const date = new Date(dateString);
@@ -35,23 +43,31 @@ const getDateTenYearsFromToday = () => {
const schema = z
.object({
type: z.enum([InternalCaType.ROOT, InternalCaType.INTERMEDIATE]),
friendlyName: z.string(),
organization: z.string(),
ou: z.string(),
country: z.string(),
province: z.string(),
locality: z.string(),
commonName: z.string(),
notAfter: z.string().trim().refine(isValidDate, { message: "Invalid date format" }),
maxPathLength: z.string(),
keyAlgorithm: z.enum([
CertKeyAlgorithm.RSA_2048,
CertKeyAlgorithm.RSA_4096,
CertKeyAlgorithm.ECDSA_P256,
CertKeyAlgorithm.ECDSA_P384
]),
requireTemplateForIssuance: z.boolean()
type: z.nativeEnum(CaType),
name: slugSchema({
field: "Name"
}),
enableDirectIssuance: z.boolean(),
status: z.nativeEnum(CaStatus),
configuration: z
.object({
type: z.enum([InternalCaType.ROOT, InternalCaType.INTERMEDIATE]),
organization: z.string(),
ou: z.string(),
country: z.string(),
province: z.string(),
locality: z.string(),
commonName: z.string(),
notAfter: z.string().trim().refine(isValidDate, { message: "Invalid date format" }),
maxPathLength: z.string(),
keyAlgorithm: z.enum([
CertKeyAlgorithm.RSA_2048,
CertKeyAlgorithm.RSA_4096,
CertKeyAlgorithm.ECDSA_P256,
CertKeyAlgorithm.ECDSA_P384
])
})
.required()
})
.required();
@@ -69,9 +85,11 @@ const caTypes = [
export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace();
// const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false);
const { data: ca } = useGetCaById((popUp?.ca?.data as { caId: string })?.caId || "");
const { data: ca } = useGetCa({
caName: (popUp?.ca?.data as { name: string })?.name || "",
projectId: currentWorkspace?.id || "",
type: CaType.INTERNAL
});
const { mutateAsync: createMutateAsync } = useCreateCa();
const { mutateAsync: updateMutateAsync } = useUpdateCa();
@@ -85,42 +103,12 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
} = useForm<FormData>({
resolver: zodResolver(schema),
defaultValues: {
type: InternalCaType.ROOT,
friendlyName: "",
organization: "",
ou: "",
country: "",
province: "",
locality: "",
commonName: "",
notAfter: getDateTenYearsFromToday(),
maxPathLength: "-1",
keyAlgorithm: CertKeyAlgorithm.RSA_2048
}
});
const caType = watch("type");
useEffect(() => {
if (ca) {
reset({
type: ca.type,
friendlyName: ca.friendlyName,
organization: ca.organization,
ou: ca.ou,
country: ca.country,
province: ca.province,
locality: ca.locality,
commonName: ca.commonName,
notAfter: ca.notAfter ? format(new Date(ca.notAfter), "yyyy-MM-dd") : "",
maxPathLength: ca.maxPathLength ? String(ca.maxPathLength) : "",
keyAlgorithm: ca.keyAlgorithm,
requireTemplateForIssuance: ca.requireTemplateForIssuance
});
} else {
reset({
type: CaType.INTERNAL,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: true,
configuration: {
type: InternalCaType.ROOT,
friendlyName: "",
organization: "",
ou: "",
country: "",
@@ -129,25 +117,65 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
commonName: "",
notAfter: getDateTenYearsFromToday(),
maxPathLength: "-1",
keyAlgorithm: CertKeyAlgorithm.RSA_2048,
requireTemplateForIssuance: true
keyAlgorithm: CertKeyAlgorithm.RSA_2048
}
}
});
const caType = watch("configuration.type");
useEffect(() => {
if (ca && ca.type === CaType.INTERNAL) {
reset({
type: ca.type,
name: ca.name,
status: ca.status,
enableDirectIssuance: ca.enableDirectIssuance,
configuration: {
type: ca.configuration.type,
organization: ca.configuration.organization,
ou: ca.configuration.ou,
country: ca.configuration.country,
province: ca.configuration.province,
locality: ca.configuration.locality,
commonName: ca.configuration.commonName,
notAfter: ca.configuration.notAfter
? format(new Date(ca.configuration.notAfter), "yyyy-MM-dd")
: "",
maxPathLength: ca.configuration.maxPathLength
? String(ca.configuration.maxPathLength)
: "",
keyAlgorithm: ca.configuration.keyAlgorithm
}
});
} else {
reset({
type: CaType.INTERNAL,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: true,
configuration: {
type: InternalCaType.ROOT,
organization: "",
ou: "",
country: "",
province: "",
locality: "",
commonName: "",
notAfter: getDateTenYearsFromToday(),
maxPathLength: "-1",
keyAlgorithm: CertKeyAlgorithm.RSA_2048
}
});
}
}, [ca]);
const onFormSubmit = async ({
type,
friendlyName,
commonName,
organization,
ou,
country,
locality,
province,
notAfter,
maxPathLength,
keyAlgorithm,
requireTemplateForIssuance
name,
enableDirectIssuance,
status,
configuration
}: FormData) => {
try {
if (!currentWorkspace?.slug) return;
@@ -155,26 +183,29 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (ca) {
// update
await updateMutateAsync({
projectSlug: currentWorkspace.slug,
caId: ca.id,
requireTemplateForIssuance
caName: ca.name,
projectId: currentWorkspace.id,
name,
type: CaType.INTERNAL,
status,
enableDirectIssuance,
configuration: {
...configuration,
maxPathLength: Number(configuration.maxPathLength)
}
});
} else {
// create
await createMutateAsync({
projectSlug: currentWorkspace.slug,
projectId: currentWorkspace.id,
name,
type,
friendlyName,
commonName,
organization,
ou,
country,
province,
locality,
notAfter,
maxPathLength: Number(maxPathLength),
keyAlgorithm,
requireTemplateForIssuance
status,
enableDirectIssuance,
configuration: {
...configuration,
maxPathLength: Number(configuration.maxPathLength)
}
});
}
@@ -211,7 +242,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
)}
<Controller
control={control}
name="type"
name="configuration.type"
defaultValue={InternalCaType.ROOT}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl label="CA Type" errorText={error?.message} isError={Boolean(error)}>
@@ -264,7 +295,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="notAfter"
name="configuration.notAfter"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Valid Until"
@@ -278,7 +309,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
/>
<Controller
control={control}
name="maxPathLength"
name="configuration.maxPathLength"
defaultValue="-1"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
@@ -307,7 +338,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
)}
<Controller
control={control}
name="keyAlgorithm"
name="configuration.keyAlgorithm"
defaultValue={CertKeyAlgorithm.RSA_2048}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
@@ -334,21 +365,22 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="friendlyName"
name="name"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Friendly Name"
label="Name"
isError={Boolean(error)}
errorText={error?.message}
isRequired
>
<Input {...field} placeholder="My CA" isDisabled={Boolean(ca)} />
<Input {...field} placeholder="my-internal-ca" isDisabled={Boolean(ca)} />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue=""
name="organization"
name="configuration.organization"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Organization (O)"
@@ -362,7 +394,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="ou"
name="configuration.ou"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Organization Unit (OU)"
@@ -376,7 +408,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="country"
name="configuration.country"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Country Name (C)"
@@ -390,7 +422,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="province"
name="configuration.province"
render={({ field, fieldState: { error } }) => (
<FormControl
label="State or Province Name"
@@ -404,7 +436,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="locality"
name="configuration.locality"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Locality Name"
@@ -418,7 +450,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller
control={control}
defaultValue=""
name="commonName"
name="configuration.commonName"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Common Name (CN)"
@@ -431,16 +463,16 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
/>
<Controller
control={control}
name="requireTemplateForIssuance"
name="enableDirectIssuance"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message} className="my-8">
<Switch
id="is-active"
id="enable-direct-issuance"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Require Template for Certificate Issuance</p>
<p className="w-full">Enable Direct Issuance</p>
</Switch>
</FormControl>
);
@@ -6,7 +6,7 @@ import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { CaStatus, useDeleteCa, useUpdateCa } from "@app/hooks/api";
import { CaStatus, CaType, useDeleteCa, useUpdateCa } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { CaCertModal } from "./CaCertModal";
@@ -28,11 +28,11 @@ export const CaSection = () => {
"upgradePlan"
] as const);
const onRemoveCaSubmit = async (caId: string) => {
const onRemoveCaSubmit = async (caName: string) => {
try {
if (!currentWorkspace?.slug) return;
await deleteCa({ caId, projectSlug: currentWorkspace.slug });
await deleteCa({ caName, projectId: currentWorkspace.id, type: CaType.INTERNAL });
createNotification({
text: "Successfully deleted CA",
@@ -48,11 +48,11 @@ export const CaSection = () => {
}
};
const onUpdateCaStatus = async ({ caId, status }: { caId: string; status: CaStatus }) => {
const onUpdateCaStatus = async ({ caName, status }: { caName: string; status: CaStatus }) => {
try {
if (!currentWorkspace?.slug) return;
await updateCa({ caId, projectSlug: currentWorkspace.slug, status });
await updateCa({ caName, projectId: currentWorkspace.id, type: CaType.INTERNAL, status });
createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -102,7 +102,9 @@ export const CaSection = () => {
subTitle="This action will delete other CAs and certificates below it in your CA hierarchy."
onChange={(isOpen) => handlePopUpToggle("deleteCa", isOpen)}
deleteKey="confirm"
onDeleteApproved={() => onRemoveCaSubmit((popUp?.deleteCa?.data as { caId: string })?.caId)}
onDeleteApproved={() =>
onRemoveCaSubmit((popUp?.deleteCa?.data as { caName: string })?.caName)
}
/>
<DeleteActionModal
isOpen={popUp.caStatus.isOpen}
@@ -119,7 +121,7 @@ export const CaSection = () => {
onChange={(isOpen) => handlePopUpToggle("caStatus", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onUpdateCaStatus(popUp?.caStatus?.data as { caId: string; status: CaStatus })
onUpdateCaStatus(popUp?.caStatus?.data as { caName: string; status: CaStatus })
}
/>
<UpgradePlanModal
@@ -23,12 +23,13 @@ import {
Tr
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { CaStatus, useListWorkspaceCas } from "@app/hooks/api";
import { CaStatus, CaType, useListCasByTypeAndProjectId } from "@app/hooks/api";
import {
caStatusToNameMap,
caTypeToNameMap,
getCaStatusBadgeVariant
} from "@app/hooks/api/ca/constants";
import { TInternalCertificateAuthority } from "@app/hooks/api/ca/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -39,6 +40,7 @@ type Props = {
>,
data?: {
caId?: string;
caName?: string;
dn?: string;
status?: CaStatus;
description?: string;
@@ -49,9 +51,8 @@ type Props = {
export const CaTable = ({ handlePopUpOpen }: Props) => {
const navigate = useNavigate();
const { currentWorkspace } = useWorkspace();
const { data, isPending } = useListWorkspaceCas({
projectSlug: currentWorkspace?.slug ?? ""
});
const { data, isPending } = useListCasByTypeAndProjectId(CaType.INTERNAL, currentWorkspace.id);
const cas = data as TInternalCertificateAuthority[];
return (
<div>
@@ -59,7 +60,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
<Table>
<THead>
<Tr>
<Th>Friendly Name</Th>
<Th>Name</Th>
<Th>Status</Th>
<Th>Type</Th>
<Th>Valid Until</Th>
@@ -69,33 +70,37 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
<TBody>
{isPending && <TableSkeleton columns={3} innerKey="project-cas" />}
{!isPending &&
data &&
data.length > 0 &&
data.map((ca) => {
cas &&
cas.length > 0 &&
cas.map((ca) => {
return (
<Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
key={`ca-${ca.id}`}
onClick={() =>
navigate({
to: `/${ProjectType.CertificateManager}/$projectId/ca/$caId` as const,
to: `/${ProjectType.CertificateManager}/$projectId/ca/$caName` as const,
params: {
projectId: currentWorkspace.id,
caId: ca.id
caName: ca.name
}
})
}
>
<Td>{ca.friendlyName}</Td>
<Td>{ca.name}</Td>
<Td>
<Badge variant={getCaStatusBadgeVariant(ca.status)}>
{caStatusToNameMap[ca.status]}
</Badge>
</Td>
<Td>{caTypeToNameMap[ca.type]}</Td>
<Td>{caTypeToNameMap[ca.configuration.type]}</Td>
<Td>
<div className="flex items-center">
<p>{ca.notAfter ? format(new Date(ca.notAfter), "yyyy-MM-dd") : "-"}</p>
<p>
{ca.configuration.notAfter
? format(new Date(ca.configuration.notAfter), "yyyy-MM-dd")
: "-"}
</p>
</div>
</Td>
<Td className="flex justify-end">
@@ -172,7 +177,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("caStatus", {
caId: ca.id,
caName: ca.name,
status:
ca.status === CaStatus.ACTIVE
? CaStatus.DISABLED
@@ -199,8 +204,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("deleteCa", {
caId: ca.id,
dn: ca.dn
caName: ca.name
});
}}
disabled={!isAllowed}
@@ -22,9 +22,9 @@ import {
AcmeDnsProvider,
CaStatus,
CaType,
useCreateUnifiedCa,
useCreateCa,
useGetCa,
useUpdateUnifiedCa
useUpdateCa
} from "@app/hooks/api/ca";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { slugSchema } from "@app/lib/schemas";
@@ -71,8 +71,8 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: (popUp?.ca?.data as { type: CaType })?.type || ""
});
const { mutateAsync: createMutateAsync } = useCreateUnifiedCa();
const { mutateAsync: updateMutateAsync } = useUpdateUnifiedCa();
const { mutateAsync: createMutateAsync } = useCreateCa();
const { mutateAsync: updateMutateAsync } = useUpdateCa();
const {
control,
@@ -6,7 +6,7 @@ import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { CaStatus, CaType, useDeleteUnifiedCa, useUpdateUnifiedCa } from "@app/hooks/api";
import { CaStatus, CaType, useDeleteCa, useUpdateCa } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { ExternalCaModal } from "./ExternalCaModal";
@@ -14,8 +14,8 @@ import { ExternalCaTable } from "./ExternalCaTable";
export const ExternalCaSection = () => {
const { currentWorkspace } = useWorkspace();
const { mutateAsync: deleteCa } = useDeleteUnifiedCa();
const { mutateAsync: updateCa } = useUpdateUnifiedCa();
const { mutateAsync: deleteCa } = useDeleteCa();
const { mutateAsync: updateCa } = useUpdateCa();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"ca",