Improvements to cert-syncs

This commit is contained in:
Carlos Monastyrski
2025-09-22 11:45:45 -03:00
parent 27dbe5b013
commit 3587fbf98b
26 changed files with 180 additions and 190 deletions

View File

@@ -309,7 +309,7 @@ export const registerSyncPkiEndpoints = ({
server.route({
method: "POST",
url: "/:pkiSyncId/remove",
url: "/:pkiSyncId/remove-certificates",
config: {
rateLimit: writeLimit
},

View File

@@ -23,7 +23,9 @@ import {
} from "@app/services/certificate/certificate-types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-fns";
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -57,12 +59,8 @@ type TAcmeCertificateAuthorityFnsDeps = {
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
};

View File

@@ -26,7 +26,9 @@ import {
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
import { TPkiSubscriberProperties } from "@app/services/pki-subscriber/pki-subscriber-types";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-fns";
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -56,12 +58,8 @@ type TAzureAdCsCertificateAuthorityFnsDeps = {
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
};

View File

@@ -20,6 +20,8 @@ import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal"
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
import { SubscriberOperationStatus } from "../pki-subscriber/pki-subscriber-types";
import { TPkiSyncDALFactory } from "../pki-sync/pki-sync-dal";
import { TPkiSyncQueueFactory } from "../pki-sync/pki-sync-queue";
import { AcmeCertificateAuthorityFns } from "./acme/acme-certificate-authority-fns";
import { AzureAdCsCertificateAuthorityFns } from "./azure-ad-cs/azure-ad-cs-certificate-authority-fns";
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
@@ -50,12 +52,8 @@ type TCertificateAuthorityQueueFactoryDep = {
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
queueService: TQueueServiceFactory;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById" | "updateById">;
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
};
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;

View File

@@ -13,6 +13,8 @@ import { TCertificateDALFactory } from "../certificate/certificate-dal";
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
import { TPkiSyncDALFactory } from "../pki-sync/pki-sync-dal";
import { TPkiSyncQueueFactory } from "../pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "../project/project-dal";
import {
AcmeCertificateAuthorityFns,
@@ -68,12 +70,8 @@ type TCertificateAuthorityServiceFactoryDep = {
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
};
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;

View File

@@ -19,7 +19,9 @@ import {
TAltNameMapping
} from "@app/services/certificate/certificate-types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-fns";
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -52,12 +54,8 @@ type TInternalCertificateAuthorityFnsDeps = {
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
};
export const InternalCertificateAuthorityFns = ({

View File

@@ -22,7 +22,7 @@ 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 { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-fns";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";

View File

@@ -38,7 +38,7 @@ import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-fns";
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";

View File

@@ -0,0 +1,52 @@
import RE2 from "re2";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
/**
* Azure Key Vault naming constraints for certificates
*/
export const AZURE_KEY_VAULT_CERTIFICATE_NAMING = {
/**
* Regular expression pattern for valid Azure Key Vault certificate names
* Must contain only alphanumeric characters and hyphens (a-z, A-Z, 0-9, -)
* Must be 1-127 characters long
*/
NAME_PATTERN: new RE2("^[a-zA-Z0-9-]{1,127}$"),
/**
* String of characters that are forbidden in Azure Key Vault certificate names
*/
FORBIDDEN_CHARACTERS: "!@#$%^&*()+=[]{}|\\:;\"'<>,.?/~` _",
/**
* Maximum length for certificate names in Azure Key Vault
*/
MAX_NAME_LENGTH: 127,
/**
* Minimum length for certificate names in Azure Key Vault
*/
MIN_NAME_LENGTH: 1,
/**
* String representation of the allowed character pattern (for UI display)
*/
ALLOWED_CHARACTER_PATTERN: "^[a-zA-Z0-9-]{1,127}$"
} as const;
/**
* Azure Key Vault PKI Sync list option configuration
*/
export const AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION = {
name: "Azure Key Vault" as const,
connection: AppConnection.AzureKeyVault,
destination: PkiSync.AzureKeyVault,
canImportCertificates: false,
canRemoveCertificates: true,
defaultCertificateNameSchema: "Infisical-PKI-Sync-{{certificateId}}",
forbiddenCharacters: AZURE_KEY_VAULT_CERTIFICATE_NAMING.FORBIDDEN_CHARACTERS,
allowedCharacterPattern: AZURE_KEY_VAULT_CERTIFICATE_NAMING.ALLOWED_CHARACTER_PATTERN,
maxCertificateNameLength: AZURE_KEY_VAULT_CERTIFICATE_NAMING.MAX_NAME_LENGTH,
minCertificateNameLength: AZURE_KEY_VAULT_CERTIFICATE_NAMING.MIN_NAME_LENGTH
} as const;

View File

@@ -5,14 +5,12 @@ import * as crypto from "crypto";
import { request } from "@app/lib/config/request";
import { logger } from "@app/lib/logger";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
import { createConnectionQueue, RateLimitConfig } from "@app/services/connection-queue";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { matchesCertificateNameSchema } from "@app/services/pki-sync/pki-sync-fns";
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
import { PkiSync } from "../pki-sync-enums";
import { PkiSyncError } from "../pki-sync-errors";
import { TPkiSyncWithCredentials } from "../pki-sync-types";
import { GetAzureKeyVaultCertificate, TAzureKeyVaultPkiSyncConfig } from "./azure-key-vault-pki-sync-types";
@@ -45,19 +43,6 @@ const isInfisicalManagedCertificate = (certificateName: string, pkiSync: TPkiSyn
return certificateName.startsWith("Infisical-PKI-Sync-");
};
export const AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION = {
name: "Azure Key Vault" as const,
connection: AppConnection.AzureKeyVault,
destination: PkiSync.AzureKeyVault,
canImportCertificates: false,
canRemoveCertificates: true,
defaultCertificateNameSchema: "Infisical-PKI-Sync-{{certificateId}}",
forbiddenCharacters: "!@#$%^&*()+=[]{}|\\:;\"'<>,.?/~` _",
allowedCharacterPattern: "^[a-zA-Z0-9-]{1,127}$",
maxCertificateNameLength: 127,
minCertificateNameLength: 1
};
type TAzureKeyVaultPkiSyncFactoryDeps = {
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;

View File

@@ -5,6 +5,8 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
import { PkiSyncSchema } from "@app/services/pki-sync/pki-sync-schemas";
import { AZURE_KEY_VAULT_CERTIFICATE_NAMING } from "./azure-key-vault-pki-sync-constants";
export const AzureKeyVaultPkiSyncConfigSchema = z.object({
vaultBaseUrl: z.string().url()
});
@@ -22,12 +24,12 @@ const AzureKeyVaultPkiSyncOptionsSchema = z.object({
const testName = schema
.replace(new RE2("\\{\\{certificateId\\}\\}", "g"), "")
.replace(new RE2("\\{\\{environment\\}\\}", "g"), "");
const azureNamePattern = new RE2("^[a-zA-Z0-9-]{1,127}$");
const forbiddenChars = "!@#$%^&*()+=[]{}|\\:;\"'<>,.?/~` _";
const hasForbiddenChars = forbiddenChars.split("").some((char) => testName.includes(char));
const hasForbiddenChars = AZURE_KEY_VAULT_CERTIFICATE_NAMING.FORBIDDEN_CHARACTERS.split("").some((char) =>
testName.includes(char)
);
return azureNamePattern.test(testName) && !hasForbiddenChars;
return AZURE_KEY_VAULT_CERTIFICATE_NAMING.NAME_PATTERN.test(testName) && !hasForbiddenChars;
},
{
message:

View File

@@ -1,3 +1,4 @@
export * from "./azure-key-vault-pki-sync-constants";
export * from "./azure-key-vault-pki-sync-fns";
export * from "./azure-key-vault-pki-sync-schemas";
export * from "./azure-key-vault-pki-sync-types";

View File

@@ -3,14 +3,11 @@ import { z, ZodSchema } from "zod";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import {
AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION,
azureKeyVaultPkiSyncFactory
} from "./azure-key-vault/azure-key-vault-pki-sync-fns";
import { AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION } from "./azure-key-vault/azure-key-vault-pki-sync-constants";
import { azureKeyVaultPkiSyncFactory } from "./azure-key-vault/azure-key-vault-pki-sync-fns";
import { PkiSync } from "./pki-sync-enums";
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
@@ -151,36 +148,6 @@ const isAzureKeyVaultPkiSync = (pkiSync: TPkiSyncWithCredentials): boolean => {
return pkiSync.destination === PkiSync.AzureKeyVault;
};
/**
* Trigger auto sync for PKI syncs connected to a PKI subscriber when certificates are issued/revoked/deleted
*/
export const triggerAutoSyncForSubscriber = async (
subscriberId: string,
dependencies: {
pkiSyncDAL: {
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
};
pkiSyncQueue: {
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
};
}
) => {
try {
const pkiSyncs = await dependencies.pkiSyncDAL.find({
subscriberId,
isAutoSyncEnabled: true
});
// Queue sync jobs for each auto sync enabled PKI sync
const syncPromises = pkiSyncs.map((pkiSync) =>
dependencies.pkiSyncQueue.queuePkiSyncSyncCertificatesById({ syncId: pkiSync.id })
);
await Promise.all(syncPromises);
} catch (error) {
logger.error(error, `Failed to trigger auto sync for subscriber ${subscriberId}:`);
}
};
export const PkiSyncFns = {
getCertificates: async (
pkiSync: TPkiSyncWithCredentials,

View File

@@ -14,10 +14,8 @@ export const PkiSyncOptionsSchema = z.object({
(val) => {
if (!val) return true;
const allowedOptionalPlaceholders = ["{{environment}}"];
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) // Escape regex special characters
const allowedPlaceholdersRegexPart = ["{{certificateId}}"]
.map((p) => p.replace(new RE2(/[-/\\^$*+?.()|[\]{}]/g), "\\$&")) // Escape regex special characters
.join("|");
const allowedContentRegex = new RE2(`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`);
@@ -33,7 +31,7 @@ export const PkiSyncOptionsSchema = z.object({
},
{
message:
"Certificate name schema must include exactly one {{certificateId}} placeholder. It can also include {{environment}} placeholders. Only alphanumeric characters (a-z, A-Z, 0-9), dashes (-), underscores (_), and slashes (/) are allowed besides the placeholders."
"Certificate name schema must include exactly one {{certificateId}} placeholder. Only alphanumeric characters (a-z, A-Z, 0-9), dashes (-), underscores (_), and slashes (/) are allowed besides the placeholders."
}
)
});

View File

@@ -153,8 +153,8 @@ export const pkiSyncServiceFactory = ({
}: Omit<TUpdatePkiSyncDTO, "auditLogInfo" | "projectId">,
actor: OrgServiceActor
): Promise<TPkiSync> => {
const existingSync = await pkiSyncDAL.findById(id);
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
@@ -162,12 +162,9 @@ export const pkiSyncServiceFactory = ({
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager,
projectId: existingSync.projectId
projectId: pkiSync.projectId
});
const pkiSync = await pkiSyncDAL.findByIdAndProjectId(id, existingSync.projectId);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
let currentSubscriber;
if (pkiSync.subscriberId) {
currentSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
@@ -181,7 +178,7 @@ export const pkiSyncServiceFactory = ({
);
if (name && name !== pkiSync.name) {
const existingPkiSync = await pkiSyncDAL.findByNameAndProjectId(name, existingSync.projectId);
const existingPkiSync = await pkiSyncDAL.findByNameAndProjectId(name, pkiSync.projectId);
if (existingPkiSync) {
throw new BadRequestError({ message: "PKI sync with this name already exists" });
}
@@ -189,7 +186,7 @@ export const pkiSyncServiceFactory = ({
if (subscriberId) {
const subscriber = await pkiSubscriberDAL.findById(subscriberId);
if (!subscriber || subscriber.projectId !== existingSync.projectId) {
if (!subscriber || subscriber.projectId !== pkiSync.projectId) {
throw new NotFoundError({ message: "PKI subscriber not found" });
}
}
@@ -209,9 +206,9 @@ export const pkiSyncServiceFactory = ({
});
}
if (syncOptions.canRemoveCertificates === false && providerCapabilities.canRemoveCertificates) {
if (syncOptions.canRemoveCertificates && !providerCapabilities.canRemoveCertificates) {
throw new BadRequestError({
message: `Certificate removal cannot be disabled for ${PKI_SYNC_NAME_MAP[pkiSync.destination]} PKI sync destination`
message: `Certificate removal cannot be enabled for ${PKI_SYNC_NAME_MAP[pkiSync.destination]} PKI sync destination`
});
}
@@ -237,9 +234,9 @@ export const pkiSyncServiceFactory = ({
const deletePkiSync = async (
{ id }: Omit<TDeletePkiSyncDTO, "auditLogInfo" | "projectId">,
actor: OrgServiceActor
): Promise<TPkiSync> => {
const existingSync = await pkiSyncDAL.findById(id);
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
) => {
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
@@ -247,12 +244,9 @@ export const pkiSyncServiceFactory = ({
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager,
projectId: existingSync.projectId
projectId: pkiSync.projectId
});
const pkiSync = await pkiSyncDAL.findByIdAndProjectId(id, existingSync.projectId);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
let pkiSyncSubscriber;
if (pkiSync.subscriberId) {
pkiSyncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
@@ -265,31 +259,7 @@ export const pkiSyncServiceFactory = ({
: ProjectPermissionSub.PkiSyncs
);
await pkiSyncDAL.deleteById(id);
return {
...pkiSync,
description: pkiSync.description || undefined,
subscriberId: pkiSync.subscriberId || undefined,
syncStatus: pkiSync.syncStatus || undefined,
lastSyncedAt: pkiSync.lastSyncedAt || undefined,
lastSyncJobId: pkiSync.lastSyncJobId || undefined,
lastSyncMessage: pkiSync.lastSyncMessage || undefined,
importStatus: pkiSync.importStatus || undefined,
lastImportJobId: pkiSync.lastImportJobId || undefined,
lastImportMessage: pkiSync.lastImportMessage || undefined,
lastImportedAt: pkiSync.lastImportedAt || undefined,
removeStatus: pkiSync.removeStatus || undefined,
lastRemoveJobId: pkiSync.lastRemoveJobId || undefined,
lastRemoveMessage: pkiSync.lastRemoveMessage || undefined,
lastRemovedAt: pkiSync.lastRemovedAt || undefined,
connection: {
...pkiSync.connection,
description: pkiSync.connection.description || undefined,
gatewayId: pkiSync.connection.gatewayId || undefined,
projectId: pkiSync.connection.projectId || undefined,
isPlatformManagedCredentials: pkiSync.connection.isPlatformManagedCredentials || undefined
}
};
return pkiSyncDAL.deleteById(id);
};
const listPkiSyncsByProjectId = async ({ projectId }: TListPkiSyncsByProjectId, actor: OrgServiceActor) => {
@@ -310,15 +280,10 @@ export const pkiSyncServiceFactory = ({
const findPkiSyncById = async ({ id, projectId }: TFindPkiSyncByIdDTO, actor: OrgServiceActor) => {
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync)
if (!pkiSync || (projectId && pkiSync.projectId !== projectId)) {
throw new NotFoundError({
message: `Could not find PKI Sync with ID "${id}"`
});
if (projectId && pkiSync.projectId !== projectId) {
throw new NotFoundError({
message: `Could not find PKI Sync with ID "${id}" in project "${projectId}"`
});
}
const { permission } = await permissionService.getProjectPermission({
@@ -349,8 +314,8 @@ export const pkiSyncServiceFactory = ({
{ id }: Omit<TTriggerPkiSyncSyncCertificatesByIdDTO, "auditLogInfo" | "projectId">,
actor: OrgServiceActor
) => {
const existingSync = await pkiSyncDAL.findById(id);
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
@@ -358,12 +323,9 @@ export const pkiSyncServiceFactory = ({
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager,
projectId: existingSync.projectId
projectId: pkiSync.projectId
});
const pkiSync = await pkiSyncDAL.findByIdAndProjectId(id, existingSync.projectId);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
let syncSubscriber;
if (pkiSync.subscriberId) {
syncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
@@ -385,8 +347,8 @@ export const pkiSyncServiceFactory = ({
{ id }: Omit<TTriggerPkiSyncImportCertificatesByIdDTO, "auditLogInfo" | "projectId">,
actor: OrgServiceActor
) => {
const existingSync = await pkiSyncDAL.findById(id);
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
@@ -394,12 +356,9 @@ export const pkiSyncServiceFactory = ({
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager,
projectId: existingSync.projectId
projectId: pkiSync.projectId
});
const pkiSync = await pkiSyncDAL.findByIdAndProjectId(id, existingSync.projectId);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
// Check if the PKI sync destination supports importing certificates
const syncOptions = listPkiSyncOptions().find((option) => option.destination === pkiSync.destination);
if (!syncOptions?.canImportCertificates) {
@@ -429,8 +388,8 @@ export const pkiSyncServiceFactory = ({
{ id }: Omit<TTriggerPkiSyncRemoveCertificatesByIdDTO, "auditLogInfo" | "projectId">,
actor: OrgServiceActor
) => {
const existingSync = await pkiSyncDAL.findById(id);
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
const pkiSync = await pkiSyncDAL.findById(id);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
@@ -438,12 +397,9 @@ export const pkiSyncServiceFactory = ({
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager,
projectId: existingSync.projectId
projectId: pkiSync.projectId
});
const pkiSync = await pkiSyncDAL.findByIdAndProjectId(id, existingSync.projectId);
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
let removeSubscriber;
if (pkiSync.subscriberId) {
removeSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);

View File

@@ -0,0 +1,27 @@
import { logger } from "@app/lib/logger";
import { TPkiSyncDALFactory } from "./pki-sync-dal";
import { TPkiSyncQueueFactory } from "./pki-sync-queue";
export const triggerAutoSyncForSubscriber = async (
subscriberId: string,
dependencies: {
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
}
) => {
try {
const pkiSyncs = await dependencies.pkiSyncDAL.find({
subscriberId,
isAutoSyncEnabled: true
});
// Queue sync jobs for each auto sync enabled PKI sync
const syncPromises = pkiSyncs.map((pkiSync) =>
dependencies.pkiSyncQueue.queuePkiSyncSyncCertificatesById({ syncId: pkiSync.id })
);
await Promise.all(syncPromises);
} catch (error) {
logger.error(error, `Failed to trigger auto sync for subscriber ${subscriberId}:`);
}
};