feat: added support for custom extended key usages

This commit is contained in:
Sheen Capadngan
2024-09-11 22:38:36 +08:00
parent c1f61f2db4
commit a8759e7410
14 changed files with 306 additions and 45 deletions
@@ -33,7 +33,12 @@ import {
useListWorkspacePkiCollections
} from "@app/hooks/api";
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
import { CertKeyUsage, KEY_USAGES_OPTIONS } from "@app/hooks/api/certificates/types";
import {
CertExtendedKeyUsage,
CertKeyUsage,
EXTENDED_KEY_USAGES_OPTIONS,
KEY_USAGES_OPTIONS
} from "@app/hooks/api/certificates/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { CertificateContent } from "./CertificateContent";
@@ -56,6 +61,14 @@ const schema = z.object({
[CertKeyUsage.CRL_SIGN]: z.boolean().optional(),
[CertKeyUsage.ENCIPHER_ONLY]: z.boolean().optional(),
[CertKeyUsage.DECIPHER_ONLY]: z.boolean().optional()
}),
extendedKeyUsages: z.object({
[CertExtendedKeyUsage.CLIENT_AUTH]: z.boolean().optional(),
[CertExtendedKeyUsage.CODE_SIGNING]: z.boolean().optional(),
[CertExtendedKeyUsage.EMAIL_PROTECTION]: z.boolean().optional(),
[CertExtendedKeyUsage.OCSP_SIGNING]: z.boolean().optional(),
[CertExtendedKeyUsage.SERVER_AUTH]: z.boolean().optional(),
[CertExtendedKeyUsage.TIMESTAMPING]: z.boolean().optional()
})
});
@@ -110,7 +123,8 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
keyUsages: {
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
[CertKeyUsage.KEY_ENCIPHERMENT]: true
}
},
extendedKeyUsages: {}
}
});
@@ -131,7 +145,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
altNames: cert.altNames,
certificateTemplateId: cert.certificateTemplateId ?? CERT_TEMPLATE_NONE_VALUE,
ttl: "",
keyUsages: Object.fromEntries(cert.keyUsages.map((name) => [name, true]))
keyUsages: Object.fromEntries((cert.keyUsages || []).map((name) => [name, true])),
extendedKeyUsages: Object.fromEntries(
(cert.extendedKeyUsages || []).map((name) => [name, true])
)
});
} else {
reset({
@@ -144,7 +161,8 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
keyUsages: {
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
[CertKeyUsage.KEY_ENCIPHERMENT]: true
}
},
extendedKeyUsages: {}
});
}
}, [cert]);
@@ -156,6 +174,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
"keyUsages",
Object.fromEntries(selectedCertTemplate.keyUsages.map((name) => [name, true]))
);
setValue(
"extendedKeyUsages",
Object.fromEntries(selectedCertTemplate.extendedKeyUsages.map((name) => [name, true]))
);
}
}, [selectedCertTemplate, cert]);
@@ -166,7 +188,8 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
commonName,
altNames,
ttl,
keyUsages
keyUsages,
extendedKeyUsages
}: FormData) => {
try {
if (!currentWorkspace?.slug) return;
@@ -182,7 +205,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
ttl,
keyUsages: Object.entries(keyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertKeyUsage)
.map(([key]) => key as CertKeyUsage),
extendedKeyUsages: Object.entries(extendedKeyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertExtendedKeyUsage)
});
reset();
@@ -440,6 +466,41 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
);
}}
/>
<Controller
control={control}
name="extendedKeyUsages"
render={({ field: { onChange, value }, fieldState: { error } }) => {
return (
<FormControl
label="Extended Key Usages"
errorText={error?.message}
isError={Boolean(error)}
>
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
{EXTENDED_KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
return (
<Checkbox
id={optionValue}
key={optionValue}
className="data-[state=checked]:bg-primary"
isDisabled={Boolean(cert)}
isChecked={value[optionValue]}
onCheckedChange={(state) => {
onChange({
...value,
[optionValue]: state
});
}}
>
{label}
</Checkbox>
);
})}
</div>
</FormControl>
);
}}
/>
</AccordionContent>
</AccordionItem>
</Accordion>
@@ -33,7 +33,12 @@ import {
useUpdateCertTemplate
} from "@app/hooks/api";
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
import { CertKeyUsage, KEY_USAGES_OPTIONS } from "@app/hooks/api/certificates/types";
import {
CertExtendedKeyUsage,
CertKeyUsage,
EXTENDED_KEY_USAGES_OPTIONS,
KEY_USAGES_OPTIONS
} from "@app/hooks/api/certificates/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
const validateTemplateRegexField = z
@@ -63,6 +68,14 @@ const schema = z.object({
[CertKeyUsage.CRL_SIGN]: z.boolean().optional(),
[CertKeyUsage.ENCIPHER_ONLY]: z.boolean().optional(),
[CertKeyUsage.DECIPHER_ONLY]: z.boolean().optional()
}),
extendedKeyUsages: z.object({
[CertExtendedKeyUsage.CLIENT_AUTH]: z.boolean().optional(),
[CertExtendedKeyUsage.CODE_SIGNING]: z.boolean().optional(),
[CertExtendedKeyUsage.EMAIL_PROTECTION]: z.boolean().optional(),
[CertExtendedKeyUsage.OCSP_SIGNING]: z.boolean().optional(),
[CertExtendedKeyUsage.SERVER_AUTH]: z.boolean().optional(),
[CertExtendedKeyUsage.TIMESTAMPING]: z.boolean().optional()
})
});
@@ -122,7 +135,10 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
subjectAlternativeName: certTemplate.subjectAlternativeName,
collectionId: certTemplate.pkiCollectionId ?? undefined,
ttl: certTemplate.ttl,
keyUsages: Object.fromEntries(certTemplate.keyUsages.map((name) => [name, true]))
keyUsages: Object.fromEntries(certTemplate.keyUsages.map((name) => [name, true]) ?? []),
extendedKeyUsages: Object.fromEntries(
certTemplate.extendedKeyUsages.map((name) => [name, true]) ?? []
)
});
} else {
reset({
@@ -133,7 +149,8 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
keyUsages: {
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
[CertKeyUsage.KEY_ENCIPHERMENT]: true
}
},
extendedKeyUsages: {}
});
}
}, [certTemplate, ca]);
@@ -144,7 +161,8 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
commonName,
subjectAlternativeName,
ttl,
keyUsages
keyUsages,
extendedKeyUsages
}: FormData) => {
if (!currentWorkspace?.id) {
return;
@@ -163,7 +181,10 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
ttl,
keyUsages: Object.entries(keyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertKeyUsage)
.map(([key]) => key as CertKeyUsage),
extendedKeyUsages: Object.entries(extendedKeyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertExtendedKeyUsage)
});
createNotification({
@@ -181,7 +202,10 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
ttl,
keyUsages: Object.entries(keyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertKeyUsage)
.map(([key]) => key as CertKeyUsage),
extendedKeyUsages: Object.entries(extendedKeyUsages)
.filter(([, value]) => value)
.map(([key]) => key as CertExtendedKeyUsage)
});
createNotification({
@@ -408,6 +432,40 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
);
}}
/>
<Controller
control={control}
name="extendedKeyUsages"
render={({ field: { onChange, value }, fieldState: { error } }) => {
return (
<FormControl
label="Extended Key Usages"
errorText={error?.message}
isError={Boolean(error)}
>
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
{EXTENDED_KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
return (
<Checkbox
id={optionValue}
key={optionValue}
className="data-[state=checked]:bg-primary"
isChecked={value[optionValue]}
onCheckedChange={(state) => {
onChange({
...value,
[optionValue]: state
});
}}
>
{label}
</Checkbox>
);
})}
</div>
</FormControl>
);
}}
/>
</AccordionContent>
</AccordionItem>
</Accordion>