/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { ForbiddenError } from "@casl/ability"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal"; import { ACMESANType, CertificateOrderStatus } from "@app/services/certificate/certificate-types"; import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service"; import { CertExtendedKeyUsageType, CertIncludeType, CertKeyUsageType, CertSubjectAttributeType } from "@app/services/certificate-common/certificate-constants"; import { TCertificateProfileDALFactory } from "@app/services/certificate-profile/certificate-profile-dal"; import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types"; import { TCertificateTemplateV2ServiceFactory } from "@app/services/certificate-template-v2/certificate-template-v2-service"; import { ActorType, AuthMethod } from "../auth/auth-type"; import { certificateV3ServiceFactory, TCertificateV3ServiceFactory } from "./certificate-v3-service"; describe("CertificateV3Service", () => { let service: TCertificateV3ServiceFactory; const mockCertificateDAL: Pick = { findOne: vi.fn(), updateById: vi.fn() }; const mockCertificateAuthorityDAL: Pick = { findByIdWithAssociatedCa: vi.fn() }; const mockCertificateProfileDAL: Pick = { findByIdWithConfigs: vi.fn() }; const mockCertificateTemplateV2Service: Pick< TCertificateTemplateV2ServiceFactory, "validateCertificateRequest" | "getTemplateV2ById" > = { validateCertificateRequest: vi.fn(), getTemplateV2ById: vi.fn() }; const mockInternalCaService: Pick = { signCertFromCa: vi.fn(), issueCertFromCa: vi.fn() }; const mockPermissionService: Pick = { getProjectPermission: vi.fn().mockResolvedValue({ permission: { throwUnlessCan: vi.fn(), can: vi.fn().mockReturnValue(true), cannot: vi.fn().mockReturnValue(false), relevantRuleFor: vi.fn(), rules: [] } }) }; const mockActor = { actor: ActorType.USER, actorId: "user-123", actorAuthMethod: AuthMethod.EMAIL, actorOrgId: "org-123" }; beforeEach(() => { // Reset all mocks before each test vi.clearAllMocks(); // Mock ForbiddenError.from static method vi.spyOn(ForbiddenError, "from").mockReturnValue({ throwUnlessCan: vi.fn() } as any); // Ensure the permission service mock is properly set up (mockPermissionService.getProjectPermission as any).mockResolvedValue({ permission: { throwUnlessCan: vi.fn(), can: vi.fn().mockReturnValue(true), cannot: vi.fn().mockReturnValue(false), relevantRuleFor: vi.fn(), rules: [], detectSubjectType: vi.fn() } }); service = certificateV3ServiceFactory({ certificateDAL: mockCertificateDAL, certificateAuthorityDAL: mockCertificateAuthorityDAL, certificateProfileDAL: mockCertificateProfileDAL, certificateTemplateV2Service: mockCertificateTemplateV2Service, internalCaService: mockInternalCaService, permissionService: mockPermissionService }); }); afterEach(() => { vi.clearAllMocks(); vi.restoreAllMocks(); // Ensure static method mocks are properly restored }); describe("issueCertificateFromProfile", () => { const mockCertificateRequest = { commonName: "test.example.com", keyUsages: [CertKeyUsageType.DIGITAL_SIGNATURE], extendedKeyUsages: [CertExtendedKeyUsageType.SERVER_AUTH], validity: { ttl: "30d" }, signatureAlgorithm: "RSA-SHA256", keyAlgorithm: "RSA_2048" }; it("should issue certificate successfully for API enrollment profile", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.API, caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile", description: "Test profile" }; const mockCA = { id: "ca-123", projectId: "project-123", externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-123" }, name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true }; const mockTemplate = { id: "template-123", name: "Test Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", description: "Test template", signatureAlgorithm: { defaultAlgorithm: "RSA-SHA256" }, keyAlgorithm: { defaultKeyType: "RSA_2048" }, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ] }; const mockCertificateResult = { certificate: "cert", certificateChain: "chain", issuingCaCertificate: "issuing-ca", privateKey: "key", serialNumber: "123456", ca: { id: "ca-123", projectId: "project-123", name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: null, notAfter: null, activeCaCertId: "cert-123", caId: "ca-123" } } }; const mockCertRecord = { id: "cert-123", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", commonName: "test.example.com", friendlyName: "Test Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-123", certificateTemplateId: "template-123", revokedAt: null, revokedBy: null }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(mockCA); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(mockTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue(mockCertificateResult as any); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue(mockCertRecord); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue(mockCertRecord); const result = await service.issueCertificateFromProfile({ profileId, certificateRequest: mockCertificateRequest, ...mockActor }); expect(result).toHaveProperty("certificate"); expect(result).toHaveProperty("issuingCaCertificate"); expect(result).toHaveProperty("certificateChain"); expect(result).toHaveProperty("privateKey"); expect(result).toHaveProperty("serialNumber", "123456"); expect(result).toHaveProperty("certificateId", "cert-123"); }); it("should correctly map camelCase key usages to snake_case before validation", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.API, caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-camel", description: "Test camelCase profile", estConfigId: null, apiConfigId: null }; const mockCA = { id: "ca-123", projectId: "project-123", externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-123" }, name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true }; const mockTemplate = { id: "template-123", name: "Test Template for CamelCase", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", description: "Test template for camelCase validation", signatureAlgorithm: { defaultAlgorithm: "RSA-SHA256" }, keyAlgorithm: { defaultKeyType: "RSA_2048" }, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; const mockCertRecord = { id: "cert-123", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", commonName: "test.example.com", friendlyName: "Test Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-123", certificateTemplateId: "template-123", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }; const camelCaseRequest = { commonName: "test.example.com", keyUsages: [ CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.NON_REPUDIATION, CertKeyUsageType.KEY_AGREEMENT, CertKeyUsageType.CRL_SIGN, CertKeyUsageType.DECIPHER_ONLY ], extendedKeyUsages: [ CertExtendedKeyUsageType.CLIENT_AUTH, CertExtendedKeyUsageType.CODE_SIGNING, CertExtendedKeyUsageType.OCSP_SIGNING, CertExtendedKeyUsageType.SERVER_AUTH ], validity: { ttl: "10d" } }; const mockCertificateResultWithCa = { certificate: "cert", certificateChain: "chain", issuingCaCertificate: "issuing-ca", privateKey: "key", serialNumber: "123456", ca: { id: "ca-123", projectId: "project-123", name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: null, notAfter: null, activeCaCertId: "cert-123", caId: "ca-123" } } }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(mockCA); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(mockTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue(mockCertificateResultWithCa as any); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue(mockCertRecord); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue(mockCertRecord); await service.issueCertificateFromProfile({ profileId, certificateRequest: camelCaseRequest, ...mockActor }); // Verify that the template validation service was called with mapped snake_case values expect(mockCertificateTemplateV2Service.validateCertificateRequest).toHaveBeenCalledWith( "template-123", expect.objectContaining({ keyUsages: [ CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.NON_REPUDIATION, CertKeyUsageType.KEY_AGREEMENT, CertKeyUsageType.CRL_SIGN, CertKeyUsageType.DECIPHER_ONLY ], extendedKeyUsages: [ CertExtendedKeyUsageType.CLIENT_AUTH, CertExtendedKeyUsageType.CODE_SIGNING, CertExtendedKeyUsageType.OCSP_SIGNING, CertExtendedKeyUsageType.SERVER_AUTH ] }) ); }); it("should throw ForbiddenRequestError when profile is not configured for API enrollment", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.EST, // Wrong enrollment type caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-est", description: "Test EST profile", estConfigId: null, apiConfigId: null }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); await expect( service.issueCertificateFromProfile({ profileId, certificateRequest: mockCertificateRequest, ...mockActor }) ).rejects.toThrow(ForbiddenRequestError); await expect( service.issueCertificateFromProfile({ profileId, certificateRequest: mockCertificateRequest, ...mockActor }) ).rejects.toThrow("Profile is not configured for api enrollment"); }); it("should throw NotFoundError when profile doesn't exist", async () => { const profileId = "non-existent-profile"; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(undefined); await expect( service.issueCertificateFromProfile({ profileId, certificateRequest: mockCertificateRequest, ...mockActor }) ).rejects.toThrow(NotFoundError); }); }); describe("signCertificateFromProfile", () => { const mockCSR = "-----BEGIN CERTIFICATE REQUEST-----\nMIIC..."; const mockValidity = { ttl: "30d" }; it("should sign certificate successfully for API enrollment profile", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.API, caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-sign", description: "Test signing profile", estConfigId: null, apiConfigId: null }; const mockCA = { id: "ca-123", projectId: "project-123", externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-123" }, name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true }; const mockSignResult = { certificate: "signed-cert", certificateChain: "chain", issuingCaCertificate: "issuing-ca", serialNumber: "789012", commonName: "test.example.com", ca: { id: "ca-123", projectId: "project-123", name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: null, notAfter: null, activeCaCertId: "cert-123", caId: "ca-123" } } }; const mockCertRecord = { id: "cert-456", serialNumber: "789012", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", commonName: "test.example.com", friendlyName: "Test Signing Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-123", certificateTemplateId: "template-123", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }; const mockTemplate = { id: "template-123", name: "Test Signing Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", description: "Test template for signing certificates", signatureAlgorithm: { defaultAlgorithm: "RSA-SHA256" }, keyAlgorithm: { defaultKeyType: "RSA_2048" }, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(mockCA); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(mockTemplate); vi.mocked(mockInternalCaService.signCertFromCa).mockResolvedValue(mockSignResult as any); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue(mockCertRecord); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue(mockCertRecord); const result = await service.signCertificateFromProfile({ profileId, csr: mockCSR, validity: mockValidity, ...mockActor }); expect(result).toHaveProperty("certificate"); expect(result).toHaveProperty("issuingCaCertificate"); expect(result).toHaveProperty("certificateChain"); expect(result).toHaveProperty("serialNumber", "789012"); expect(result).toHaveProperty("certificateId", "cert-456"); expect(result).not.toHaveProperty("privateKey"); }); it("should throw ForbiddenRequestError when profile is not configured for API enrollment", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.EST, // Wrong enrollment type caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-est-sign", description: "Test EST signing profile", estConfigId: null, apiConfigId: null }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); await expect( service.signCertificateFromProfile({ profileId, csr: mockCSR, validity: mockValidity, ...mockActor }) ).rejects.toThrow(ForbiddenRequestError); await expect( service.signCertificateFromProfile({ profileId, csr: mockCSR, validity: mockValidity, ...mockActor }) ).rejects.toThrow("Profile is not configured for api enrollment"); }); }); describe("orderCertificateFromProfile", () => { const mockCertificateOrder = { altNames: [{ type: ACMESANType.DNS, value: "example.com" }], validity: { ttl: "30d" }, commonName: "example.com", keyUsages: [CertKeyUsageType.DIGITAL_SIGNATURE], extendedKeyUsages: [CertExtendedKeyUsageType.SERVER_AUTH], signatureAlgorithm: "RSA-SHA256", keyAlgorithm: "RSA_2048" }; it("should create order successfully for API enrollment profile", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.API, caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-order", description: "Test order profile", estConfigId: null, apiConfigId: null }; const mockCA = { id: "ca-123", projectId: "project-123", externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-123" }, name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true }; const mockTemplate = { id: "template-123", name: "Test Order Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", description: "Test template for ordering certificates", signatureAlgorithm: { defaultAlgorithm: "RSA-SHA256" }, keyAlgorithm: { defaultKeyType: "RSA_2048" }, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; const mockCertificateResult = { certificate: "cert", certificateChain: "chain", issuingCaCertificate: "issuing-ca", privateKey: "key", serialNumber: "123456", ca: { id: "ca-123", projectId: "project-123", name: "Test CA", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-123", parentCaId: null, type: "ROOT", friendlyName: "Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "Test CA", dn: "CN=Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: null, notAfter: null, activeCaCertId: "cert-123", caId: "ca-123" } } }; const mockCertRecord = { id: "cert-123", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-123", commonName: "example.com", friendlyName: "Test Order Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-123", certificateTemplateId: "template-123", revokedAt: null, altNames: JSON.stringify([{ type: "DNS", value: "example.com" }]), caCertId: null, keyUsages: ["DIGITAL_SIGNATURE"], extendedKeyUsages: ["SERVER_AUTH"], revocationReason: null, pkiSubscriberId: null, profileId: null }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(mockCA); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(mockTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue(mockCertificateResult as any); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue(mockCertRecord); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue(mockCertRecord); const result = await service.orderCertificateFromProfile({ profileId, certificateOrder: mockCertificateOrder, ...mockActor }); expect(result).toHaveProperty("orderId"); expect(result).toHaveProperty("status", "valid"); expect(result).toHaveProperty("certificate"); expect(result.subjectAlternativeNames).toHaveLength(1); expect(result.subjectAlternativeNames[0]).toEqual({ type: ACMESANType.DNS, value: "example.com", status: CertificateOrderStatus.VALID }); }); it("should throw ForbiddenRequestError when profile is not configured for API enrollment", async () => { const profileId = "profile-123"; const mockProfile = { id: profileId, projectId: "project-123", enrollmentType: EnrollmentType.EST, // Wrong enrollment type caId: "ca-123", certificateTemplateId: "template-123", createdAt: new Date(), updatedAt: new Date(), slug: "test-profile-est-order", description: "Test EST order profile", estConfigId: null, apiConfigId: null }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); await expect( service.orderCertificateFromProfile({ profileId, certificateOrder: mockCertificateOrder, ...mockActor }) ).rejects.toThrow(ForbiddenRequestError); await expect( service.orderCertificateFromProfile({ profileId, certificateOrder: mockCertificateOrder, ...mockActor }) ).rejects.toThrow("Profile is not configured for api enrollment"); }); }); describe("algorithm compatibility (integration tests)", () => { const mockProfile = { id: "profile-1", slug: "test-profile", projectId: "project-1", caId: "ca-1", certificateTemplateId: "template-1", enrollmentType: EnrollmentType.API, createdAt: new Date(), updatedAt: new Date(), description: "Test profile for algorithm compatibility", estConfigId: null, apiConfigId: null }; const mockCertificateRequest = { commonName: "test.example.com", validity: { ttl: "30d" }, keyUsages: [CertKeyUsageType.DIGITAL_SIGNATURE], extendedKeyUsages: [CertExtendedKeyUsageType.SERVER_AUTH] }; beforeEach(() => { vi.clearAllMocks(); }); it("should successfully process RSA algorithms with RSA CAs", async () => { const rsaCa = { id: "ca-1", projectId: "project-1", status: "active", name: "RSA Test CA", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-1", parentCaId: null, type: "ROOT", friendlyName: "RSA Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "RSA Test CA", dn: "CN=RSA Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_2048", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-1" } }; const rsaTemplate = { id: "template-1", name: "RSA Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", description: "RSA template for algorithm compatibility", signatureAlgorithm: { allowedAlgorithms: ["SHA256-RSA", "SHA384-RSA"] }, keyAlgorithm: null, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(rsaCa); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(rsaTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue({ certificate: "cert", certificateChain: "chain", issuingCaCertificate: "ca-cert", privateKey: "key", serialNumber: "123456", ca: rsaCa as any }); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); // Should not throw - RSA CA is compatible with RSA signature algorithms await expect( service.issueCertificateFromProfile({ profileId: mockProfile.id, certificateRequest: { ...mockCertificateRequest, signatureAlgorithm: "RSA-SHA256" }, ...mockActor }) ).resolves.toBeDefined(); }); it("should successfully process ECDSA algorithms with EC CAs", async () => { const ecCa = { id: "ca-1", projectId: "project-1", status: "active", name: "EC Test CA", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-1", parentCaId: null, type: "ROOT", friendlyName: "EC Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "EC Test CA", dn: "CN=EC Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "EC_prime256v1", notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-1" } }; const ecdsaTemplate = { id: "template-1", name: "ECDSA Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", description: "ECDSA template for algorithm compatibility", signatureAlgorithm: { allowedAlgorithms: ["SHA256-ECDSA", "SHA384-ECDSA"] }, keyAlgorithm: null, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(ecCa); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(ecdsaTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue({ certificate: "cert", certificateChain: "chain", issuingCaCertificate: "ca-cert", privateKey: "key", serialNumber: "123456", ca: ecCa as any }); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); // Should not throw - EC CA is compatible with ECDSA signature algorithms await expect( service.issueCertificateFromProfile({ profileId: mockProfile.id, certificateRequest: { ...mockCertificateRequest, signatureAlgorithm: "ECDSA-SHA256" }, ...mockActor }) ).resolves.toBeDefined(); }); it("should dynamically support new RSA key sizes", async () => { const rsa8192Ca = { id: "ca-1", projectId: "project-1", status: "active", name: "RSA 8192 Test CA", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-1", parentCaId: null, type: "ROOT", friendlyName: "RSA 8192 Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "RSA 8192 Test CA", dn: "CN=RSA 8192 Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "RSA_8192", // Future RSA key size notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-1" } }; const rsaTemplate = { id: "template-1", name: "RSA 8192 Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", description: "RSA 8192 template for future key sizes", signatureAlgorithm: { allowedAlgorithms: ["SHA256-RSA"] }, keyAlgorithm: null, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(rsa8192Ca); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(rsaTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue({ certificate: "cert", certificateChain: "chain", issuingCaCertificate: "ca-cert", privateKey: "key", serialNumber: "123456", ca: rsa8192Ca as any }); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); // Should not throw - dynamic check supports new RSA key sizes await expect( service.issueCertificateFromProfile({ profileId: mockProfile.id, certificateRequest: { ...mockCertificateRequest, signatureAlgorithm: "RSA-SHA256" }, ...mockActor }) ).resolves.toBeDefined(); }); it("should dynamically support new EC curve types", async () => { const newEcCa = { id: "ca-1", projectId: "project-1", status: "active", name: "EC secp521r1 Test CA", createdAt: new Date(), updatedAt: new Date(), enableDirectIssuance: true, externalCa: undefined, internalCa: { id: "internal-ca-1", parentCaId: null, type: "ROOT", friendlyName: "EC secp521r1 Test CA", organization: "Test Org", ou: "Test OU", country: "US", province: "CA", locality: "SF", commonName: "EC secp521r1 Test CA", dn: "CN=EC secp521r1 Test CA", serialNumber: "123", maxPathLength: null, keyAlgorithm: "EC_secp521r1", // Future EC curve notBefore: undefined, notAfter: undefined, activeCaCertId: "cert-123", caId: "ca-1" } }; const ecdsaTemplate = { id: "template-1", name: "ECDSA secp521r1 Template", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", description: "ECDSA secp521r1 template for future EC curves", signatureAlgorithm: { allowedAlgorithms: ["SHA384-ECDSA"] }, keyAlgorithm: null, attributes: [ { type: CertSubjectAttributeType.COMMON_NAME, include: CertIncludeType.OPTIONAL, value: ["example.com"] } ], subject: undefined, sans: undefined, keyUsages: undefined, extendedKeyUsages: undefined, algorithms: undefined, validity: undefined }; vi.mocked(mockCertificateProfileDAL.findByIdWithConfigs).mockResolvedValue(mockProfile); vi.mocked(mockCertificateAuthorityDAL.findByIdWithAssociatedCa).mockResolvedValue(newEcCa); vi.mocked(mockCertificateTemplateV2Service.validateCertificateRequest).mockResolvedValue({ isValid: true, errors: [], warnings: [] }); vi.mocked(mockCertificateTemplateV2Service.getTemplateV2ById).mockResolvedValue(ecdsaTemplate); vi.mocked(mockInternalCaService.issueCertFromCa).mockResolvedValue({ certificate: "cert", certificateChain: "chain", issuingCaCertificate: "ca-cert", privateKey: "key", serialNumber: "123456", ca: newEcCa as any }); vi.mocked(mockCertificateDAL.findOne).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); vi.mocked(mockCertificateDAL.updateById).mockResolvedValue({ id: "cert-1", serialNumber: "123456", status: "ACTIVE", createdAt: new Date(), updatedAt: new Date(), projectId: "project-1", commonName: "test.example.com", friendlyName: "Test Algorithm Cert", notBefore: new Date(), notAfter: new Date(), caId: "ca-1", certificateTemplateId: "template-1", revokedAt: null, altNames: null, caCertId: null, keyUsages: null, extendedKeyUsages: null, revocationReason: null, pkiSubscriberId: null, profileId: null }); // Should not throw - dynamic check supports new EC curves await expect( service.issueCertificateFromProfile({ profileId: mockProfile.id, certificateRequest: { ...mockCertificateRequest, signatureAlgorithm: "ECDSA-SHA384" }, ...mockActor }) ).resolves.toBeDefined(); }); }); });