diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index fb91e58e7..4c885c3cb 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -6,6 +6,7 @@ import { TAccessApprovalRequestServiceFactory } from "@app/ee/services/access-ap import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types"; import { TAuditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service"; +import { TCertificateAuthorityCrlServiceFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-service"; import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service"; import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service"; import { TGroupServiceFactory } from "@app/ee/services/group/group-service"; @@ -141,6 +142,7 @@ declare module "fastify" { auditLogStream: TAuditLogStreamServiceFactory; certificate: TCertificateServiceFactory; certificateAuthority: TCertificateAuthorityServiceFactory; + certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory; secretScanning: TSecretScanningServiceFactory; license: TLicenseServiceFactory; trustedIp: TTrustedIpServiceFactory; diff --git a/backend/src/ee/routes/v1/certificate-authority-crl-router.ts b/backend/src/ee/routes/v1/certificate-authority-crl-router.ts new file mode 100644 index 000000000..10792f508 --- /dev/null +++ b/backend/src/ee/routes/v1/certificate-authority-crl-router.ts @@ -0,0 +1,86 @@ +import { z } from "zod"; + +import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs"; +import { readLimit } from "@app/server/config/rateLimiter"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerCaCrlRouter = async (server: FastifyZodProvider) => { + server.route({ + method: "GET", + url: "/:caId/crl", + config: { + rateLimit: readLimit + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + schema: { + description: "Get CRL of the CA", + params: z.object({ + caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CRL.caId) + }), + response: { + 200: z.object({ + crl: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CRL.crl) + }) + } + }, + handler: async (req) => { + const { crl, ca } = await server.services.certificateAuthorityCrl.getCaCrl({ + caId: req.params.caId, + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId + }); + + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: ca.projectId, + event: { + type: EventType.GET_CA_CRL, + metadata: { + caId: ca.id, + dn: ca.dn + } + } + }); + + return { + crl + }; + } + }); + + // server.route({ + // method: "GET", + // url: "/:caId/crl/rotate", + // config: { + // rateLimit: writeLimit + // }, + // onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + // schema: { + // description: "Rotate CRL of the CA", + // params: z.object({ + // caId: z.string().trim() + // }), + // response: { + // 200: z.object({ + // message: z.string() + // }) + // } + // }, + // handler: async (req) => { + // await server.services.certificateAuthority.rotateCaCrl({ + // caId: req.params.caId, + // actor: req.permission.type, + // actorId: req.permission.id, + // actorAuthMethod: req.permission.authMethod, + // actorOrgId: req.permission.orgId + // }); + // return { + // message: "Successfully rotated CA CRL" + // }; + // } + // }); +}; diff --git a/backend/src/ee/routes/v1/index.ts b/backend/src/ee/routes/v1/index.ts index 16e23eb88..41d3e6bda 100644 --- a/backend/src/ee/routes/v1/index.ts +++ b/backend/src/ee/routes/v1/index.ts @@ -1,6 +1,7 @@ import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router"; import { registerAccessApprovalRequestRouter } from "./access-approval-request-router"; import { registerAuditLogStreamRouter } from "./audit-log-stream-router"; +import { registerCaCrlRouter } from "./certificate-authority-crl-router"; import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router"; import { registerDynamicSecretRouter } from "./dynamic-secret-router"; import { registerGroupRouter } from "./group-router"; @@ -54,6 +55,13 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => { { prefix: "/dynamic-secrets" } ); + await server.register( + async (pkiRouter) => { + await pkiRouter.register(registerCaCrlRouter, { prefix: "/ca" }); + }, + { prefix: "/pki" } + ); + await server.register(registerSamlRouter, { prefix: "/sso" }); await server.register(registerScimRouter, { prefix: "/scim" }); await server.register(registerLdapRouter, { prefix: "/ldap" }); diff --git a/backend/src/services/certificate-authority/certificate-authority-crl-dal.ts b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-dal.ts similarity index 100% rename from backend/src/services/certificate-authority/certificate-authority-crl-dal.ts rename to backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-dal.ts diff --git a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts new file mode 100644 index 000000000..c8b56561e --- /dev/null +++ b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts @@ -0,0 +1,172 @@ +import { ForbiddenError } from "@casl/ability"; +import * as x509 from "@peculiar/x509"; + +import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; +import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { BadRequestError } from "@app/lib/errors"; +import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; +import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; + +import { TGetCrl } from "./certificate-authority-crl-types"; + +type TCertificateAuthorityCrlServiceFactoryDep = { + certificateAuthorityDAL: Pick; + certificateAuthorityCrlDAL: Pick; + projectDAL: Pick; + kmsService: Pick; + permissionService: Pick; + licenseService: Pick; +}; + +export type TCertificateAuthorityCrlServiceFactory = ReturnType; + +export const certificateAuthorityCrlServiceFactory = ({ + certificateAuthorityDAL, + certificateAuthorityCrlDAL, + projectDAL, + kmsService, + permissionService, + licenseService +}: TCertificateAuthorityCrlServiceFactoryDep) => { + /** + * Return the Certificate Revocation List (CRL) for CA with id [caId] + */ + const getCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCrl) => { + const ca = await certificateAuthorityDAL.findById(caId); + if (!ca) throw new BadRequestError({ message: "CA not found" }); + + const { permission } = await permissionService.getProjectPermission( + actor, + actorId, + ca.projectId, + actorAuthMethod, + actorOrgId + ); + + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Read, + ProjectPermissionSub.CertificateAuthorities + ); + + const plan = await licenseService.getPlan(actorOrgId); + if (!plan.caCrl) + throw new BadRequestError({ + message: + "Failed to get CA certificate revocation list (CRL) due to plan restriction. Upgrade plan to get the CA CRL." + }); + + const caCrl = await certificateAuthorityCrlDAL.findOne({ caId: ca.id }); + if (!caCrl) throw new BadRequestError({ message: "CRL not found" }); + + const keyId = await getProjectKmsCertificateKeyId({ + projectId: ca.projectId, + projectDAL, + kmsService + }); + + const decryptedCrl = await kmsService.decrypt({ + kmsId: keyId, + cipherTextBlob: caCrl.encryptedCrl + }); + + const crl = new x509.X509Crl(decryptedCrl); + + const base64crl = crl.toString("base64"); + const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`; + + return { + crl: crlPem, + ca + }; + }; + + // const rotateCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TRotateCrlDTO) => { + // const ca = await certificateAuthorityDAL.findById(caId); + // if (!ca) throw new BadRequestError({ message: "CA not found" }); + + // const { permission } = await permissionService.getProjectPermission( + // actor, + // actorId, + // ca.projectId, + // actorAuthMethod, + // actorOrgId + // ); + + // ForbiddenError.from(permission).throwUnlessCan( + // ProjectPermissionActions.Read, + // ProjectPermissionSub.CertificateAuthorities + // ); + + // const caSecret = await certificateAuthoritySecretDAL.findOne({ caId: ca.id }); + + // const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm); + + // const keyId = await getProjectKmsCertificateKeyId({ + // projectId: ca.projectId, + // projectDAL, + // kmsService + // }); + + // const privateKey = await kmsService.decrypt({ + // kmsId: keyId, + // cipherTextBlob: caSecret.encryptedPrivateKey + // }); + + // const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" }); + // const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [ + // "sign" + // ]); + + // const revokedCerts = await certificateDAL.find({ + // caId: ca.id, + // status: CertStatus.REVOKED + // }); + + // const crl = await x509.X509CrlGenerator.create({ + // issuer: ca.dn, + // thisUpdate: new Date(), + // nextUpdate: new Date("2025/12/12"), + // entries: revokedCerts.map((revokedCert) => { + // return { + // serialNumber: revokedCert.serialNumber, + // revocationDate: new Date(revokedCert.revokedAt as Date), + // reason: revokedCert.revocationReason as number, + // invalidity: new Date("2022/01/01"), + // issuer: ca.dn + // }; + // }), + // signingAlgorithm: alg, + // signingKey: sk + // }); + + // const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({ + // kmsId: keyId, + // plainText: Buffer.from(new Uint8Array(crl.rawData)) + // }); + + // await certificateAuthorityCrlDAL.update( + // { + // caId: ca.id + // }, + // { + // encryptedCrl + // } + // ); + + // const base64crl = crl.toString("base64"); + // const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`; + + // return { + // crl: crlPem + // }; + // }; + + return { + getCaCrl + // rotateCaCrl + }; +}; diff --git a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-types.ts b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-types.ts new file mode 100644 index 000000000..fc31e9eef --- /dev/null +++ b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-types.ts @@ -0,0 +1,5 @@ +import { TProjectPermission } from "@app/lib/types"; + +export type TGetCrl = { + caId: string; +} & Omit; diff --git a/backend/src/ee/services/license/__mocks__/licence-fns.ts b/backend/src/ee/services/license/__mocks__/licence-fns.ts index b5cbf103e..ddbffba45 100644 --- a/backend/src/ee/services/license/__mocks__/licence-fns.ts +++ b/backend/src/ee/services/license/__mocks__/licence-fns.ts @@ -25,6 +25,7 @@ export const getDefaultOnPremFeatures = () => { trial_end: null, has_used_trial: true, secretApproval: false, - secretRotation: true + secretRotation: true, + caCrl: false }; }; diff --git a/backend/src/ee/services/license/licence-fns.ts b/backend/src/ee/services/license/licence-fns.ts index 189a3c4e0..9d2c5a472 100644 --- a/backend/src/ee/services/license/licence-fns.ts +++ b/backend/src/ee/services/license/licence-fns.ts @@ -34,7 +34,8 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ trial_end: null, has_used_trial: true, secretApproval: false, - secretRotation: true + secretRotation: true, + caCrl: false }); export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index 0c8fdc197..e23ff2c84 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -52,6 +52,7 @@ export type TFeatureSet = { has_used_trial: true; secretApproval: false; secretRotation: true; + caCrl: false; }; export type TOrgPlansTableDTO = { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index d9f4bf029..6b050daf6 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -13,6 +13,8 @@ import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-lo import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; import { auditLogStreamDALFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-dal"; import { auditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service"; +import { certificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; +import { certificateAuthorityCrlServiceFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-service"; import { dynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal"; import { dynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service"; import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/providers"; @@ -75,7 +77,6 @@ import { certificateBodyDALFactory } from "@app/services/certificate/certificate import { certificateDALFactory } from "@app/services/certificate/certificate-dal"; import { certificateServiceFactory } from "@app/services/certificate/certificate-service"; import { certificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal"; -import { certificateAuthorityCrlDALFactory } from "@app/services/certificate-authority/certificate-authority-crl-dal"; import { certificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; import { certificateAuthorityQueueFactory } from "@app/services/certificate-authority/certificate-authority-queue"; import { certificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal"; @@ -558,6 +559,15 @@ export const registerRoutes = async ( permissionService }); + const certificateAuthorityCrlService = certificateAuthorityCrlServiceFactory({ + certificateAuthorityDAL, + certificateAuthorityCrlDAL, + projectDAL, + kmsService, + permissionService, + licenseService + }); + const projectService = projectServiceFactory({ permissionService, projectDAL, @@ -945,6 +955,7 @@ export const registerRoutes = async ( auditLogStream: auditLogStreamService, certificate: certificateService, certificateAuthority: certificateAuthorityService, + certificateAuthorityCrl: certificateAuthorityCrlService, secretScanning: secretScanningService, license: licenseService, trustedIp: trustedIpService, diff --git a/backend/src/server/routes/v1/certificate-authority-router.ts b/backend/src/server/routes/v1/certificate-authority-router.ts index b0d39df6a..7573c0bd2 100644 --- a/backend/src/server/routes/v1/certificate-authority-router.ts +++ b/backend/src/server/routes/v1/certificate-authority-router.ts @@ -512,81 +512,4 @@ export const registerCaRouter = async (server: FastifyZodProvider) => { }; } }); - - server.route({ - method: "GET", - url: "/:caId/crl", - config: { - rateLimit: readLimit - }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), - schema: { - description: "Get CRL of the CA", - params: z.object({ - caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CRL.caId) - }), - response: { - 200: z.object({ - crl: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CRL.crl) - }) - } - }, - handler: async (req) => { - const { crl, ca } = await server.services.certificateAuthority.getCaCrl({ - caId: req.params.caId, - actor: req.permission.type, - actorId: req.permission.id, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId - }); - - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - projectId: ca.projectId, - event: { - type: EventType.GET_CA_CRL, - metadata: { - caId: ca.id, - dn: ca.dn - } - } - }); - - return { - crl - }; - } - }); - - // server.route({ - // method: "GET", - // url: "/:caId/crl/rotate", - // config: { - // rateLimit: writeLimit - // }, - // onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), - // schema: { - // description: "Rotate CRL of the CA", - // params: z.object({ - // caId: z.string().trim() - // }), - // response: { - // 200: z.object({ - // message: z.string() - // }) - // } - // }, - // handler: async (req) => { - // await server.services.certificateAuthority.rotateCaCrl({ - // caId: req.params.caId, - // actor: req.permission.type, - // actorId: req.permission.id, - // actorAuthMethod: req.permission.authMethod, - // actorOrgId: req.permission.orgId - // }); - // return { - // message: "Successfully rotated CA CRL" - // }; - // } - // }); }; diff --git a/backend/src/services/certificate-authority/certificate-authority-queue.ts b/backend/src/services/certificate-authority/certificate-authority-queue.ts index fed0a91bb..384f45c09 100644 --- a/backend/src/services/certificate-authority/certificate-authority-queue.ts +++ b/backend/src/services/certificate-authority/certificate-authority-queue.ts @@ -12,7 +12,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; -import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal"; +import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal"; import { keyAlgorithmToAlgCfg } from "./certificate-authority-fns"; import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal"; diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 7d753f879..2345180a3 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -13,9 +13,9 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; +import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types"; import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal"; -import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal"; import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal"; import { createDistinguishedName, @@ -33,10 +33,8 @@ import { TGetCaCertDTO, TGetCaCsrDTO, TGetCaDTO, - TGetCrl, TImportCertToCaDTO, TIssueCertFromCaDTO, - // TRotateCrlDTO, TSignIntermediateDTO, TUpdateCaDTO } from "./certificate-authority-types"; @@ -809,132 +807,6 @@ export const certificateAuthorityServiceFactory = ({ }; }; - /** - * Return the Certificate Revocation List (CRL) for CA with id [caId] - */ - const getCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCrl) => { - const ca = await certificateAuthorityDAL.findById(caId); - if (!ca) throw new BadRequestError({ message: "CA not found" }); - - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - ca.projectId, - actorAuthMethod, - actorOrgId - ); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.CertificateAuthorities - ); - - const caCrl = await certificateAuthorityCrlDAL.findOne({ caId: ca.id }); - if (!caCrl) throw new BadRequestError({ message: "CRL not found" }); - - const keyId = await getProjectKmsCertificateKeyId({ - projectId: ca.projectId, - projectDAL, - kmsService - }); - - const decryptedCrl = await kmsService.decrypt({ - kmsId: keyId, - cipherTextBlob: caCrl.encryptedCrl - }); - - const crl = new x509.X509Crl(decryptedCrl); - - const base64crl = crl.toString("base64"); - const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`; - - return { - crl: crlPem, - ca - }; - }; - - // const rotateCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TRotateCrlDTO) => { - // const ca = await certificateAuthorityDAL.findById(caId); - // if (!ca) throw new BadRequestError({ message: "CA not found" }); - - // const { permission } = await permissionService.getProjectPermission( - // actor, - // actorId, - // ca.projectId, - // actorAuthMethod, - // actorOrgId - // ); - - // ForbiddenError.from(permission).throwUnlessCan( - // ProjectPermissionActions.Read, - // ProjectPermissionSub.CertificateAuthorities - // ); - - // const caSecret = await certificateAuthoritySecretDAL.findOne({ caId: ca.id }); - - // const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm); - - // const keyId = await getProjectKmsCertificateKeyId({ - // projectId: ca.projectId, - // projectDAL, - // kmsService - // }); - - // const privateKey = await kmsService.decrypt({ - // kmsId: keyId, - // cipherTextBlob: caSecret.encryptedPrivateKey - // }); - - // const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" }); - // const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [ - // "sign" - // ]); - - // const revokedCerts = await certificateDAL.find({ - // caId: ca.id, - // status: CertStatus.REVOKED - // }); - - // const crl = await x509.X509CrlGenerator.create({ - // issuer: ca.dn, - // thisUpdate: new Date(), - // nextUpdate: new Date("2025/12/12"), - // entries: revokedCerts.map((revokedCert) => { - // return { - // serialNumber: revokedCert.serialNumber, - // revocationDate: new Date(revokedCert.revokedAt as Date), - // reason: revokedCert.revocationReason as number, - // invalidity: new Date("2022/01/01"), - // issuer: ca.dn - // }; - // }), - // signingAlgorithm: alg, - // signingKey: sk - // }); - - // const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({ - // kmsId: keyId, - // plainText: Buffer.from(new Uint8Array(crl.rawData)) - // }); - - // await certificateAuthorityCrlDAL.update( - // { - // caId: ca.id - // }, - // { - // encryptedCrl - // } - // ); - - // const base64crl = crl.toString("base64"); - // const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`; - - // return { - // crl: crlPem - // }; - // }; - return { createCa, getCaById, @@ -944,8 +816,6 @@ export const certificateAuthorityServiceFactory = ({ getCaCert, signIntermediate, importCertToCa, - issueCertFromCa, - getCaCrl - // rotateCaCrl + issueCertFromCa }; }; diff --git a/backend/src/services/certificate-authority/certificate-authority-types.ts b/backend/src/services/certificate-authority/certificate-authority-types.ts index d6e17159e..3ba7624c0 100644 --- a/backend/src/services/certificate-authority/certificate-authority-types.ts +++ b/backend/src/services/certificate-authority/certificate-authority-types.ts @@ -3,9 +3,9 @@ import { TCertificateDALFactory } from "@app/services/certificate/certificate-da import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { CertKeyAlgorithm } from "../certificate/certificate-types"; import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal"; -import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal"; import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal"; import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal"; @@ -80,14 +80,6 @@ export type TIssueCertFromCaDTO = { notAfter?: string; } & Omit; -export type TGetCrl = { - caId: string; -} & Omit; - -export type TRotateCrlDTO = { - caId: string; -} & Omit; - export type TDNParts = { commonName?: string; organization?: string; diff --git a/backend/src/services/certificate/certificate-service.ts b/backend/src/services/certificate/certificate-service.ts index 0a7548df8..ba865caa1 100644 --- a/backend/src/services/certificate/certificate-service.ts +++ b/backend/src/services/certificate/certificate-service.ts @@ -1,12 +1,12 @@ import { ForbiddenError } from "@casl/ability"; import * as x509 from "@peculiar/x509"; +import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal"; import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal"; import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal"; -import { TCertificateAuthorityCrlDALFactory } from "@app/services/certificate-authority/certificate-authority-crl-dal"; import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index 45414292d..66959ad1b 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -25,14 +25,15 @@ export type SubscriptionPlan = { ldap: boolean; groups: boolean; status: - | "incomplete" - | "incomplete_expired" - | "trialing" - | "active" - | "past_due" - | "canceled" - | "unpaid" - | null; + | "incomplete" + | "incomplete_expired" + | "trialing" + | "active" + | "past_due" + | "canceled" + | "unpaid" + | null; trial_end: number | null; has_used_trial: boolean; + caCrl: boolean; }; diff --git a/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaSection.tsx b/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaSection.tsx index ef18f9d57..86e617a56 100644 --- a/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaSection.tsx +++ b/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaSection.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; -import { Button, DeleteActionModal } from "@app/components/v2"; +import { Button, DeleteActionModal, UpgradePlanModal } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { CaStatus, useDeleteCa, useUpdateCa } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; @@ -25,7 +25,8 @@ export const CaSection = () => { "installCaCert", "deleteCa", "caStatus", // enable / disable - "caCrl" // enable / disable + "caCrl", // enable / disable + "upgradePlan" ] as const); const onRemoveCaSubmit = async (caId: string) => { @@ -124,6 +125,11 @@ export const CaSection = () => { onUpdateCaStatus(popUp?.caStatus?.data as { caId: string; status: CaStatus }) } /> + handlePopUpToggle("upgradePlan", isOpen)} + text={(popUp.upgradePlan?.data as { description: string })?.description} + /> ); }; diff --git a/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaTable.tsx b/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaTable.tsx index 2fe5826b1..a35a4e432 100644 --- a/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaTable.tsx +++ b/frontend/src/views/Project/CertificatesPage/components/CaTab/components/CaTable.tsx @@ -27,7 +27,11 @@ import { Tooltip, Tr } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { + ProjectPermissionActions, + ProjectPermissionSub, + useSubscription, + useWorkspace} from "@app/context"; import { CaStatus, useListWorkspaceCas } from "@app/hooks/api"; import { caStatusToNameMap, caTypeToNameMap } from "@app/hooks/api/ca/constants"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -35,17 +39,19 @@ import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { handlePopUpOpen: ( popUpName: keyof UsePopUpState< - ["installCaCert", "caCert", "ca", "deleteCa", "caStatus", "caCrl"] + ["installCaCert", "caCert", "ca", "deleteCa", "caStatus", "caCrl", "upgradePlan"] >, data?: { caId?: string; dn?: string; status?: CaStatus; + description?: string; } ) => void; }; export const CaTable = ({ handlePopUpOpen }: Props) => { + const { subscription } = useSubscription(); const { currentWorkspace } = useWorkspace(); const { data, isLoading } = useListWorkspaceCas({ projectSlug: currentWorkspace?.slug ?? "" @@ -144,11 +150,18 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { !isAllowed && "pointer-events-none cursor-not-allowed opacity-50" )} - onClick={async () => - handlePopUpOpen("caCrl", { - caId: ca.id - }) - } + onClick={async () => { + if (!subscription?.caCrl) { + handlePopUpOpen("upgradePlan", { + description: + "You can use the certificate revocation list (CRL) feature if you upgrade your Infisical plan." + }); + } else { + handlePopUpOpen("caCrl", { + caId: ca.id + }); + } + }} disabled={!isAllowed} icon={} >