diff --git a/backend/src/ee/services/pki-acme/pki-acme-service.ts b/backend/src/ee/services/pki-acme/pki-acme-service.ts index c4fb0ab1c..d24ed5394 100644 --- a/backend/src/ee/services/pki-acme/pki-acme-service.ts +++ b/backend/src/ee/services/pki-acme/pki-acme-service.ts @@ -739,11 +739,7 @@ export const pkiAcmeServiceFactory = ({ throw new AcmeBadCSRError({ message: "Invalid CSR: Common name + SANs mismatch with order identifiers" }); } - if (!profile.caId) { - throw new NotFoundError({ message: "Self-signed certificates are not supported for ACME enrollment" }); - } - - const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(profile.caId); + const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(profile.caId!); if (!ca) { throw new NotFoundError({ message: "Certificate Authority not found" }); } diff --git a/backend/src/server/routes/v1/certificate-profiles-router.ts b/backend/src/server/routes/v1/certificate-profiles-router.ts index 0f9fb2581..b23dd1fee 100644 --- a/backend/src/server/routes/v1/certificate-profiles-router.ts +++ b/backend/src/server/routes/v1/certificate-profiles-router.ts @@ -51,58 +51,100 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid .refine( (data) => { if (data.enrollmentType === EnrollmentType.EST) { - if (!data.estConfig) { - return false; - } - if (data.apiConfig) { - return false; - } - if (data.acmeConfig) { - return false; - } + return !!data.estConfig; } - if (data.enrollmentType === EnrollmentType.API) { - if (!data.apiConfig) { - return false; - } - if (data.estConfig) { - return false; - } - if (data.acmeConfig) { - return false; - } - } - if (data.enrollmentType === EnrollmentType.ACME) { - if (!data.acmeConfig) { - return false; - } - if (data.estConfig) { - return false; - } - if (data.apiConfig) { - return false; - } - } - - if (data.issuerType === IssuerType.CA) { - if (!data.caId) { - return false; - } - } - if (data.issuerType === IssuerType.SELF_SIGNED) { - if (data.caId) { - return false; - } - if (data.enrollmentType !== EnrollmentType.API) { - return false; - } - } - return true; }, { - message: - "EST enrollment type requires EST configuration and cannot have API or ACME configuration. API enrollment type requires API configuration and cannot have EST or ACME configuration. ACME enrollment type requires ACME configuration and cannot have EST or API configuration. CA issuer type requires a CA ID. Self-signed issuer type cannot have a CA ID and only supports API enrollment." + message: "EST enrollment type requires EST configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !!data.apiConfig; + } + return true; + }, + { + message: "API enrollment type requires API configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !!data.acmeConfig; + } + return true; + }, + { + message: "ACME enrollment type requires ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.EST) { + return !data.apiConfig && !data.acmeConfig; + } + return true; + }, + { + message: "EST enrollment type cannot have API or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !data.estConfig && !data.acmeConfig; + } + return true; + }, + { + message: "API enrollment type cannot have EST or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !data.estConfig && !data.apiConfig; + } + return true; + }, + { + message: "ACME enrollment type cannot have EST or API configuration" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.CA) { + return !!data.caId; + } + return true; + }, + { + message: "CA issuer type requires a CA ID" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return !data.caId; + } + return true; + }, + { + message: "Self-signed issuer type cannot have a CA ID" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return data.enrollmentType === EnrollmentType.API; + } + return true; + }, + { + message: "Self-signed issuer type only supports API enrollment" } ), response: { diff --git a/backend/src/services/certificate-profile/certificate-profile-schemas.ts b/backend/src/services/certificate-profile/certificate-profile-schemas.ts index dc5637475..e6b574dea 100644 --- a/backend/src/services/certificate-profile/certificate-profile-schemas.ts +++ b/backend/src/services/certificate-profile/certificate-profile-schemas.ts @@ -7,11 +7,7 @@ import { EnrollmentType, IssuerType } from "./certificate-profile-types"; export const createCertificateProfileSchema = z .object({ projectId: z.string().uuid("Project ID must be valid"), - caId: z - .union([z.string().uuid(), z.literal("")]) - .optional() - .nullable() - .transform((val) => (val === "" ? null : val)), + caId: z.string().uuid().nullable().optional(), certificateTemplateId: z.string().uuid(), slug: z .string() @@ -38,60 +34,101 @@ export const createCertificateProfileSchema = z }) .refine( (data) => { - // Validate enrollment type configurations if (data.enrollmentType === EnrollmentType.EST) { - if (!data.estConfig) { - return false; - } - if (data.apiConfig) { - return false; - } - if (data.acmeConfig) { - return false; - } + return !!data.estConfig; } - if (data.enrollmentType === EnrollmentType.API) { - if (!data.apiConfig) { - return false; - } - if (data.estConfig) { - return false; - } - if (data.acmeConfig) { - return false; - } - } - if (data.enrollmentType === EnrollmentType.ACME) { - if (!data.acmeConfig) { - return false; - } - if (data.estConfig) { - return false; - } - if (data.apiConfig) { - return false; - } - } - - if (data.issuerType === IssuerType.CA) { - if (!data.caId) { - return false; - } - } - if (data.issuerType === IssuerType.SELF_SIGNED) { - if (data.caId) { - return false; - } - if (data.enrollmentType !== EnrollmentType.API) { - return false; - } - } - return true; }, { - message: - "EST enrollment type requires EST configuration and cannot have API configuration. API enrollment type requires API configuration and cannot have EST configuration. CA issuer type requires a CA ID. Self-signed issuer type cannot have a CA ID and only supports API enrollment." + message: "EST enrollment type requires EST configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !!data.apiConfig; + } + return true; + }, + { + message: "API enrollment type requires API configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !!data.acmeConfig; + } + return true; + }, + { + message: "ACME enrollment type requires ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.EST) { + return !data.apiConfig && !data.acmeConfig; + } + return true; + }, + { + message: "EST enrollment type cannot have API or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !data.estConfig && !data.acmeConfig; + } + return true; + }, + { + message: "API enrollment type cannot have EST or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !data.estConfig && !data.apiConfig; + } + return true; + }, + { + message: "ACME enrollment type cannot have EST or API configuration" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.CA) { + return !!data.caId; + } + return true; + }, + { + message: "CA issuer type requires a CA ID" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return !data.caId; + } + return true; + }, + { + message: "Self-signed issuer type cannot have a CA ID" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return data.enrollmentType === EnrollmentType.API; + } + return true; + }, + { + message: "Self-signed issuer type only supports API enrollment" } ); @@ -123,27 +160,34 @@ export const updateCertificateProfileSchema = z .refine( (data) => { if (data.enrollmentType === EnrollmentType.EST) { - if (data.apiConfig) { - return false; - } + return !data.apiConfig; } - if (data.enrollmentType === EnrollmentType.API) { - if (data.estConfig) { - return false; - } - } - - if (data.issuerType === IssuerType.SELF_SIGNED) { - if (data.enrollmentType && data.enrollmentType !== EnrollmentType.API) { - return false; - } - } - return true; }, { - message: - "Cannot have EST config with API enrollment type or API config with EST enrollment type. Self-signed issuer type only supports API enrollment." + message: "EST enrollment type cannot have API configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !data.estConfig; + } + return true; + }, + { + message: "API enrollment type cannot have EST configuration" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return !data.enrollmentType || data.enrollmentType === EnrollmentType.API; + } + return true; + }, + { + message: "Self-signed issuer type only supports API enrollment" } ); diff --git a/backend/src/services/certificate-v3/certificate-v3-service.ts b/backend/src/services/certificate-v3/certificate-v3-service.ts index b4bad0f76..0fadbc8de 100644 --- a/backend/src/services/certificate-v3/certificate-v3-service.ts +++ b/backend/src/services/certificate-v3/certificate-v3-service.ts @@ -407,11 +407,16 @@ const generateSelfSignedCertificate = async ({ const signatureAlgorithmConfig = signatureAlgorithmToAlgCfg(effectiveSignatureAlgorithm, effectiveKeyAlgorithm); const notBeforeDate = certificateRequest.notBefore ? new Date(certificateRequest.notBefore) : new Date(); - let notAfterDate = new Date(new Date().setFullYear(new Date().getFullYear() + 1)); + + let notAfterDate: Date; if (certificateRequest.notAfter) { notAfterDate = new Date(certificateRequest.notAfter); } else if (certificateRequest.validity.ttl) { notAfterDate = new Date(new Date().getTime() + ms(certificateRequest.validity.ttl)); + } else { + throw new BadRequestError({ + message: "Either notAfter date or TTL must be provided for certificate validity" + }); } const serialNumber = createSerialNumber(); @@ -458,10 +463,22 @@ const generateSelfSignedCertificate = async ({ ...(subjectAlternativeNames ? [ new x509.SubjectAlternativeNameExtension( - certificateRequest.altNames?.map((san) => ({ - type: san.type === CertSubjectAlternativeNameType.DNS_NAME ? "dns" : "ip", - value: san.value - })) || [], + certificateRequest.altNames?.map((san) => { + switch (san.type) { + case CertSubjectAlternativeNameType.DNS_NAME: + return { type: "dns" as const, value: san.value }; + case CertSubjectAlternativeNameType.IP_ADDRESS: + return { type: "ip" as const, value: san.value }; + case CertSubjectAlternativeNameType.EMAIL: + return { type: "email" as const, value: san.value }; + case CertSubjectAlternativeNameType.URI: + return { type: "url" as const, value: san.value }; + default: + throw new BadRequestError({ + message: `Unsupported Subject Alternative Name type: ${san.type as string}` + }); + } + }) || [], false ) ] @@ -545,7 +562,7 @@ const createSelfSignedCertificateRecord = async ({ (selfSignedResult.certificateSubject.common_name as string) || certificateRequest.commonName || originalCert?.commonName || - (isRenewal ? "Renewed Self-signed Certificate" : "Self-signed Certificate"); + ""; const altNamesList = selfSignedResult.subjectAlternativeNames.map((san) => san.value).join(","); @@ -726,8 +743,8 @@ const processSelfSignedCertificate = async ({ await createEncryptedCertificateData({ certificateId: certificateData.id, - certificate: Buffer.from(selfSignedResult.certificate), - privateKey: Buffer.from(selfSignedResult.privateKey), + certificate: selfSignedResult.certificate, + privateKey: selfSignedResult.privateKey, projectId, certificateBodyDAL, certificateSecretDAL, @@ -1100,10 +1117,25 @@ export const certificateV3ServiceFactory = ({ commonName: certificateOrder.commonName, keyUsages: certificateOrder.keyUsages, extendedKeyUsages: certificateOrder.extendedKeyUsages, - subjectAlternativeNames: certificateOrder.altNames.map((san) => ({ - type: san.type === "dns" ? CertSubjectAlternativeNameType.DNS_NAME : CertSubjectAlternativeNameType.IP_ADDRESS, - value: san.value - })), + subjectAlternativeNames: certificateOrder.altNames.map((san) => { + let certType: CertSubjectAlternativeNameType; + switch (san.type) { + case "dns": + certType = CertSubjectAlternativeNameType.DNS_NAME; + break; + case "ip": + certType = CertSubjectAlternativeNameType.IP_ADDRESS; + break; + default: + throw new BadRequestError({ + message: `Unsupported Subject Alternative Name type: ${san.type as string}` + }); + } + return { + type: certType, + value: san.value + }; + }), validity: certificateOrder.validity, notBefore: certificateOrder.notBefore, notAfter: certificateOrder.notAfter, @@ -1216,7 +1248,8 @@ export const certificateV3ServiceFactory = ({ if (profile.enrollmentType !== EnrollmentType.API) { throw new ForbiddenRequestError({ - message: "Certificate is not eligible for renewal: EST certificates cannot be renewed through this endpoint" + message: + "Certificate is not eligible for renewal: Only certificates issued from an API enrollment profile can be renewed through this endpoint" }); } } diff --git a/frontend/src/hooks/api/certificateProfiles/index.ts b/frontend/src/hooks/api/certificateProfiles/index.ts index e12e066c4..27e73c15a 100644 --- a/frontend/src/hooks/api/certificateProfiles/index.ts +++ b/frontend/src/hooks/api/certificateProfiles/index.ts @@ -10,4 +10,4 @@ export { useGetProfileCertificates, useListCertificateProfiles } from "./queries"; -export type * from "./types"; +export * from "./types"; diff --git a/frontend/src/hooks/api/certificateProfiles/types.ts b/frontend/src/hooks/api/certificateProfiles/types.ts index 35e50a9f4..176a5dd9e 100644 --- a/frontend/src/hooks/api/certificateProfiles/types.ts +++ b/frontend/src/hooks/api/certificateProfiles/types.ts @@ -1,3 +1,14 @@ +export enum EnrollmentType { + API = "api", + EST = "est", + ACME = "acme" +} + +export enum IssuerType { + CA = "ca", + SELF_SIGNED = "self-signed" +} + export type TCertificateProfile = { id: string; projectId: string; @@ -5,8 +16,8 @@ export type TCertificateProfile = { certificateTemplateId: string; slug: string; description?: string; - enrollmentType: "api" | "est" | "acme"; - issuerType: "ca" | "self-signed"; + enrollmentType: EnrollmentType; + issuerType: IssuerType; estConfigId?: string; apiConfigId?: string; createdAt: string; @@ -49,8 +60,8 @@ export type TCreateCertificateProfileDTO = { certificateTemplateId: string; slug: string; description?: string; - enrollmentType: "api" | "est" | "acme"; - issuerType: "ca" | "self-signed"; + enrollmentType: EnrollmentType; + issuerType: IssuerType; estConfig?: { disableBootstrapCaValidation?: boolean; passphrase: string; @@ -67,8 +78,8 @@ export type TUpdateCertificateProfileDTO = { profileId: string; slug?: string; description?: string; - enrollmentType?: "api" | "est" | "acme"; - issuerType?: "ca" | "self-signed"; + enrollmentType?: EnrollmentType; + issuerType?: IssuerType; estConfig?: { disableBootstrapCaValidation?: boolean; passphrase?: string; @@ -91,8 +102,8 @@ export type TListCertificateProfilesDTO = { offset?: number; search?: string; includeConfigs?: boolean; - enrollmentType?: "api" | "est" | "acme"; - issuerType?: "ca" | "self-signed"; + enrollmentType?: EnrollmentType; + issuerType?: IssuerType; caId?: string; }; diff --git a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal.tsx b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal.tsx index 49ffe3494..5f6462d2e 100644 --- a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal.tsx +++ b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal.tsx @@ -21,7 +21,7 @@ import { import { useProject } from "@app/context"; import { useGetCert } from "@app/hooks/api"; import { useCreateCertificateV3 } from "@app/hooks/api/ca"; -import { useListCertificateProfiles } from "@app/hooks/api/certificateProfiles"; +import { EnrollmentType, useListCertificateProfiles } from "@app/hooks/api/certificateProfiles"; import { CertExtendedKeyUsage, CertKeyUsage } from "@app/hooks/api/certificates/enums"; import { useGetCertificateTemplateV2ById } from "@app/hooks/api/certificateTemplates/queries"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -122,7 +122,7 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId } const { data: profilesData } = useListCertificateProfiles({ projectId: currentProject?.id || "", - enrollmentType: "api" + enrollmentType: EnrollmentType.API }); const { mutateAsync: createCertificate } = useCreateCertificateV3({ diff --git a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/CreateProfileModal.tsx b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/CreateProfileModal.tsx index 612fde634..044bc9cfc 100644 --- a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/CreateProfileModal.tsx +++ b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/CreateProfileModal.tsx @@ -21,6 +21,8 @@ import { import { useProject, useSubscription } from "@app/context"; import { useListCasByProjectId } from "@app/hooks/api/ca/queries"; import { + EnrollmentType, + IssuerType, TCertificateProfileWithDetails, TCreateCertificateProfileDTO, TUpdateCertificateProfileDTO, @@ -46,8 +48,8 @@ const createSchema = z .trim() .max(1000, "Description must be less than 1000 characters") .optional(), - enrollmentType: z.enum(["api", "est", "acme"]), - issuerType: z.enum(["ca", "self-signed"]), + enrollmentType: z.nativeEnum(EnrollmentType), + issuerType: z.nativeEnum(IssuerType), certificateAuthorityId: z.string().nullable().optional(), certificateTemplateId: z.string().min(1, "Certificate Template is required"), estConfig: z @@ -79,30 +81,101 @@ const createSchema = z }) .refine( (data) => { - if (data.enrollmentType === "est" && !data.estConfig) { - return false; + if (data.enrollmentType === EnrollmentType.EST) { + return !!data.estConfig; } - if (data.enrollmentType === "api" && !data.apiConfig) { - return false; - } - if (data.enrollmentType === "acme" && !data.acmeConfig) { - return false; - } - - if (data.issuerType === "ca" && !data.certificateAuthorityId) { - return false; - } - if (data.issuerType === "self-signed" && data.certificateAuthorityId) { - return false; - } - if (data.issuerType === "self-signed" && data.enrollmentType !== "api") { - return false; - } - return true; }, { - message: "Configuration is required for selected enrollment type and issuer type" + message: "EST enrollment type requires EST configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !!data.apiConfig; + } + return true; + }, + { + message: "API enrollment type requires API configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !!data.acmeConfig; + } + return true; + }, + { + message: "ACME enrollment type requires ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.EST) { + return !data.apiConfig && !data.acmeConfig; + } + return true; + }, + { + message: "EST enrollment type cannot have API or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !data.estConfig && !data.acmeConfig; + } + return true; + }, + { + message: "API enrollment type cannot have EST or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !data.estConfig && !data.apiConfig; + } + return true; + }, + { + message: "ACME enrollment type cannot have EST or API configuration" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.CA) { + return !!data.certificateAuthorityId; + } + return true; + }, + { + message: "CA issuer type requires a certificate authority" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return !data.certificateAuthorityId; + } + return true; + }, + { + message: "Self-signed issuer type cannot have a certificate authority" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return data.enrollmentType === EnrollmentType.API; + } + return true; + }, + { + message: "Self-signed issuer type only supports API enrollment" } ); @@ -122,8 +195,8 @@ const editSchema = z .trim() .max(1000, "Description must be less than 1000 characters") .optional(), - enrollmentType: z.enum(["api", "est", "acme"]), - issuerType: z.enum(["ca", "self-signed"]), + enrollmentType: z.nativeEnum(EnrollmentType), + issuerType: z.nativeEnum(IssuerType), certificateAuthorityId: z.string().nullable().optional(), certificateTemplateId: z.string().optional(), estConfig: z @@ -143,31 +216,101 @@ const editSchema = z }) .refine( (data) => { - if (data.enrollmentType === "est" && !data.estConfig) { - return false; + if (data.enrollmentType === EnrollmentType.EST) { + return !!data.estConfig; } - if (data.enrollmentType === "api" && !data.apiConfig) { - return false; - } - if (data.enrollmentType === "acme" && !data.acmeConfig) { - return false; - } - - if (data.issuerType === "ca" && !data.certificateAuthorityId) { - return false; - } - if (data.issuerType === "self-signed" && data.certificateAuthorityId) { - return false; - } - if (data.issuerType === "self-signed" && data.enrollmentType !== "api") { - return false; - } - return true; }, { - message: - "Configuration is required for selected enrollment type and issuer type. CA issuer requires a certificate authority. Self-signed issuer cannot have a certificate authority and only supports API enrollment." + message: "EST enrollment type requires EST configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !!data.apiConfig; + } + return true; + }, + { + message: "API enrollment type requires API configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !!data.acmeConfig; + } + return true; + }, + { + message: "ACME enrollment type requires ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.EST) { + return !data.apiConfig && !data.acmeConfig; + } + return true; + }, + { + message: "EST enrollment type cannot have API or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.API) { + return !data.estConfig && !data.acmeConfig; + } + return true; + }, + { + message: "API enrollment type cannot have EST or ACME configuration" + } + ) + .refine( + (data) => { + if (data.enrollmentType === EnrollmentType.ACME) { + return !data.estConfig && !data.apiConfig; + } + return true; + }, + { + message: "ACME enrollment type cannot have EST or API configuration" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.CA) { + return !!data.certificateAuthorityId; + } + return true; + }, + { + message: "CA issuer type requires a certificate authority" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return !data.certificateAuthorityId; + } + return true; + }, + { + message: "Self-signed issuer type cannot have a certificate authority" + } + ) + .refine( + (data) => { + if (data.issuerType === IssuerType.SELF_SIGNED) { + return data.enrollmentType === EnrollmentType.API; + } + return true; + }, + { + message: "Self-signed issuer type only supports API enrollment" } ); @@ -222,7 +365,7 @@ export const CreateProfileModal = ({ certificateAuthorityId: profile.caId || undefined, certificateTemplateId: profile.certificateTemplateId, estConfig: - profile.enrollmentType === "est" + profile.enrollmentType === EnrollmentType.EST ? { disableBootstrapCaValidation: profile.estConfig?.disableBootstrapCaValidation || false, @@ -231,19 +374,19 @@ export const CreateProfileModal = ({ } : undefined, apiConfig: - profile.enrollmentType === "api" + profile.enrollmentType === EnrollmentType.API ? { autoRenew: profile.apiConfig?.autoRenew || false, renewBeforeDays: profile.apiConfig?.renewBeforeDays || 30 } : undefined, - acmeConfig: profile.enrollmentType === "acme" ? {} : undefined + acmeConfig: profile.enrollmentType === EnrollmentType.ACME ? {} : undefined } : { slug: "", description: "", - enrollmentType: "api", - issuerType: "ca", + enrollmentType: EnrollmentType.API, + issuerType: IssuerType.CA, certificateAuthorityId: "", certificateTemplateId: "", apiConfig: { @@ -284,13 +427,13 @@ export const CreateProfileModal = ({ renewBeforeDays: profile.apiConfig?.renewBeforeDays || 30 } : undefined, - acmeConfig: profile.enrollmentType === "acme" ? {} : undefined + acmeConfig: profile.enrollmentType === EnrollmentType.ACME ? {} : undefined }); } }, [isEdit, profile, reset]); const onFormSubmit = async (data: FormData) => { - if (!isEdit && !subscription?.pkiAcme && data.enrollmentType === "acme") { + if (!isEdit && !subscription?.pkiAcme && data.enrollmentType === EnrollmentType.ACME) { reset(); onClose(); handlePopUpOpen("upgradePlan", { @@ -309,11 +452,11 @@ export const CreateProfileModal = ({ issuerType: data.issuerType }; - if (data.enrollmentType === "est" && data.estConfig) { + if (data.enrollmentType === EnrollmentType.EST && data.estConfig) { updateData.estConfig = data.estConfig; - } else if (data.enrollmentType === "api" && data.apiConfig) { + } else if (data.enrollmentType === EnrollmentType.API && data.apiConfig) { updateData.apiConfig = data.apiConfig; - } else if (data.enrollmentType === "acme" && data.acmeConfig) { + } else if (data.enrollmentType === EnrollmentType.ACME && data.acmeConfig) { updateData.acmeConfig = data.acmeConfig; } @@ -330,19 +473,21 @@ export const CreateProfileModal = ({ enrollmentType: data.enrollmentType, issuerType: data.issuerType, caId: - data.issuerType === "self-signed" ? undefined : data.certificateAuthorityId || undefined, + data.issuerType === IssuerType.SELF_SIGNED + ? undefined + : data.certificateAuthorityId || undefined, certificateTemplateId: data.certificateTemplateId }; - if (data.enrollmentType === "est" && data.estConfig) { + if (data.enrollmentType === EnrollmentType.EST && data.estConfig) { createData.estConfig = { passphrase: data.estConfig.passphrase, caChain: data.estConfig.caChain || undefined, disableBootstrapCaValidation: data.estConfig.disableBootstrapCaValidation }; - } else if (data.enrollmentType === "api" && data.apiConfig) { + } else if (data.enrollmentType === EnrollmentType.API && data.apiConfig) { createData.apiConfig = data.apiConfig; - } else if (data.enrollmentType === "acme" && data.acmeConfig) { + } else if (data.enrollmentType === EnrollmentType.ACME && data.acmeConfig) { createData.acmeConfig = data.acmeConfig; } @@ -417,7 +562,7 @@ export const CreateProfileModal = ({ onValueChange={(value) => { if (value === "self-signed") { setValue("certificateAuthorityId", ""); - setValue("enrollmentType", "api"); + setValue("enrollmentType", EnrollmentType.API); setValue("apiConfig", { autoRenew: false, renewBeforeDays: 30 @@ -559,12 +704,12 @@ export const CreateProfileModal = ({ isDisabled={Boolean(isEdit)} > API - - EST - - - ACME - + {watchedIssuerType !== IssuerType.SELF_SIGNED && ( + EST + )} + {watchedIssuerType !== IssuerType.SELF_SIGNED && ( + ACME + )} )} diff --git a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileList.tsx b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileList.tsx index ed042c07e..0ba217ce9 100644 --- a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileList.tsx +++ b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileList.tsx @@ -47,7 +47,6 @@ export const ProfileList = ({ Name Enrollment Method - Issuer Type Issuing CA Certificate Template @@ -55,7 +54,7 @@ export const ProfileList = ({ - + @@ -72,17 +71,16 @@ export const ProfileList = ({ Name Enrollment Method - Issuer Type Issuing CA Certificate Template - {isLoading && } + {isLoading && } {!isLoading && (!profiles || profiles.length === 0) && ( - + diff --git a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileRow.tsx b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileRow.tsx index 60057d40e..c2ec7b688 100644 --- a/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileRow.tsx +++ b/frontend/src/pages/cert-manager/PoliciesPage/components/CertificateProfilesTab/ProfileRow.tsx @@ -30,7 +30,7 @@ import { } from "@app/context/ProjectPermissionContext/types"; import { usePopUp, useToggle } from "@app/hooks"; import { useGetCaById } from "@app/hooks/api/ca/queries"; -import { TCertificateProfile } from "@app/hooks/api/certificateProfiles"; +import { IssuerType, TCertificateProfile } from "@app/hooks/api/certificateProfiles"; import { useGetCertificateTemplateV2ById } from "@app/hooks/api/certificateTemplates/queries"; import { CertificateIssuanceModal } from "@app/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal"; @@ -106,20 +106,6 @@ export const ProfileRow = ({ return {label}; }; - const getIssuerTypeBadge = (issuerType: string) => { - const config = { - ca: { variant: "success" as const, label: "CA" }, - "self-signed": { variant: "info" as const, label: "Self-Signed" } - } as const; - - const configKey = Object.keys(config).includes(issuerType) - ? (issuerType as keyof typeof config) - : "ca"; - const { variant, label } = config[configKey]; - - return {label}; - }; - return ( @@ -133,11 +119,10 @@ export const ProfileRow = ({ {getEnrollmentTypeBadge(profile.enrollmentType)} - {getIssuerTypeBadge(profile.issuerType)} - {profile.issuerType === "self-signed" - ? "—" + {profile.issuerType === IssuerType.SELF_SIGNED + ? "Self-signed" : caData?.friendlyName || caData?.commonName || profile.caId}