mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-18 22:29:15 +00:00
PKI Syncs revamp
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user