mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 11:38:55 +00:00
Merge pull request #2401 from Infisical/feat/add-key-usages-for-template-and-cert
feat: add support for configuring certificate key usage and extended key usage
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { CertKeyAlgorithm } from "../certificates/enums";
|
||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "../certificates/enums";
|
||||
import { CaRenewalType, CaStatus, CaType } from "./enums";
|
||||
|
||||
export type TCertificateAuthority = {
|
||||
@@ -91,6 +91,8 @@ export type TCreateCertificateDTO = {
|
||||
ttl: string; // string compatible with ms
|
||||
notBefore?: string;
|
||||
notAfter?: string;
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
};
|
||||
|
||||
export type TCreateCertificateResponse = {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CertExtendedKeyUsage, CertKeyUsage } from "../certificates/enums";
|
||||
|
||||
export type TCertificateTemplate = {
|
||||
id: string;
|
||||
caId: string;
|
||||
@@ -8,6 +10,8 @@ export type TCertificateTemplate = {
|
||||
commonName: string;
|
||||
subjectAlternativeName: string;
|
||||
ttl: string;
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
};
|
||||
|
||||
export type TCreateCertificateTemplateDTO = {
|
||||
@@ -18,6 +22,8 @@ export type TCreateCertificateTemplateDTO = {
|
||||
subjectAlternativeName: string;
|
||||
ttl: string;
|
||||
projectId: string;
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
};
|
||||
|
||||
export type TUpdateCertificateTemplateDTO = {
|
||||
@@ -29,6 +35,8 @@ export type TUpdateCertificateTemplateDTO = {
|
||||
subjectAlternativeName?: string;
|
||||
ttl?: string;
|
||||
projectId: string;
|
||||
keyUsages?: CertKeyUsage[];
|
||||
extendedKeyUsages?: CertExtendedKeyUsage[];
|
||||
};
|
||||
|
||||
export type TDeleteCertificateTemplateDTO = {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { CertKeyAlgorithm, CertStatus, CrlReason } from "./enums";
|
||||
import {
|
||||
CertExtendedKeyUsage,
|
||||
CertKeyAlgorithm,
|
||||
CertKeyUsage,
|
||||
CertStatus,
|
||||
CrlReason
|
||||
} from "./enums";
|
||||
|
||||
export const certStatusToNameMap: { [K in CertStatus]: string } = {
|
||||
[CertStatus.ACTIVE]: "Active",
|
||||
@@ -69,3 +75,24 @@ export const crlReasons = [
|
||||
},
|
||||
{ label: crlReasonToNameMap[CrlReason.A_A_COMPROMISE], value: CrlReason.A_A_COMPROMISE }
|
||||
];
|
||||
|
||||
export const KEY_USAGES_OPTIONS = [
|
||||
{ value: CertKeyUsage.DIGITAL_SIGNATURE, label: "Digital Signature" },
|
||||
{ value: CertKeyUsage.KEY_ENCIPHERMENT, label: "Key Encipherment" },
|
||||
{ value: CertKeyUsage.NON_REPUDIATION, label: "Non Repudiation" },
|
||||
{ value: CertKeyUsage.DATA_ENCIPHERMENT, label: "Data Encipherment" },
|
||||
{ value: CertKeyUsage.KEY_AGREEMENT, label: "Key Agreement" },
|
||||
{ value: CertKeyUsage.KEY_CERT_SIGN, label: "Certificate Sign" },
|
||||
{ value: CertKeyUsage.CRL_SIGN, label: "CRL Sign" },
|
||||
{ value: CertKeyUsage.ENCIPHER_ONLY, label: "Encipher Only" },
|
||||
{ value: CertKeyUsage.DECIPHER_ONLY, label: "Decipher Only" }
|
||||
] as const;
|
||||
|
||||
export const EXTENDED_KEY_USAGES_OPTIONS = [
|
||||
{ value: CertExtendedKeyUsage.CLIENT_AUTH, label: "Client Auth" },
|
||||
{ value: CertExtendedKeyUsage.SERVER_AUTH, label: "Server Auth" },
|
||||
{ value: CertExtendedKeyUsage.EMAIL_PROTECTION, label: "Email Protection" },
|
||||
{ value: CertExtendedKeyUsage.OCSP_SIGNING, label: "OCSP Signing" },
|
||||
{ value: CertExtendedKeyUsage.CODE_SIGNING, label: "Code Signing" },
|
||||
{ value: CertExtendedKeyUsage.TIMESTAMPING, label: "Timestamping" }
|
||||
] as const;
|
||||
|
||||
@@ -22,3 +22,24 @@ export enum CrlReason {
|
||||
PRIVILEGE_WITHDRAWN = "PRIVILEGE_WITHDRAWN",
|
||||
A_A_COMPROMISE = "A_A_COMPROMISE"
|
||||
}
|
||||
|
||||
export enum CertKeyUsage {
|
||||
DIGITAL_SIGNATURE = "digitalSignature",
|
||||
KEY_ENCIPHERMENT = "keyEncipherment",
|
||||
NON_REPUDIATION = "nonRepudiation",
|
||||
DATA_ENCIPHERMENT = "dataEncipherment",
|
||||
KEY_AGREEMENT = "keyAgreement",
|
||||
KEY_CERT_SIGN = "keyCertSign",
|
||||
CRL_SIGN = "cRLSign",
|
||||
ENCIPHER_ONLY = "encipherOnly",
|
||||
DECIPHER_ONLY = "decipherOnly"
|
||||
}
|
||||
|
||||
export enum CertExtendedKeyUsage {
|
||||
CLIENT_AUTH = "clientAuth",
|
||||
SERVER_AUTH = "serverAuth",
|
||||
CODE_SIGNING = "codeSigning",
|
||||
EMAIL_PROTECTION = "emailProtection",
|
||||
TIMESTAMPING = "timeStamping",
|
||||
OCSP_SIGNING = "ocspSigning"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CertStatus } from "./enums";
|
||||
import { CertExtendedKeyUsage, CertKeyUsage, CertStatus } from "./enums";
|
||||
|
||||
export type TCertificate = {
|
||||
id: string;
|
||||
@@ -11,6 +11,8 @@ export type TCertificate = {
|
||||
serialNumber: string;
|
||||
notBefore: string;
|
||||
notAfter: string;
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
};
|
||||
|
||||
export type TDeleteCertDTO = {
|
||||
|
||||
+147
-7
@@ -7,7 +7,12 @@ import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
@@ -28,6 +33,11 @@ import {
|
||||
useListWorkspacePkiCollections
|
||||
} from "@app/hooks/api";
|
||||
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||
import {
|
||||
EXTENDED_KEY_USAGES_OPTIONS,
|
||||
KEY_USAGES_OPTIONS
|
||||
} from "@app/hooks/api/certificates/constants";
|
||||
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/hooks/api/certificates/enums";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
import { CertificateContent } from "./CertificateContent";
|
||||
@@ -39,7 +49,26 @@ const schema = z.object({
|
||||
friendlyName: z.string(),
|
||||
commonName: z.string().trim().min(1),
|
||||
altNames: z.string(),
|
||||
ttl: z.string().trim()
|
||||
ttl: z.string().trim(),
|
||||
keyUsages: z.object({
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.NON_REPUDIATION]: z.boolean().optional(),
|
||||
[CertKeyUsage.DATA_ENCIPHERMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_AGREEMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_CERT_SIGN]: z.boolean().optional(),
|
||||
[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()
|
||||
})
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof schema>;
|
||||
@@ -88,7 +117,14 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
setValue,
|
||||
watch
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema)
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
keyUsages: {
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||
},
|
||||
extendedKeyUsages: {}
|
||||
}
|
||||
});
|
||||
|
||||
const selectedCertTemplateId = watch("certificateTemplateId");
|
||||
@@ -107,7 +143,11 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames,
|
||||
certificateTemplateId: cert.certificateTemplateId ?? CERT_TEMPLATE_NONE_VALUE,
|
||||
ttl: ""
|
||||
ttl: "",
|
||||
keyUsages: Object.fromEntries((cert.keyUsages || []).map((name) => [name, true])),
|
||||
extendedKeyUsages: Object.fromEntries(
|
||||
(cert.extendedKeyUsages || []).map((name) => [name, true])
|
||||
)
|
||||
});
|
||||
} else {
|
||||
reset({
|
||||
@@ -116,7 +156,12 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
commonName: "",
|
||||
altNames: "",
|
||||
ttl: "",
|
||||
certificateTemplateId: CERT_TEMPLATE_NONE_VALUE
|
||||
certificateTemplateId: CERT_TEMPLATE_NONE_VALUE,
|
||||
keyUsages: {
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||
},
|
||||
extendedKeyUsages: {}
|
||||
});
|
||||
}
|
||||
}, [cert]);
|
||||
@@ -124,6 +169,14 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
useEffect(() => {
|
||||
if (!cert && selectedCertTemplate) {
|
||||
setValue("ttl", selectedCertTemplate.ttl);
|
||||
setValue(
|
||||
"keyUsages",
|
||||
Object.fromEntries(selectedCertTemplate.keyUsages.map((name) => [name, true]))
|
||||
);
|
||||
setValue(
|
||||
"extendedKeyUsages",
|
||||
Object.fromEntries(selectedCertTemplate.extendedKeyUsages.map((name) => [name, true]))
|
||||
);
|
||||
}
|
||||
}, [selectedCertTemplate, cert]);
|
||||
|
||||
@@ -133,7 +186,9 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
collectionId,
|
||||
commonName,
|
||||
altNames,
|
||||
ttl
|
||||
ttl,
|
||||
keyUsages,
|
||||
extendedKeyUsages
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentWorkspace?.slug) return;
|
||||
@@ -146,7 +201,13 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
friendlyName,
|
||||
commonName,
|
||||
altNames,
|
||||
ttl
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertKeyUsage),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertExtendedKeyUsage)
|
||||
});
|
||||
|
||||
reset();
|
||||
@@ -363,8 +424,87 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="key-usages" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">Key Usage</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Controller
|
||||
control={control}
|
||||
name="keyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
|
||||
{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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="extendedKeyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Extended Key Usage"
|
||||
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>
|
||||
{!cert && (
|
||||
<div className="flex items-center">
|
||||
<div className="mt-4 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
|
||||
+147
-11
@@ -7,7 +7,12 @@ import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
@@ -25,8 +30,14 @@ import {
|
||||
useGetCertTemplate,
|
||||
useListWorkspaceCas,
|
||||
useListWorkspacePkiCollections,
|
||||
useUpdateCertTemplate} from "@app/hooks/api";
|
||||
useUpdateCertTemplate
|
||||
} from "@app/hooks/api";
|
||||
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||
import {
|
||||
EXTENDED_KEY_USAGES_OPTIONS,
|
||||
KEY_USAGES_OPTIONS
|
||||
} from "@app/hooks/api/certificates/constants";
|
||||
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/hooks/api/certificates/enums";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const validateTemplateRegexField = z
|
||||
@@ -45,7 +56,26 @@ const schema = z.object({
|
||||
name: z.string().min(1),
|
||||
commonName: validateTemplateRegexField,
|
||||
subjectAlternativeName: validateTemplateRegexField,
|
||||
ttl: z.string().trim().min(1)
|
||||
ttl: z.string().trim().min(1),
|
||||
keyUsages: z.object({
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.NON_REPUDIATION]: z.boolean().optional(),
|
||||
[CertKeyUsage.DATA_ENCIPHERMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_AGREEMENT]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_CERT_SIGN]: z.boolean().optional(),
|
||||
[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()
|
||||
})
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof schema>;
|
||||
@@ -61,9 +91,9 @@ type Props = {
|
||||
|
||||
export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
|
||||
const { data: ca } = useGetCaById(caId);
|
||||
|
||||
|
||||
const { data: certTemplate } = useGetCertTemplate(
|
||||
(popUp?.certificateTemplate?.data as { id: string })?.id || ""
|
||||
);
|
||||
@@ -86,7 +116,13 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
reset,
|
||||
formState: { isSubmitting }
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema)
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
keyUsages: {
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -97,14 +133,23 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
commonName: certTemplate.commonName,
|
||||
subjectAlternativeName: certTemplate.subjectAlternativeName,
|
||||
collectionId: certTemplate.pkiCollectionId ?? undefined,
|
||||
ttl: certTemplate.ttl
|
||||
ttl: certTemplate.ttl,
|
||||
keyUsages: Object.fromEntries(certTemplate.keyUsages.map((name) => [name, true]) ?? []),
|
||||
extendedKeyUsages: Object.fromEntries(
|
||||
certTemplate.extendedKeyUsages.map((name) => [name, true]) ?? []
|
||||
)
|
||||
});
|
||||
} else {
|
||||
reset({
|
||||
caId,
|
||||
name: "",
|
||||
commonName: "",
|
||||
ttl: ""
|
||||
ttl: "",
|
||||
keyUsages: {
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||
},
|
||||
extendedKeyUsages: {}
|
||||
});
|
||||
}
|
||||
}, [certTemplate, ca]);
|
||||
@@ -114,7 +159,9 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl
|
||||
ttl,
|
||||
keyUsages,
|
||||
extendedKeyUsages
|
||||
}: FormData) => {
|
||||
if (!currentWorkspace?.id) {
|
||||
return;
|
||||
@@ -130,7 +177,13 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertKeyUsage),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertExtendedKeyUsage)
|
||||
});
|
||||
|
||||
createNotification({
|
||||
@@ -145,7 +198,13 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertKeyUsage),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertExtendedKeyUsage)
|
||||
});
|
||||
|
||||
createNotification({
|
||||
@@ -332,7 +391,84 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="key-usages" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">Key Usage</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Controller
|
||||
control={control}
|
||||
name="keyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
|
||||
{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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="extendedKeyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Extended Key Usage"
|
||||
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>
|
||||
<div className="mt-4 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
|
||||
Reference in New Issue
Block a user