mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 09:26:47 +00:00
misc: key usages setup
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
||||||
|
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.CertificateTemplate, (tb) => {
|
||||||
|
if (!hasKeyUsagesCol) {
|
||||||
|
tb.specificType("keyUsages", "text[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasExtendedKeyUsagesCol) {
|
||||||
|
tb.specificType("extendedKeyUsages", "text[]");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasKeyUsagesCol) {
|
||||||
|
await knex(TableName.CertificateTemplate).update({
|
||||||
|
keyUsages: [CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
||||||
|
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.CertificateTemplate, (t) => {
|
||||||
|
if (hasKeyUsagesCol) {
|
||||||
|
t.dropColumn("keyUsages");
|
||||||
|
}
|
||||||
|
if (hasExtendedKeyUsagesCol) {
|
||||||
|
t.dropColumn("extendedKeyUsages");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -16,7 +16,9 @@ export const CertificateTemplatesSchema = z.object({
|
|||||||
subjectAlternativeName: z.string(),
|
subjectAlternativeName: z.string(),
|
||||||
ttl: z.string(),
|
ttl: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
keyUsages: z.string().array().nullable().optional(),
|
||||||
|
extendedKeyUsages: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificateTemplates = z.infer<typeof CertificateTemplatesSchema>;
|
export type TCertificateTemplates = z.infer<typeof CertificateTemplatesSchema>;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
|||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema";
|
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema";
|
||||||
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
|
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
|
||||||
|
|
||||||
@@ -74,7 +75,12 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
|
|||||||
ttl: z
|
ttl: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl)
|
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl),
|
||||||
|
keyUsages: z
|
||||||
|
.nativeEnum(CertKeyUsage)
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
.default([CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT])
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: sanitizedCertificateTemplate
|
200: sanitizedCertificateTemplate
|
||||||
@@ -130,7 +136,8 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
|
|||||||
.string()
|
.string()
|
||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
.optional()
|
.optional()
|
||||||
.describe(CERTIFICATE_TEMPLATES.UPDATE.ttl)
|
.describe(CERTIFICATE_TEMPLATES.UPDATE.ttl),
|
||||||
|
keyUsages: z.nativeEnum(CertKeyUsage).array().optional()
|
||||||
}),
|
}),
|
||||||
params: z.object({
|
params: z.object({
|
||||||
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.UPDATE.certificateTemplateId)
|
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.UPDATE.certificateTemplateId)
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ export const certificateTemplateDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
return certTemplate;
|
if (certTemplate) {
|
||||||
|
return { ...certTemplate, keyUsages: certTemplate.keyUsages || [] };
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Get certificate template by ID" });
|
throw new DatabaseError({ error, name: "Get certificate template by ID" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const sanitizedCertificateTemplate = CertificateTemplatesSchema.pick({
|
|||||||
}).merge(
|
}).merge(
|
||||||
z.object({
|
z.object({
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
caName: z.string()
|
caName: z.string(),
|
||||||
|
keyUsages: z.string().array()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actor,
|
actor,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
keyUsages
|
||||||
}: TCreateCertTemplateDTO) => {
|
}: TCreateCertTemplateDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) {
|
if (!ca) {
|
||||||
@@ -86,7 +87,8 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
name,
|
name,
|
||||||
commonName,
|
commonName,
|
||||||
subjectAlternativeName,
|
subjectAlternativeName,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -113,7 +115,8 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actor,
|
actor,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
keyUsages
|
||||||
}: TUpdateCertTemplateDTO) => {
|
}: TUpdateCertTemplateDTO) => {
|
||||||
const certTemplate = await certificateTemplateDAL.getById(id);
|
const certTemplate = await certificateTemplateDAL.getById(id);
|
||||||
if (!certTemplate) {
|
if (!certTemplate) {
|
||||||
@@ -153,7 +156,8 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
commonName,
|
commonName,
|
||||||
subjectAlternativeName,
|
subjectAlternativeName,
|
||||||
name,
|
name,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
import { CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
|
|
||||||
export type TCreateCertTemplateDTO = {
|
export type TCreateCertTemplateDTO = {
|
||||||
caId: string;
|
caId: string;
|
||||||
@@ -7,6 +8,7 @@ export type TCreateCertTemplateDTO = {
|
|||||||
commonName: string;
|
commonName: string;
|
||||||
subjectAlternativeName: string;
|
subjectAlternativeName: string;
|
||||||
ttl: string;
|
ttl: string;
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateCertTemplateDTO = {
|
export type TUpdateCertTemplateDTO = {
|
||||||
@@ -17,6 +19,7 @@ export type TUpdateCertTemplateDTO = {
|
|||||||
commonName?: string;
|
commonName?: string;
|
||||||
subjectAlternativeName?: string;
|
subjectAlternativeName?: string;
|
||||||
ttl?: string;
|
ttl?: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TGetCertTemplateDTO = {
|
export type TGetCertTemplateDTO = {
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ export enum CertKeyAlgorithm {
|
|||||||
ECDSA_P384 = "EC_secp384r1"
|
ECDSA_P384 = "EC_secp384r1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 CrlReason {
|
export enum CrlReason {
|
||||||
UNSPECIFIED = "UNSPECIFIED",
|
UNSPECIFIED = "UNSPECIFIED",
|
||||||
KEY_COMPROMISE = "KEY_COMPROMISE",
|
KEY_COMPROMISE = "KEY_COMPROMISE",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { CertKeyUsage } from "../certificates/types";
|
||||||
|
|
||||||
export type TCertificateTemplate = {
|
export type TCertificateTemplate = {
|
||||||
id: string;
|
id: string;
|
||||||
caId: string;
|
caId: string;
|
||||||
@@ -8,6 +10,7 @@ export type TCertificateTemplate = {
|
|||||||
commonName: string;
|
commonName: string;
|
||||||
subjectAlternativeName: string;
|
subjectAlternativeName: string;
|
||||||
ttl: string;
|
ttl: string;
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateCertificateTemplateDTO = {
|
export type TCreateCertificateTemplateDTO = {
|
||||||
@@ -18,6 +21,7 @@ export type TCreateCertificateTemplateDTO = {
|
|||||||
subjectAlternativeName: string;
|
subjectAlternativeName: string;
|
||||||
ttl: string;
|
ttl: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateCertificateTemplateDTO = {
|
export type TUpdateCertificateTemplateDTO = {
|
||||||
@@ -29,6 +33,7 @@ export type TUpdateCertificateTemplateDTO = {
|
|||||||
subjectAlternativeName?: string;
|
subjectAlternativeName?: string;
|
||||||
ttl?: string;
|
ttl?: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteCertificateTemplateDTO = {
|
export type TDeleteCertificateTemplateDTO = {
|
||||||
|
|||||||
@@ -23,3 +23,15 @@ export type TRevokeCertDTO = {
|
|||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
revocationReason: string;
|
revocationReason: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|||||||
+100
-9
@@ -7,7 +7,12 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
Input,
|
Input,
|
||||||
@@ -25,8 +30,10 @@ import {
|
|||||||
useGetCertTemplate,
|
useGetCertTemplate,
|
||||||
useListWorkspaceCas,
|
useListWorkspaceCas,
|
||||||
useListWorkspacePkiCollections,
|
useListWorkspacePkiCollections,
|
||||||
useUpdateCertTemplate} from "@app/hooks/api";
|
useUpdateCertTemplate
|
||||||
|
} from "@app/hooks/api";
|
||||||
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||||
|
import { CertKeyUsage } from "@app/hooks/api/certificates/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const validateTemplateRegexField = z
|
const validateTemplateRegexField = z
|
||||||
@@ -45,7 +52,18 @@ const schema = z.object({
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
commonName: validateTemplateRegexField,
|
commonName: validateTemplateRegexField,
|
||||||
subjectAlternativeName: 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()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormData = z.infer<typeof schema>;
|
export type FormData = z.infer<typeof schema>;
|
||||||
@@ -59,6 +77,18 @@ type Props = {
|
|||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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: "Key Certification 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 CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => {
|
export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
@@ -86,7 +116,13 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
reset,
|
reset,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema)
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
keyUsages: {
|
||||||
|
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||||
|
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -97,14 +133,19 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
commonName: certTemplate.commonName,
|
commonName: certTemplate.commonName,
|
||||||
subjectAlternativeName: certTemplate.subjectAlternativeName,
|
subjectAlternativeName: certTemplate.subjectAlternativeName,
|
||||||
collectionId: certTemplate.pkiCollectionId ?? undefined,
|
collectionId: certTemplate.pkiCollectionId ?? undefined,
|
||||||
ttl: certTemplate.ttl
|
ttl: certTemplate.ttl,
|
||||||
|
keyUsages: Object.fromEntries(certTemplate.keyUsages.map((name) => [name, true]))
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reset({
|
reset({
|
||||||
caId,
|
caId,
|
||||||
name: "",
|
name: "",
|
||||||
commonName: "",
|
commonName: "",
|
||||||
ttl: ""
|
ttl: "",
|
||||||
|
keyUsages: {
|
||||||
|
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||||
|
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [certTemplate, ca]);
|
}, [certTemplate, ca]);
|
||||||
@@ -114,7 +155,8 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
name,
|
name,
|
||||||
commonName,
|
commonName,
|
||||||
subjectAlternativeName,
|
subjectAlternativeName,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
if (!currentWorkspace?.id) {
|
if (!currentWorkspace?.id) {
|
||||||
return;
|
return;
|
||||||
@@ -130,7 +172,10 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
name,
|
name,
|
||||||
commonName,
|
commonName,
|
||||||
subjectAlternativeName,
|
subjectAlternativeName,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages: Object.entries(keyUsages)
|
||||||
|
.filter(([, value]) => value)
|
||||||
|
.map(([key]) => key as CertKeyUsage)
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -145,7 +190,10 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
name,
|
name,
|
||||||
commonName,
|
commonName,
|
||||||
subjectAlternativeName,
|
subjectAlternativeName,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages: Object.entries(keyUsages)
|
||||||
|
.filter(([, value]) => value)
|
||||||
|
.map(([key]) => key as CertKeyUsage)
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -332,7 +380,50 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
|||||||
</FormControl>
|
</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 Usages</div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="keyUsages"
|
||||||
|
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
label="Key Usages"
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
<div className="mt-4 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
Reference in New Issue
Block a user