mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
misc: integrated custom key usages for issue-cert endpoint
This commit is contained in:
@@ -5,6 +5,7 @@ import { CertKeyUsage } from "@app/services/certificate/certificate-types";
|
|||||||
import { TableName } from "../schemas";
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// Certificate template
|
||||||
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
||||||
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
||||||
|
|
||||||
@@ -23,9 +24,41 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
keyUsages: [CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT]
|
keyUsages: [CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasExtendedKeyUsagesCol) {
|
||||||
|
await knex(TableName.CertificateTemplate).update({
|
||||||
|
extendedKeyUsages: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate
|
||||||
|
const doesCertTableHaveKeyUsages = await knex.schema.hasColumn(TableName.Certificate, "keyUsages");
|
||||||
|
const doesCertTableHaveExtendedKeyUsages = await knex.schema.hasColumn(TableName.Certificate, "extendedKeyUsages");
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (tb) => {
|
||||||
|
if (!doesCertTableHaveKeyUsages) {
|
||||||
|
tb.specificType("keyUsages", "text[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doesCertTableHaveExtendedKeyUsages) {
|
||||||
|
tb.specificType("extendedKeyUsages", "text[]");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!doesCertTableHaveKeyUsages) {
|
||||||
|
await knex(TableName.Certificate).update({
|
||||||
|
keyUsages: [CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doesCertTableHaveExtendedKeyUsages) {
|
||||||
|
await knex(TableName.Certificate).update({
|
||||||
|
extendedKeyUsages: []
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
// Certificate Template
|
||||||
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
const hasKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "keyUsages");
|
||||||
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
const hasExtendedKeyUsagesCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "extendedKeyUsages");
|
||||||
|
|
||||||
@@ -37,4 +70,16 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
t.dropColumn("extendedKeyUsages");
|
t.dropColumn("extendedKeyUsages");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Certificate
|
||||||
|
const doesCertTableHaveKeyUsages = await knex.schema.hasColumn(TableName.Certificate, "keyUsages");
|
||||||
|
const doesCertTableHaveExtendedKeyUsages = await knex.schema.hasColumn(TableName.Certificate, "extendedKeyUsages");
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
if (doesCertTableHaveKeyUsages) {
|
||||||
|
t.dropColumn("keyUsages");
|
||||||
|
}
|
||||||
|
if (doesCertTableHaveExtendedKeyUsages) {
|
||||||
|
t.dropColumn("extendedKeyUsages");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export const CertificatesSchema = z.object({
|
|||||||
revocationReason: z.number().nullable().optional(),
|
revocationReason: z.number().nullable().optional(),
|
||||||
altNames: z.string().default("").nullable().optional(),
|
altNames: z.string().default("").nullable().optional(),
|
||||||
caCertId: z.string().uuid(),
|
caCertId: z.string().uuid(),
|
||||||
certificateTemplateId: z.string().uuid().nullable().optional()
|
certificateTemplateId: z.string().uuid().nullable().optional(),
|
||||||
|
keyUsages: z.string().array().nullable().optional(),
|
||||||
|
extendedKeyUsages: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { CERTIFICATE_AUTHORITIES } 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 { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { CaRenewalType, CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaRenewalType, CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-types";
|
||||||
import {
|
import {
|
||||||
validateAltNamesField,
|
validateAltNamesField,
|
||||||
@@ -573,7 +573,8 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
|
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
|
||||||
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
|
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
|
||||||
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter)
|
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter),
|
||||||
|
keyUsages: z.nativeEnum(CertKeyUsage).array().optional()
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { CERTIFICATE_AUTHORITIES, CERTIFICATES } 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 { CrlReason } from "@app/services/certificate/certificate-types";
|
import { CertKeyUsage, CrlReason } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
validateAltNamesField,
|
validateAltNamesField,
|
||||||
validateCaDateField
|
validateCaDateField
|
||||||
@@ -86,7 +86,8 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
|
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
|
||||||
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
|
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
|
||||||
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter)
|
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter),
|
||||||
|
keyUsages: z.nativeEnum(CertKeyUsage).array().optional()
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||||
import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types";
|
import { CertKeyAlgorithm, CertKeyUsage, CertStatus } from "../certificate/certificate-types";
|
||||||
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
||||||
import { validateCertificateDetailsAgainstTemplate } from "../certificate-template/certificate-template-fns";
|
import { validateCertificateDetailsAgainstTemplate } from "../certificate-template/certificate-template-fns";
|
||||||
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
||||||
@@ -1052,7 +1052,8 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actor,
|
actor,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
keyUsages
|
||||||
}: TIssueCertFromCaDTO) => {
|
}: TIssueCertFromCaDTO) => {
|
||||||
let ca: TCertificateAuthorities | undefined;
|
let ca: TCertificateAuthorities | undefined;
|
||||||
let certificateTemplate: TCertificateTemplates | undefined;
|
let certificateTemplate: TCertificateTemplates | undefined;
|
||||||
@@ -1170,14 +1171,37 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
|
|
||||||
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}`;
|
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}`;
|
||||||
|
|
||||||
|
let selectedKeyUsages: CertKeyUsage[] = keyUsages ?? [];
|
||||||
|
if (keyUsages === undefined && !certificateTemplate) {
|
||||||
|
selectedKeyUsages = [CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyUsages === undefined && certificateTemplate) {
|
||||||
|
selectedKeyUsages = (certificateTemplate.keyUsages ?? []) as CertKeyUsage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyUsages?.length && certificateTemplate) {
|
||||||
|
const validKeyUsages = certificateTemplate.keyUsages || [];
|
||||||
|
if (keyUsages.some((keyUsage) => !validKeyUsages.includes(keyUsage))) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Invalid key usage value for certificate"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
selectedKeyUsages = keyUsages;
|
||||||
|
}
|
||||||
|
|
||||||
const extensions: x509.Extension[] = [
|
const extensions: x509.Extension[] = [
|
||||||
new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment, true),
|
|
||||||
new x509.BasicConstraintsExtension(false),
|
new x509.BasicConstraintsExtension(false),
|
||||||
new x509.CRLDistributionPointsExtension([distributionPointUrl]),
|
new x509.CRLDistributionPointsExtension([distributionPointUrl]),
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
|
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
|
||||||
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey)
|
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const keyUsagesBitValue = selectedKeyUsages.reduce((accum, keyUsage) => accum | x509.KeyUsageFlags[keyUsage], 0);
|
||||||
|
if (keyUsagesBitValue) {
|
||||||
|
extensions.push(new x509.KeyUsagesExtension(keyUsagesBitValue, true));
|
||||||
|
}
|
||||||
|
|
||||||
let altNamesArray: {
|
let altNamesArray: {
|
||||||
type: "email" | "dns";
|
type: "email" | "dns";
|
||||||
value: string;
|
value: string;
|
||||||
@@ -1259,7 +1283,8 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
altNames,
|
altNames,
|
||||||
serialNumber,
|
serialNumber,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: notAfterDate
|
notAfter: notAfterDate,
|
||||||
|
keyUsages: selectedKeyUsages
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||||
import { CertKeyAlgorithm } from "../certificate/certificate-types";
|
import { CertKeyAlgorithm, CertKeyUsage } from "../certificate/certificate-types";
|
||||||
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
||||||
@@ -97,6 +97,7 @@ export type TIssueCertFromCaDTO = {
|
|||||||
ttl: string;
|
ttl: string;
|
||||||
notBefore?: string;
|
notBefore?: string;
|
||||||
notAfter?: string;
|
notAfter?: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TSignCertFromCaDTO =
|
export type TSignCertFromCaDTO =
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ export const certificateTemplateDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (certTemplate) {
|
return 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" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ export const sanitizedCertificateTemplate = CertificateTemplatesSchema.pick({
|
|||||||
commonName: true,
|
commonName: true,
|
||||||
subjectAlternativeName: true,
|
subjectAlternativeName: true,
|
||||||
pkiCollectionId: true,
|
pkiCollectionId: true,
|
||||||
ttl: true
|
ttl: true,
|
||||||
|
keyUsages: true
|
||||||
}).merge(
|
}).merge(
|
||||||
z.object({
|
z.object({
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
caName: z.string(),
|
caName: z.string()
|
||||||
keyUsages: z.string().array()
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CertKeyAlgorithm } from "../certificates/enums";
|
import { CertKeyAlgorithm } from "../certificates/enums";
|
||||||
|
import { CertKeyUsage } from "../certificates/types";
|
||||||
import { CaRenewalType, CaStatus, CaType } from "./enums";
|
import { CaRenewalType, CaStatus, CaType } from "./enums";
|
||||||
|
|
||||||
export type TCertificateAuthority = {
|
export type TCertificateAuthority = {
|
||||||
@@ -91,6 +92,7 @@ export type TCreateCertificateDTO = {
|
|||||||
ttl: string; // string compatible with ms
|
ttl: string; // string compatible with ms
|
||||||
notBefore?: string;
|
notBefore?: string;
|
||||||
notAfter?: string;
|
notAfter?: string;
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateCertificateResponse = {
|
export type TCreateCertificateResponse = {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type TCertificate = {
|
|||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
notBefore: string;
|
notBefore: string;
|
||||||
notAfter: string;
|
notAfter: string;
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteCertDTO = {
|
export type TDeleteCertDTO = {
|
||||||
@@ -35,3 +36,15 @@ export enum CertKeyUsage {
|
|||||||
ENCIPHER_ONLY = "encipherOnly",
|
ENCIPHER_ONLY = "encipherOnly",
|
||||||
DECIPHER_ONLY = "decipherOnly"
|
DECIPHER_ONLY = "decipherOnly"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -28,6 +33,7 @@ import {
|
|||||||
useListWorkspacePkiCollections
|
useListWorkspacePkiCollections
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||||
|
import { CertKeyUsage, KEY_USAGES_OPTIONS } from "@app/hooks/api/certificates/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { CertificateContent } from "./CertificateContent";
|
import { CertificateContent } from "./CertificateContent";
|
||||||
@@ -39,7 +45,18 @@ const schema = z.object({
|
|||||||
friendlyName: z.string(),
|
friendlyName: z.string(),
|
||||||
commonName: z.string().trim().min(1),
|
commonName: z.string().trim().min(1),
|
||||||
altNames: z.string(),
|
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()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormData = z.infer<typeof schema>;
|
export type FormData = z.infer<typeof schema>;
|
||||||
@@ -88,7 +105,13 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
setValue,
|
setValue,
|
||||||
watch
|
watch
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema)
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
keyUsages: {
|
||||||
|
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||||
|
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedCertTemplateId = watch("certificateTemplateId");
|
const selectedCertTemplateId = watch("certificateTemplateId");
|
||||||
@@ -107,7 +130,8 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
commonName: cert.commonName,
|
commonName: cert.commonName,
|
||||||
altNames: cert.altNames,
|
altNames: cert.altNames,
|
||||||
certificateTemplateId: cert.certificateTemplateId ?? CERT_TEMPLATE_NONE_VALUE,
|
certificateTemplateId: cert.certificateTemplateId ?? CERT_TEMPLATE_NONE_VALUE,
|
||||||
ttl: ""
|
ttl: "",
|
||||||
|
keyUsages: Object.fromEntries(cert.keyUsages.map((name) => [name, true]))
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reset({
|
reset({
|
||||||
@@ -116,7 +140,11 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
commonName: "",
|
commonName: "",
|
||||||
altNames: "",
|
altNames: "",
|
||||||
ttl: "",
|
ttl: "",
|
||||||
certificateTemplateId: CERT_TEMPLATE_NONE_VALUE
|
certificateTemplateId: CERT_TEMPLATE_NONE_VALUE,
|
||||||
|
keyUsages: {
|
||||||
|
[CertKeyUsage.DIGITAL_SIGNATURE]: true,
|
||||||
|
[CertKeyUsage.KEY_ENCIPHERMENT]: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [cert]);
|
}, [cert]);
|
||||||
@@ -124,6 +152,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!cert && selectedCertTemplate) {
|
if (!cert && selectedCertTemplate) {
|
||||||
setValue("ttl", selectedCertTemplate.ttl);
|
setValue("ttl", selectedCertTemplate.ttl);
|
||||||
|
setValue(
|
||||||
|
"keyUsages",
|
||||||
|
Object.fromEntries(selectedCertTemplate.keyUsages.map((name) => [name, true]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [selectedCertTemplate, cert]);
|
}, [selectedCertTemplate, cert]);
|
||||||
|
|
||||||
@@ -133,7 +165,8 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
collectionId,
|
collectionId,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!currentWorkspace?.slug) return;
|
if (!currentWorkspace?.slug) return;
|
||||||
@@ -146,7 +179,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
friendlyName,
|
friendlyName,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
ttl
|
ttl,
|
||||||
|
keyUsages: Object.entries(keyUsages)
|
||||||
|
.filter(([, value]) => value)
|
||||||
|
.map(([key]) => key as CertKeyUsage)
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
@@ -363,8 +399,52 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
</FormControl>
|
</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 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"
|
||||||
|
isDisabled={Boolean(cert)}
|
||||||
|
isChecked={value[optionValue]}
|
||||||
|
onCheckedChange={(state) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
[optionValue]: state
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Checkbox>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
{!cert && (
|
{!cert && (
|
||||||
<div className="flex items-center">
|
<div className="mt-4 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
useUpdateCertTemplate
|
useUpdateCertTemplate
|
||||||
} from "@app/hooks/api";
|
} 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 { CertKeyUsage, KEY_USAGES_OPTIONS } from "@app/hooks/api/certificates/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const validateTemplateRegexField = z
|
const validateTemplateRegexField = z
|
||||||
@@ -77,18 +77,6 @@ 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();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user