PKI Syncs revamp

This commit is contained in:
Carlos Monastyrski
2025-10-29 13:01:27 -03:00
parent 3e83ae95d1
commit 39eecb328d
64 changed files with 2799 additions and 350 deletions
@@ -1,3 +1,5 @@
import RE2 from "re2";
import { TDbClient } from "@app/db";
import { TableName, TCertificates } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
@@ -60,11 +62,13 @@ export const certificateDALFactory = (db: TDbClient) => {
.where(`${TableName.Project}.id`, projectId);
if (friendlyName) {
query = query.andWhere(`${TableName.Certificate}.friendlyName`, friendlyName);
const sanitizedValue = String(friendlyName).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.friendlyName`, "like", `%${sanitizedValue}%`);
}
if (commonName) {
query = query.andWhere(`${TableName.Certificate}.commonName`, commonName);
const sanitizedValue = String(commonName).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.commonName`, "like", `%${sanitizedValue}%`);
}
const count = await query.count("*").first();
@@ -114,6 +118,109 @@ export const certificateDALFactory = (db: TDbClient) => {
}
};
const findActiveCertificatesByIds = async (certificateIds: string[]): Promise<TCertificates[]> => {
try {
if (certificateIds.length === 0) {
return [];
}
const certs = await db
.replicaNode()(TableName.Certificate)
.whereIn("id", certificateIds)
.where({ status: CertStatus.ACTIVE })
.where("notAfter", ">", new Date())
.orderBy("notBefore", "desc")
.select("*");
return certs;
} catch (error) {
throw new DatabaseError({ error, name: "Find active certificates by IDs" });
}
};
const findActiveCertificatesForSync = async (
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
options?: { limit?: number; offset?: number }
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
try {
let query = db
.replicaNode()(TableName.Certificate)
.leftJoin(TableName.CertificateSecret, `${TableName.Certificate}.id`, `${TableName.CertificateSecret}.certId`)
.select(selectAllTableCols(TableName.Certificate))
.select(db.ref(`${TableName.CertificateSecret}.certId`).as("privateKeyRef"))
.where({ status: CertStatus.ACTIVE })
.where("notAfter", ">", new Date())
.whereNull("renewedByCertificateId");
Object.entries(filter).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
if (key === "friendlyName" || key === "commonName") {
const sanitizedValue = String(value).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.${key}`, "like", `%${sanitizedValue}%`);
} else {
query = query.andWhere(`${TableName.Certificate}.${key}`, value);
}
}
});
if (options?.offset) {
query = query.offset(options.offset);
}
if (options?.limit) {
query = query.limit(options.limit);
}
query = query.orderBy("createdAt", "desc");
const certs = await query;
return certs.map((cert) => ({ ...cert, hasPrivateKey: Boolean(cert.privateKeyRef) }));
} catch (error) {
throw new DatabaseError({ error, name: "Find active certificates for sync" });
}
};
const countActiveCertificatesForSync = async ({
projectId,
friendlyName,
commonName
}: {
projectId: string;
friendlyName?: string;
commonName?: string;
}) => {
try {
interface CountResult {
count: string;
}
let query = db
.replicaNode()(TableName.Certificate)
.join(TableName.CertificateAuthority, `${TableName.Certificate}.caId`, `${TableName.CertificateAuthority}.id`)
.join(TableName.Project, `${TableName.CertificateAuthority}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.id`, projectId)
.where(`${TableName.Certificate}.status`, CertStatus.ACTIVE)
.where(`${TableName.Certificate}.notAfter`, ">", new Date())
.whereNull(`${TableName.Certificate}.renewedByCertificateId`);
if (friendlyName) {
const sanitizedValue = String(friendlyName).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.friendlyName`, "like", `%${sanitizedValue}%`);
}
if (commonName) {
const sanitizedValue = String(commonName).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.commonName`, "like", `%${sanitizedValue}%`);
}
const count = await query.count("*").first();
return parseInt((count as unknown as CountResult).count || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "Count active certificates for sync" });
}
};
const findCertificatesEligibleForRenewal = async ({
limit,
offset
@@ -159,7 +266,7 @@ export const certificateDALFactory = (db: TDbClient) => {
};
const findWithPrivateKeyInfo = async (
filter: Partial<TCertificates>,
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] }
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
try {
@@ -167,8 +274,18 @@ export const certificateDALFactory = (db: TDbClient) => {
.replicaNode()(TableName.Certificate)
.leftJoin(TableName.CertificateSecret, `${TableName.Certificate}.id`, `${TableName.CertificateSecret}.certId`)
.select(selectAllTableCols(TableName.Certificate))
.select(db.ref(`${TableName.CertificateSecret}.certId`).as("privateKeyRef"))
.where(filter);
.select(db.ref(`${TableName.CertificateSecret}.certId`).as("privateKeyRef"));
Object.entries(filter).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
if (key === "friendlyName" || key === "commonName") {
const sanitizedValue = String(value).replace(new RE2("[%_\\\\]", "g"), "\\$&");
query = query.andWhere(`${TableName.Certificate}.${key}`, "like", `%${sanitizedValue}%`);
} else {
query = query.andWhere(`${TableName.Certificate}.${key}`, value);
}
}
});
if (options?.offset) {
query = query.offset(options.offset);
@@ -197,10 +314,13 @@ export const certificateDALFactory = (db: TDbClient) => {
return {
...certificateOrm,
countCertificatesInProject,
countActiveCertificatesForSync,
countCertificatesForPkiSubscriber,
findLatestActiveCertForSubscriber,
findAllActiveCertsForSubscriber,
findExpiredSyncedCertificates,
findActiveCertificatesByIds,
findActiveCertificatesForSync,
findCertificatesEligibleForRenewal,
findWithPrivateKeyInfo
};
@@ -18,12 +18,13 @@ import { TCertificateAuthorityDALFactory } from "@app/services/certificate-autho
import { CaCapability, CaType } from "@app/services/certificate-authority/certificate-authority-enums";
import { caSupportsCapability } from "@app/services/certificate-authority/certificate-authority-maps";
import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
import { TCertificateSyncDALFactory } from "@app/services/certificate-sync/certificate-sync-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { triggerAutoSyncForCertificate } from "@app/services/pki-sync/pki-sync-utils";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -57,6 +58,7 @@ type TCertificateServiceFactoryDep = {
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
certificateSyncDAL: Pick<TCertificateSyncDALFactory, "findPkiSyncIdsByCertificateId">;
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
};
@@ -76,6 +78,7 @@ export const certificateServiceFactory = ({
projectDAL,
kmsService,
permissionService,
certificateSyncDAL,
pkiSyncDAL,
pkiSyncQueue
}: TCertificateServiceFactoryDep) => {
@@ -166,10 +169,12 @@ export const certificateServiceFactory = ({
const deletedCert = await certificateDAL.deleteById(cert.id);
// Trigger auto sync for PKI syncs connected to this certificate's subscriber
if (cert.pkiSubscriberId) {
await triggerAutoSyncForSubscriber(cert.pkiSubscriberId, { pkiSyncDAL, pkiSyncQueue });
}
// Trigger auto sync for PKI syncs connected to this certificate
await triggerAutoSyncForCertificate(cert.id, {
certificateSyncDAL,
pkiSyncDAL,
pkiSyncQueue
});
return {
deletedCert
@@ -235,10 +240,12 @@ export const certificateServiceFactory = ({
}
);
// Trigger auto sync for PKI syncs connected to this certificate's subscriber
if (cert.pkiSubscriberId) {
await triggerAutoSyncForSubscriber(cert.pkiSubscriberId, { pkiSyncDAL, pkiSyncQueue });
}
// Trigger auto sync for PKI syncs connected to this certificate
await triggerAutoSyncForCertificate(cert.id, {
certificateSyncDAL,
pkiSyncDAL,
pkiSyncQueue
});
// Note: External CA revocation handling would go here for supported CA types
// Currently, only internal CAs and ACME CAs support revocation