mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 17:27:40 +00:00
Improvements to cert-syncs
This commit is contained in:
@@ -309,7 +309,7 @@ export const registerSyncPkiEndpoints = ({
|
|||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:pkiSyncId/remove",
|
url: "/:pkiSyncId/remove-certificates",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ import {
|
|||||||
} from "@app/services/certificate/certificate-types";
|
} from "@app/services/certificate/certificate-types";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
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 { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
@@ -57,12 +59,8 @@ type TAcmeCertificateAuthorityFnsDeps = {
|
|||||||
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
||||||
>;
|
>;
|
||||||
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
||||||
pkiSyncDAL: {
|
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
|
||||||
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
|
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||||
};
|
|
||||||
pkiSyncQueue: {
|
|
||||||
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
|
|
||||||
};
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+5
-7
@@ -26,7 +26,9 @@ import {
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
||||||
import { TPkiSubscriberProperties } from "@app/services/pki-subscriber/pki-subscriber-types";
|
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 { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
@@ -56,12 +58,8 @@ type TAzureAdCsCertificateAuthorityFnsDeps = {
|
|||||||
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
||||||
>;
|
>;
|
||||||
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
||||||
pkiSyncDAL: {
|
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
|
||||||
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
|
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||||
};
|
|
||||||
pkiSyncQueue: {
|
|
||||||
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
|
|
||||||
};
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal"
|
|||||||
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
||||||
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
|
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
|
||||||
import { SubscriberOperationStatus } from "../pki-subscriber/pki-subscriber-types";
|
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 { AcmeCertificateAuthorityFns } from "./acme/acme-certificate-authority-fns";
|
||||||
import { AzureAdCsCertificateAuthorityFns } from "./azure-ad-cs/azure-ad-cs-certificate-authority-fns";
|
import { AzureAdCsCertificateAuthorityFns } from "./azure-ad-cs/azure-ad-cs-certificate-authority-fns";
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
@@ -50,12 +52,8 @@ type TCertificateAuthorityQueueFactoryDep = {
|
|||||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
|
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
|
||||||
queueService: TQueueServiceFactory;
|
queueService: TQueueServiceFactory;
|
||||||
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById" | "updateById">;
|
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById" | "updateById">;
|
||||||
pkiSyncDAL: {
|
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
|
||||||
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
|
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||||
};
|
|
||||||
pkiSyncQueue: {
|
|
||||||
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;
|
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { TCertificateDALFactory } from "../certificate/certificate-dal";
|
|||||||
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
|
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 { TProjectDALFactory } from "../project/project-dal";
|
||||||
import {
|
import {
|
||||||
AcmeCertificateAuthorityFns,
|
AcmeCertificateAuthorityFns,
|
||||||
@@ -68,12 +70,8 @@ type TCertificateAuthorityServiceFactoryDep = {
|
|||||||
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
|
||||||
>;
|
>;
|
||||||
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
||||||
pkiSyncDAL: {
|
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
|
||||||
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
|
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||||
};
|
|
||||||
pkiSyncQueue: {
|
|
||||||
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
|
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
|
||||||
|
|||||||
+5
-7
@@ -19,7 +19,9 @@ import {
|
|||||||
TAltNameMapping
|
TAltNameMapping
|
||||||
} from "@app/services/certificate/certificate-types";
|
} from "@app/services/certificate/certificate-types";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
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 { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
@@ -52,12 +54,8 @@ type TInternalCertificateAuthorityFnsDeps = {
|
|||||||
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
|
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
|
||||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
|
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
|
||||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
|
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
|
||||||
pkiSyncDAL: {
|
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
|
||||||
find: (filter: { subscriberId: string; isAutoSyncEnabled: boolean }) => Promise<Array<{ id: string }>>;
|
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||||
};
|
|
||||||
pkiSyncQueue: {
|
|
||||||
queuePkiSyncSyncCertificatesById: (params: { syncId: string }) => Promise<void>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InternalCertificateAuthorityFns = ({
|
export const InternalCertificateAuthorityFns = ({
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
||||||
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
||||||
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-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 { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
||||||
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-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 { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -5,14 +5,12 @@ import * as crypto from "crypto";
|
|||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
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 { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||||
import { createConnectionQueue, RateLimitConfig } from "@app/services/connection-queue";
|
import { createConnectionQueue, RateLimitConfig } from "@app/services/connection-queue";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { matchesCertificateNameSchema } from "@app/services/pki-sync/pki-sync-fns";
|
import { matchesCertificateNameSchema } from "@app/services/pki-sync/pki-sync-fns";
|
||||||
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
|
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
|
||||||
|
|
||||||
import { PkiSync } from "../pki-sync-enums";
|
|
||||||
import { PkiSyncError } from "../pki-sync-errors";
|
import { PkiSyncError } from "../pki-sync-errors";
|
||||||
import { TPkiSyncWithCredentials } from "../pki-sync-types";
|
import { TPkiSyncWithCredentials } from "../pki-sync-types";
|
||||||
import { GetAzureKeyVaultCertificate, TAzureKeyVaultPkiSyncConfig } from "./azure-key-vault-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-");
|
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 = {
|
type TAzureKeyVaultPkiSyncFactoryDeps = {
|
||||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
|
|||||||
@@ -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 { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||||
import { PkiSyncSchema } from "@app/services/pki-sync/pki-sync-schemas";
|
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({
|
export const AzureKeyVaultPkiSyncConfigSchema = z.object({
|
||||||
vaultBaseUrl: z.string().url()
|
vaultBaseUrl: z.string().url()
|
||||||
});
|
});
|
||||||
@@ -22,12 +24,12 @@ const AzureKeyVaultPkiSyncOptionsSchema = z.object({
|
|||||||
const testName = schema
|
const testName = schema
|
||||||
.replace(new RE2("\\{\\{certificateId\\}\\}", "g"), "")
|
.replace(new RE2("\\{\\{certificateId\\}\\}", "g"), "")
|
||||||
.replace(new RE2("\\{\\{environment\\}\\}", "g"), "");
|
.replace(new RE2("\\{\\{environment\\}\\}", "g"), "");
|
||||||
const azureNamePattern = new RE2("^[a-zA-Z0-9-]{1,127}$");
|
|
||||||
|
|
||||||
const forbiddenChars = "!@#$%^&*()+=[]{}|\\:;\"'<>,.?/~` _";
|
const hasForbiddenChars = AZURE_KEY_VAULT_CERTIFICATE_NAMING.FORBIDDEN_CHARACTERS.split("").some((char) =>
|
||||||
const hasForbiddenChars = forbiddenChars.split("").some((char) => testName.includes(char));
|
testName.includes(char)
|
||||||
|
);
|
||||||
|
|
||||||
return azureNamePattern.test(testName) && !hasForbiddenChars;
|
return AZURE_KEY_VAULT_CERTIFICATE_NAMING.NAME_PATTERN.test(testName) && !hasForbiddenChars;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
message:
|
||||||
|
|||||||
@@ -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-fns";
|
||||||
export * from "./azure-key-vault-pki-sync-schemas";
|
export * from "./azure-key-vault-pki-sync-schemas";
|
||||||
export * from "./azure-key-vault-pki-sync-types";
|
export * from "./azure-key-vault-pki-sync-types";
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ import { z, ZodSchema } from "zod";
|
|||||||
|
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
|
||||||
import {
|
import { AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION } from "./azure-key-vault/azure-key-vault-pki-sync-constants";
|
||||||
AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION,
|
import { azureKeyVaultPkiSyncFactory } from "./azure-key-vault/azure-key-vault-pki-sync-fns";
|
||||||
azureKeyVaultPkiSyncFactory
|
|
||||||
} from "./azure-key-vault/azure-key-vault-pki-sync-fns";
|
|
||||||
import { PkiSync } from "./pki-sync-enums";
|
import { PkiSync } from "./pki-sync-enums";
|
||||||
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
||||||
|
|
||||||
@@ -151,36 +148,6 @@ const isAzureKeyVaultPkiSync = (pkiSync: TPkiSyncWithCredentials): boolean => {
|
|||||||
return pkiSync.destination === PkiSync.AzureKeyVault;
|
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 = {
|
export const PkiSyncFns = {
|
||||||
getCertificates: async (
|
getCertificates: async (
|
||||||
pkiSync: TPkiSyncWithCredentials,
|
pkiSync: TPkiSyncWithCredentials,
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ export const PkiSyncOptionsSchema = z.object({
|
|||||||
(val) => {
|
(val) => {
|
||||||
if (!val) return true;
|
if (!val) return true;
|
||||||
|
|
||||||
const allowedOptionalPlaceholders = ["{{environment}}"];
|
const allowedPlaceholdersRegexPart = ["{{certificateId}}"]
|
||||||
|
.map((p) => p.replace(new RE2(/[-/\\^$*+?.()|[\]{}]/g), "\\$&")) // Escape regex special characters
|
||||||
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
|
|
||||||
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) // Escape regex special characters
|
|
||||||
.join("|");
|
.join("|");
|
||||||
|
|
||||||
const allowedContentRegex = new RE2(`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`);
|
const allowedContentRegex = new RE2(`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`);
|
||||||
@@ -33,7 +31,7 @@ export const PkiSyncOptionsSchema = z.object({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
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."
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ export const pkiSyncServiceFactory = ({
|
|||||||
}: Omit<TUpdatePkiSyncDTO, "auditLogInfo" | "projectId">,
|
}: Omit<TUpdatePkiSyncDTO, "auditLogInfo" | "projectId">,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
): Promise<TPkiSync> => {
|
): Promise<TPkiSync> => {
|
||||||
const existingSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
@@ -162,12 +162,9 @@ export const pkiSyncServiceFactory = ({
|
|||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
actionProjectType: ActionProjectType.CertificateManager,
|
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;
|
let currentSubscriber;
|
||||||
if (pkiSync.subscriberId) {
|
if (pkiSync.subscriberId) {
|
||||||
currentSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
currentSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
||||||
@@ -181,7 +178,7 @@ export const pkiSyncServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (name && name !== pkiSync.name) {
|
if (name && name !== pkiSync.name) {
|
||||||
const existingPkiSync = await pkiSyncDAL.findByNameAndProjectId(name, existingSync.projectId);
|
const existingPkiSync = await pkiSyncDAL.findByNameAndProjectId(name, pkiSync.projectId);
|
||||||
if (existingPkiSync) {
|
if (existingPkiSync) {
|
||||||
throw new BadRequestError({ message: "PKI sync with this name already exists" });
|
throw new BadRequestError({ message: "PKI sync with this name already exists" });
|
||||||
}
|
}
|
||||||
@@ -189,7 +186,7 @@ export const pkiSyncServiceFactory = ({
|
|||||||
|
|
||||||
if (subscriberId) {
|
if (subscriberId) {
|
||||||
const subscriber = await pkiSubscriberDAL.findById(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" });
|
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({
|
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 (
|
const deletePkiSync = async (
|
||||||
{ id }: Omit<TDeletePkiSyncDTO, "auditLogInfo" | "projectId">,
|
{ id }: Omit<TDeletePkiSyncDTO, "auditLogInfo" | "projectId">,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
): Promise<TPkiSync> => {
|
) => {
|
||||||
const existingSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
@@ -247,12 +244,9 @@ export const pkiSyncServiceFactory = ({
|
|||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
actionProjectType: ActionProjectType.CertificateManager,
|
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;
|
let pkiSyncSubscriber;
|
||||||
if (pkiSync.subscriberId) {
|
if (pkiSync.subscriberId) {
|
||||||
pkiSyncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
pkiSyncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
||||||
@@ -265,31 +259,7 @@ export const pkiSyncServiceFactory = ({
|
|||||||
: ProjectPermissionSub.PkiSyncs
|
: ProjectPermissionSub.PkiSyncs
|
||||||
);
|
);
|
||||||
|
|
||||||
await pkiSyncDAL.deleteById(id);
|
return 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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const listPkiSyncsByProjectId = async ({ projectId }: TListPkiSyncsByProjectId, actor: OrgServiceActor) => {
|
const listPkiSyncsByProjectId = async ({ projectId }: TListPkiSyncsByProjectId, actor: OrgServiceActor) => {
|
||||||
@@ -310,15 +280,10 @@ export const pkiSyncServiceFactory = ({
|
|||||||
|
|
||||||
const findPkiSyncById = async ({ id, projectId }: TFindPkiSyncByIdDTO, actor: OrgServiceActor) => {
|
const findPkiSyncById = async ({ id, projectId }: TFindPkiSyncByIdDTO, actor: OrgServiceActor) => {
|
||||||
const pkiSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!pkiSync)
|
if (!pkiSync || (projectId && pkiSync.projectId !== projectId)) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Could not find PKI Sync with ID "${id}"`
|
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({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
@@ -349,8 +314,8 @@ export const pkiSyncServiceFactory = ({
|
|||||||
{ id }: Omit<TTriggerPkiSyncSyncCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
{ id }: Omit<TTriggerPkiSyncSyncCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const existingSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
@@ -358,12 +323,9 @@ export const pkiSyncServiceFactory = ({
|
|||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
actionProjectType: ActionProjectType.CertificateManager,
|
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;
|
let syncSubscriber;
|
||||||
if (pkiSync.subscriberId) {
|
if (pkiSync.subscriberId) {
|
||||||
syncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
syncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
||||||
@@ -385,8 +347,8 @@ export const pkiSyncServiceFactory = ({
|
|||||||
{ id }: Omit<TTriggerPkiSyncImportCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
{ id }: Omit<TTriggerPkiSyncImportCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const existingSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
@@ -394,12 +356,9 @@ export const pkiSyncServiceFactory = ({
|
|||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
actionProjectType: ActionProjectType.CertificateManager,
|
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
|
// Check if the PKI sync destination supports importing certificates
|
||||||
const syncOptions = listPkiSyncOptions().find((option) => option.destination === pkiSync.destination);
|
const syncOptions = listPkiSyncOptions().find((option) => option.destination === pkiSync.destination);
|
||||||
if (!syncOptions?.canImportCertificates) {
|
if (!syncOptions?.canImportCertificates) {
|
||||||
@@ -429,8 +388,8 @@ export const pkiSyncServiceFactory = ({
|
|||||||
{ id }: Omit<TTriggerPkiSyncRemoveCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
{ id }: Omit<TTriggerPkiSyncRemoveCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const existingSync = await pkiSyncDAL.findById(id);
|
const pkiSync = await pkiSyncDAL.findById(id);
|
||||||
if (!existingSync) throw new NotFoundError({ message: "PKI sync not found" });
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
@@ -438,12 +397,9 @@ export const pkiSyncServiceFactory = ({
|
|||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
actionProjectType: ActionProjectType.CertificateManager,
|
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;
|
let removeSubscriber;
|
||||||
if (pkiSync.subscriberId) {
|
if (pkiSync.subscriberId) {
|
||||||
removeSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
removeSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
||||||
|
|||||||
@@ -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}:`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Remove Certificates from Azure Key Vault"
|
title: "Remove Certificates from Azure Key Vault"
|
||||||
openapi: "POST /api/v1/pki/syncs/azure-key-vault/{pkiSyncId}/remove"
|
openapi: "POST /api/v1/pki/syncs/azure-key-vault/{pkiSyncId}/remove-certificates"
|
||||||
---
|
---
|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
@@ -29,7 +29,7 @@ This endpoint removes certificates from the specified Azure Key Vault that are n
|
|||||||
|
|
||||||
<RequestExample>
|
<RequestExample>
|
||||||
```bash cURL
|
```bash cURL
|
||||||
curl -X POST "https://app.infisical.com/api/v1/pki/syncs/azure-key-vault/ps_12345/remove" \
|
curl -X POST "https://app.infisical.com/api/v1/pki/syncs/azure-key-vault/ps_12345/remove-certificates" \
|
||||||
-H "Authorization: Bearer <your-api-key>"
|
-H "Authorization: Bearer <your-api-key>"
|
||||||
```
|
```
|
||||||
</RequestExample>
|
</RequestExample>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This endpoint lists all available PKI sync destination options and their capabil
|
|||||||
## Request
|
## Request
|
||||||
|
|
||||||
<ParamField query="projectId" type="string" optional>
|
<ParamField query="projectId" type="string" optional>
|
||||||
Project ID (for authorization purposes, but the options are global)
|
Project ID
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|||||||
+2
-2
@@ -2646,9 +2646,9 @@
|
|||||||
"href": "https://infisical.com"
|
"href": "https://infisical.com"
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"openapi": "https://api.infisical.com/api/docs/json",
|
"openapi": "https://app.infisical.com/api/docs/json",
|
||||||
"mdx": {
|
"mdx": {
|
||||||
"server": ["https://api.infisical.com"]
|
"server": ["https://app.infisical.com"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ via the UI or API for the third-party service you intend to sync certificates to
|
|||||||
- Certificate naming schema to control how certificate names are generated in the destination
|
- Certificate naming schema to control how certificate names are generated in the destination
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
Certificate Syncs manage certificates that are prefixed with "Infisical-" in the destination. Only
|
Only certificates managed by Infisical will be affected during sync operations. Certificates not created or
|
||||||
certificates managed by Infisical will be affected during sync operations. Certificates not created or
|
|
||||||
managed by Infisical will remain untouched, and changes made to Infisical-managed certificates directly
|
managed by Infisical will remain untouched, and changes made to Infisical-managed certificates directly
|
||||||
in the destination service may be overwritten by future syncs.
|
in the destination service may be overwritten by future syncs.
|
||||||
</Note>
|
</Note>
|
||||||
@@ -110,11 +109,9 @@ By default, certificates are named using the pattern `Infisical-{certificateId}`
|
|||||||
You can customize certificate naming by providing a **Certificate Name Schema** when creating or updating a Certificate Sync. The schema supports the following placeholders:
|
You can customize certificate naming by providing a **Certificate Name Schema** when creating or updating a Certificate Sync. The schema supports the following placeholders:
|
||||||
|
|
||||||
- `{{certificateId}}` - The unique certificate identifier (required)
|
- `{{certificateId}}` - The unique certificate identifier (required)
|
||||||
- `{{environment}}` - The environment context (always "global" for PKI syncs)
|
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
- `myapp-{{certificateId}}` → `myapp-abc123def456`
|
- `myapp-{{certificateId}}` → `myapp-abc123def456`
|
||||||
- `{{environment}}-cert-{{certificateId}}` → `global-cert-abc123def456`
|
|
||||||
- `ssl/{{certificateId}}` → `ssl/abc123def456`
|
- `ssl/{{certificateId}}` → `ssl/abc123def456`
|
||||||
|
|
||||||
**Rules:**
|
**Rules:**
|
||||||
|
|||||||
@@ -130,11 +130,10 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel }: Props)
|
|||||||
Certificate Sync Behavior
|
Certificate Sync Behavior
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-sm text-bunker-200">
|
<p className="mt-1 text-sm text-bunker-200">
|
||||||
Certificate Syncs manage certificates that are prefixed with "Infisical-" in
|
Only certificates managed by Infisical will be affected during sync operations.
|
||||||
the destination. Only certificates managed by Infisical will be affected during sync
|
Certificates not created or managed by Infisical will remain untouched, and changes made
|
||||||
operations. Certificates not created or managed by Infisical will remain untouched, and
|
to Infisical-managed certificates directly in the destination service may be overwritten
|
||||||
changes made to Infisical-managed certificates directly in the destination service may
|
by future syncs.
|
||||||
be overwritten by future syncs.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex gap-4">
|
<div className="mt-4 flex gap-4">
|
||||||
|
|||||||
+3
-12
@@ -104,8 +104,8 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
|||||||
tooltipText={
|
tooltipText={
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<span>
|
<span>
|
||||||
When a certificate is synced, values will be injected into the certificate name
|
When a certificate is synced, the certificate name schema will be applied before
|
||||||
schema before it reaches the destination. This is useful for organization.
|
it reaches the destination.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
@@ -114,10 +114,6 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
|||||||
<li>
|
<li>
|
||||||
<code>{"{{certificateId}}"}</code> - The unique ID of the certificate
|
<code>{"{{certificateId}}"}</code> - The unique ID of the certificate
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<code>{"{{environment}}"}</code> - The environment which the certificate is in
|
|
||||||
(e.g. dev, staging, prod)
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{syncOption?.forbiddenCharacters && syncOption.forbiddenCharacters.length > 0 && (
|
{syncOption?.forbiddenCharacters && syncOption.forbiddenCharacters.length > 0 && (
|
||||||
@@ -129,11 +125,6 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
|||||||
The following characters are not allowed:{" "}
|
The following characters are not allowed:{" "}
|
||||||
{syncOption.forbiddenCharacters.split("").join(" ")}
|
{syncOption.forbiddenCharacters.split("").join(" ")}
|
||||||
</div>
|
</div>
|
||||||
{syncOption.allowedCharacterPattern && (
|
|
||||||
<div className="mt-1 text-xs text-bunker-300">
|
|
||||||
Only alphanumeric characters and hyphens are allowed (a-z, A-Z, 0-9, -)
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -142,7 +133,7 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
|||||||
isOptional
|
isOptional
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
label="Certificate Name Schema"
|
label="Certificate Name Schema"
|
||||||
helperText="Infisical strongly advises setting a Certificate Name Schema to ensure that Infisical only manages the specific certificates you intend, keeping everything else untouched."
|
helperText="Infisical strongly advises setting a Certificate Name Schema to ensure that Infisical only manages the specific certificates you intend to manage, keeping everything else untouched."
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
value={value || ""}
|
value={value || ""}
|
||||||
|
|||||||
@@ -97,7 +97,9 @@ export const useTriggerPkiSyncRemoveCertificates = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
||||||
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/remove`);
|
const { data } = await apiRequest.post(
|
||||||
|
`/api/v1/pki/syncs/${destination}/${syncId}/remove-certificates`
|
||||||
|
);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-2
@@ -8,8 +8,7 @@ type Props = {
|
|||||||
type:
|
type:
|
||||||
| ProjectPermissionSub.SecretFolders
|
| ProjectPermissionSub.SecretFolders
|
||||||
| ProjectPermissionSub.SecretImports
|
| ProjectPermissionSub.SecretImports
|
||||||
| ProjectPermissionSub.SecretRotation
|
| ProjectPermissionSub.SecretRotation;
|
||||||
| ProjectPermissionSub.PkiSyncs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }: Props) => {
|
export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }: Props) => {
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import { ProjectPermissionSub } from "@app/context/ProjectPermissionContext/types";
|
||||||
|
|
||||||
|
import { ConditionsFields } from "./ConditionsFields";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
position?: number;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PkiSyncPermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
||||||
|
return (
|
||||||
|
<ConditionsFields
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
subject={ProjectPermissionSub.PkiSyncs}
|
||||||
|
position={position}
|
||||||
|
selectOptions={[{ value: "subscriberName", label: "Subscriber Name" }]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -23,6 +23,7 @@ import { GeneralPermissionPolicies } from "./GeneralPermissionPolicies";
|
|||||||
import { IdentityManagementPermissionConditions } from "./IdentityManagementPermissionConditions";
|
import { IdentityManagementPermissionConditions } from "./IdentityManagementPermissionConditions";
|
||||||
import { PermissionEmptyState } from "./PermissionEmptyState";
|
import { PermissionEmptyState } from "./PermissionEmptyState";
|
||||||
import { PkiSubscriberPermissionConditions } from "./PkiSubscriberPermissionConditions";
|
import { PkiSubscriberPermissionConditions } from "./PkiSubscriberPermissionConditions";
|
||||||
|
import { PkiSyncPermissionConditions } from "./PkiSyncPermissionConditions";
|
||||||
import { PkiTemplatePermissionConditions } from "./PkiTemplatePermissionConditions";
|
import { PkiTemplatePermissionConditions } from "./PkiTemplatePermissionConditions";
|
||||||
import {
|
import {
|
||||||
EXCLUDED_PERMISSION_SUBS,
|
EXCLUDED_PERMISSION_SUBS,
|
||||||
@@ -74,6 +75,10 @@ export const renderConditionalComponents = (
|
|||||||
return <SecretSyncPermissionConditions isDisabled={isDisabled} />;
|
return <SecretSyncPermissionConditions isDisabled={isDisabled} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subject === ProjectPermissionSub.PkiSyncs) {
|
||||||
|
return <PkiSyncPermissionConditions isDisabled={isDisabled} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (subject === ProjectPermissionSub.SecretEvents) {
|
if (subject === ProjectPermissionSub.SecretEvents) {
|
||||||
return <SecretEventPermissionConditions isDisabled={isDisabled} />;
|
return <SecretEventPermissionConditions isDisabled={isDisabled} />;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user