mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-18 15:04:59 +00:00
Address PR comments
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
import * as handlebars from "handlebars";
|
||||
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 } from "./azure-key-vault/azure-key-vault-pki-sync-fns";
|
||||
import {
|
||||
AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION,
|
||||
azureKeyVaultPkiSyncFactory
|
||||
} from "./azure-key-vault/azure-key-vault-pki-sync-fns";
|
||||
import { PkiSync } from "./pki-sync-enums";
|
||||
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
||||
|
||||
@@ -34,6 +39,18 @@ export const listPkiSyncOptions = () => {
|
||||
return Object.values(PKI_SYNC_LIST_OPTIONS).sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
export const getPkiSyncProviderCapabilities = (destination: PkiSync) => {
|
||||
const providerOption = PKI_SYNC_LIST_OPTIONS[destination];
|
||||
if (!providerOption) {
|
||||
throw new BadRequestError({ message: `Unsupported PKI sync destination: ${destination}` });
|
||||
}
|
||||
|
||||
return {
|
||||
canImportCertificates: providerOption.canImportCertificates,
|
||||
canRemoveCertificates: providerOption.canRemoveCertificates
|
||||
};
|
||||
};
|
||||
|
||||
export const matchesSchema = <T extends ZodSchema>(schema: T, data: unknown): data is z.infer<T> => {
|
||||
return schema.safeParse(data).success;
|
||||
};
|
||||
@@ -50,9 +67,124 @@ export const parsePkiSyncErrorMessage = (error: unknown): string => {
|
||||
return "An unknown error occurred during PKI sync operation";
|
||||
};
|
||||
|
||||
export const applyCertificateNameSchema = (
|
||||
certificateMap: TCertificateMap,
|
||||
environment: string,
|
||||
schema?: string
|
||||
): TCertificateMap => {
|
||||
if (!schema) return certificateMap;
|
||||
|
||||
const processedCertificateMap: TCertificateMap = {};
|
||||
|
||||
for (const [certificateId, value] of Object.entries(certificateMap)) {
|
||||
const newName = handlebars.compile(schema)({
|
||||
certificateId,
|
||||
environment
|
||||
});
|
||||
|
||||
processedCertificateMap[newName] = value;
|
||||
}
|
||||
|
||||
return processedCertificateMap;
|
||||
};
|
||||
|
||||
export const stripCertificateNameSchema = (
|
||||
certificateMap: TCertificateMap,
|
||||
environment: string,
|
||||
schema?: string
|
||||
): TCertificateMap => {
|
||||
if (!schema) return certificateMap;
|
||||
|
||||
const compiledSchemaPattern = handlebars.compile(schema)({
|
||||
certificateId: "{{certificateId}}",
|
||||
environment
|
||||
});
|
||||
|
||||
const parts = compiledSchemaPattern.split("{{certificateId}}");
|
||||
const prefix = parts[0];
|
||||
const suffix = parts[parts.length - 1];
|
||||
|
||||
const strippedMap: TCertificateMap = {};
|
||||
|
||||
for (const [name, value] of Object.entries(certificateMap)) {
|
||||
if (!name.startsWith(prefix) || !name.endsWith(suffix)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
const strippedName = name.slice(prefix.length, name.length - suffix.length);
|
||||
strippedMap[strippedName] = value;
|
||||
}
|
||||
|
||||
return strippedMap;
|
||||
};
|
||||
|
||||
export const matchesCertificateNameSchema = (name: string, environment: string, schema?: string): boolean => {
|
||||
if (!schema) return true;
|
||||
|
||||
const compiledSchemaPattern = handlebars.compile(schema)({
|
||||
certificateId: "{{certificateId}}",
|
||||
environment
|
||||
});
|
||||
|
||||
if (!compiledSchemaPattern.includes("{{certificateId}}")) {
|
||||
return name === compiledSchemaPattern;
|
||||
}
|
||||
|
||||
const parts = compiledSchemaPattern.split("{{certificateId}}");
|
||||
const prefix = parts[0];
|
||||
const suffix = parts[parts.length - 1];
|
||||
|
||||
if (prefix === "" && suffix === "") return true;
|
||||
|
||||
// If prefix is empty, name must end with suffix
|
||||
if (prefix === "") return name.endsWith(suffix);
|
||||
|
||||
// If suffix is empty, name must start with prefix
|
||||
if (suffix === "") return name.startsWith(prefix);
|
||||
|
||||
// Name must start with prefix and end with suffix
|
||||
return name.startsWith(prefix) && name.endsWith(suffix);
|
||||
};
|
||||
|
||||
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,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
dependencies: {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
@@ -60,11 +192,8 @@ export const PkiSyncFns = {
|
||||
): Promise<TCertificateMap> => {
|
||||
switch (pkiSync.destination) {
|
||||
case PkiSync.AzureKeyVault: {
|
||||
const { azureKeyVaultPkiSyncFactory } = await import("./azure-key-vault/azure-key-vault-pki-sync-fns");
|
||||
const azureKeyVaultPkiSync = azureKeyVaultPkiSyncFactory(dependencies);
|
||||
// Type assertion needed due to destinationConfig type differences
|
||||
return azureKeyVaultPkiSync.importCertificates(
|
||||
pkiSync as unknown as import("./azure-key-vault/azure-key-vault-pki-sync-types").TAzureKeyVaultPkiSyncWithCredentials
|
||||
throw new Error(
|
||||
"Azure Key Vault does not support importing certificates into Infisical (private keys cannot be extracted)"
|
||||
);
|
||||
}
|
||||
default:
|
||||
@@ -84,16 +213,19 @@ export const PkiSyncFns = {
|
||||
removed?: number;
|
||||
failedRemovals?: number;
|
||||
skipped: number;
|
||||
details?: {
|
||||
failedUploads?: Array<{ name: string; error: string }>;
|
||||
failedRemovals?: Array<{ name: string; error: string }>;
|
||||
skippedCertificates?: Array<{ name: string; reason: string }>;
|
||||
};
|
||||
}> => {
|
||||
switch (pkiSync.destination) {
|
||||
case PkiSync.AzureKeyVault: {
|
||||
const { azureKeyVaultPkiSyncFactory } = await import("./azure-key-vault/azure-key-vault-pki-sync-fns");
|
||||
if (!isAzureKeyVaultPkiSync(pkiSync)) {
|
||||
throw new Error("Invalid Azure Key Vault PKI sync configuration");
|
||||
}
|
||||
const azureKeyVaultPkiSync = azureKeyVaultPkiSyncFactory(dependencies);
|
||||
// Type assertion needed due to destinationConfig type differences
|
||||
return azureKeyVaultPkiSync.syncCertificates(
|
||||
pkiSync as unknown as import("./azure-key-vault/azure-key-vault-pki-sync-types").TAzureKeyVaultPkiSyncWithCredentials,
|
||||
certificateMap
|
||||
);
|
||||
return azureKeyVaultPkiSync.syncCertificates(pkiSync, certificateMap);
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported PKI sync destination: ${String(pkiSync.destination)}`);
|
||||
@@ -110,13 +242,11 @@ export const PkiSyncFns = {
|
||||
): Promise<void> => {
|
||||
switch (pkiSync.destination) {
|
||||
case PkiSync.AzureKeyVault: {
|
||||
const { azureKeyVaultPkiSyncFactory } = await import("./azure-key-vault/azure-key-vault-pki-sync-fns");
|
||||
if (!isAzureKeyVaultPkiSync(pkiSync)) {
|
||||
throw new Error("Invalid Azure Key Vault PKI sync configuration");
|
||||
}
|
||||
const azureKeyVaultPkiSync = azureKeyVaultPkiSyncFactory(dependencies);
|
||||
// Type assertion needed due to destinationConfig type differences
|
||||
await azureKeyVaultPkiSync.removeCertificates(
|
||||
pkiSync as unknown as import("./azure-key-vault/azure-key-vault-pki-sync-types").TAzureKeyVaultPkiSyncWithCredentials,
|
||||
certificateNames
|
||||
);
|
||||
await azureKeyVaultPkiSync.removeCertificates(pkiSync, certificateNames);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user