src/crypto/key/kms/kms-provider.registry.ts

Description

Registry that loads kms.json, instantiates one KmsAdapter per configured provider, and resolves them by id.

If kms.json is missing, a single db adapter is registered under the id "db" so existing deployments keep working.

Index

Properties
Methods

Constructor

constructor(kmsConfig: KmsConfigService, httpService: HttpService)
Parameters :
Name Type Optional
kmsConfig KmsConfigService No
httpService HttpService No

Methods

Private buildBundle
buildBundle(tenantId?: string)
Parameters :
Name Type Optional
tenantId string Yes
Private getBundle
getBundle(tenantId?: string)
Parameters :
Name Type Optional
tenantId string Yes
getDefault
getDefault(tenantId?: string)
Parameters :
Name Type Optional
tenantId string Yes
Returns : KmsAdapter
Async health
health(tenantId?: string)

Run the health probe for every registered adapter in parallel.

Parameters :
Name Type Optional
tenantId string Yes
Private instantiate
instantiate(provider: KmsProviderConfigDto)
Parameters :
Name Type Optional
provider KmsProviderConfigDto No
Returns : KmsAdapter
invalidateTenant
invalidateTenant(tenantId: string)
Parameters :
Name Type Optional
tenantId string No
Returns : void
list
list(tenantId?: string)

Return the public view of registered providers (for the API).

Parameters :
Name Type Optional
tenantId string Yes
onModuleInit
onModuleInit()
Returns : void
resolve
resolve(providerId?: string, tenantId?: string)

Resolve an adapter by provider id. Throws if not registered.

Parameters :
Name Type Optional
providerId string Yes
tenantId string Yes
Returns : KmsAdapter

Properties

Private globalBundle
Type : { adapters: Map<string, KmsAdapter>; defaultProviderId: string }
Default value : { adapters: new Map<string, KmsAdapter>(), defaultProviderId: DEFAULT_PROVIDER_ID, }
Private Readonly logger
Type : unknown
Default value : new Logger(KmsProviderRegistry.name)
Private Readonly tenantBundles
Type : unknown
Default value : new Map< string, { adapters: Map<string, KmsAdapter>; defaultProviderId: string; } >()
import { HttpService } from "@nestjs/axios";
import {
    BadRequestException,
    Injectable,
    Logger,
    OnModuleInit,
} from "@nestjs/common";
import * as x509 from "@peculiar/x509";
import type {
    KmsProviderConfigDto,
    KmsProviderType,
} from "../dto/kms-config.dto";
import { KmsConfigService } from "./kms-config.service";
import { KmsCryptoProvider } from "./kms-crypto-provider";
import type { KmsProviderInfoDto } from "../dto/kms-provider-capabilities.dto";
import type { KmsProvidersResponseDto } from "../dto/kms-providers-response.dto";
import { AwsKmsAdapter } from "./adapters/aws-kms.adapter";
import { CscKmsAdapter } from "./adapters/csc-kms.adapter";
import { DbKmsAdapter } from "./adapters/db-kms.adapter";
import { HttpKmsAdapter } from "./adapters/http-kms.adapter";
import { Pkcs11KmsAdapter } from "./adapters/pkcs11-kms.adapter";
import { VaultKmsAdapter } from "./adapters/vault-kms.adapter";
import type { KmsAdapter } from "./kms-adapter";

const DEFAULT_PROVIDER_ID = "db";

/**
 * Registry that loads `kms.json`, instantiates one {@link KmsAdapter}
 * per configured provider, and resolves them by id.
 *
 * If `kms.json` is missing, a single `db` adapter is registered under
 * the id `"db"` so existing deployments keep working.
 */
@Injectable()
export class KmsProviderRegistry implements OnModuleInit {
    private readonly logger = new Logger(KmsProviderRegistry.name);
    private globalBundle: {
        adapters: Map<string, KmsAdapter>;
        defaultProviderId: string;
    } = {
        adapters: new Map<string, KmsAdapter>(),
        defaultProviderId: DEFAULT_PROVIDER_ID,
    };
    private readonly tenantBundles = new Map<
        string,
        {
            adapters: Map<string, KmsAdapter>;
            defaultProviderId: string;
        }
    >();

    constructor(
        private readonly kmsConfig: KmsConfigService,
        private readonly httpService: HttpService,
    ) {}

    onModuleInit(): void {
        this.globalBundle = this.buildBundle();

        this.logger.log(
            `Registered global KMS providers: ${[...this.globalBundle.adapters.keys()].join(", ")} (default: ${this.globalBundle.defaultProviderId})`,
        );

        // Install our KMS-aware crypto provider so @peculiar/x509 routes
        // certificate signature generation back to the configured KMS
        // adapter — private key material never leaves the backend.
        x509.cryptoProvider.set(new KmsCryptoProvider());
    }

    /** Resolve an adapter by provider id. Throws if not registered. */
    resolve(providerId?: string, tenantId?: string): KmsAdapter {
        const bundle = this.getBundle(tenantId);
        const id = providerId || bundle.defaultProviderId;
        const adapter = bundle.adapters.get(id);
        if (!adapter) {
            throw new BadRequestException(
                `Unknown KMS provider '${id}'. Configured providers: ${[...bundle.adapters.keys()].join(", ")}`,
            );
        }
        return adapter;
    }

    getDefault(tenantId?: string): KmsAdapter {
        return this.resolve(undefined, tenantId);
    }

    /** Return the public view of registered providers (for the API). */
    list(tenantId?: string): KmsProvidersResponseDto {
        const bundle = this.getBundle(tenantId);
        const providers: KmsProviderInfoDto[] = [
            ...bundle.adapters.values(),
        ].map((a) => ({
            name: a.providerId,
            type: a.type,
            capabilities: a.capabilities,
        }));
        return { providers, default: bundle.defaultProviderId };
    }

    /**
     * Run the health probe for every registered adapter in parallel.
     */
    async health(tenantId?: string): Promise<
        Array<{
            providerId: string;
            type: string;
            ok: boolean;
            latencyMs?: number;
            error?: string;
        }>
    > {
        const bundle = this.getBundle(tenantId);
        const entries = [...bundle.adapters.values()];
        return Promise.all(
            entries.map(async (a) => {
                const result = await a.health();
                return {
                    providerId: a.providerId,
                    type: a.type,
                    ...result,
                };
            }),
        );
    }

    private getBundle(tenantId?: string): {
        adapters: Map<string, KmsAdapter>;
        defaultProviderId: string;
    } {
        if (!tenantId) {
            return this.globalBundle;
        }

        const cached = this.tenantBundles.get(tenantId);
        if (cached) {
            return cached;
        }

        const bundle = this.buildBundle(tenantId);
        this.tenantBundles.set(tenantId, bundle);
        return bundle;
    }

    private buildBundle(tenantId?: string): {
        adapters: Map<string, KmsAdapter>;
        defaultProviderId: string;
    } {
        const adapters = new Map<string, KmsAdapter>();
        const defaultProviderId =
            this.kmsConfig.getDefaultProviderId(tenantId) ||
            DEFAULT_PROVIDER_ID;

        for (const provider of this.kmsConfig.getProviders(tenantId)) {
            adapters.set(provider.id, this.instantiate(provider));
        }

        // Always ensure a default db adapter exists.
        if (!adapters.has(DEFAULT_PROVIDER_ID)) {
            adapters.set(
                DEFAULT_PROVIDER_ID,
                new DbKmsAdapter(DEFAULT_PROVIDER_ID),
            );
        }

        return {
            adapters,
            defaultProviderId,
        };
    }

    invalidateTenant(tenantId: string): void {
        this.tenantBundles.delete(tenantId);
    }

    private instantiate(provider: KmsProviderConfigDto): KmsAdapter {
        const type: KmsProviderType = provider.type;
        switch (type) {
            case "db":
                return new DbKmsAdapter(provider.id);
            case "vault": {
                const p = provider as Extract<
                    KmsProviderConfigDto,
                    { type: "vault" }
                >;
                return new VaultKmsAdapter(
                    {
                        providerId: provider.id,
                        vaultUrl: p.vaultUrl,
                        vaultToken: p.vaultToken,
                    },
                    this.httpService,
                );
            }
            case "aws-kms": {
                const p = provider as Extract<
                    KmsProviderConfigDto,
                    { type: "aws-kms" }
                >;
                return new AwsKmsAdapter({
                    providerId: provider.id,
                    region: p.region,
                    accessKeyId: p.accessKeyId,
                    secretAccessKey: p.secretAccessKey,
                });
            }
            case "pkcs11": {
                const p = provider as Extract<
                    KmsProviderConfigDto,
                    { type: "pkcs11" }
                >;
                const slot =
                    typeof p.slot === "string" && /^\d+$/.test(p.slot)
                        ? Number(p.slot)
                        : p.slot;
                return new Pkcs11KmsAdapter({
                    providerId: provider.id,
                    library: p.library,
                    slot,
                    pin: p.pin,
                    readOnly: p.readOnly,
                });
            }
            case "http": {
                const p = provider as Extract<
                    KmsProviderConfigDto,
                    { type: "http" }
                >;
                return new HttpKmsAdapter(
                    {
                        providerId: provider.id,
                        baseUrl: p.baseUrl,
                        auth: p.auth,
                        keysPath: p.keysPath,
                        healthPath: p.healthPath,
                        canImport: p.canImport,
                    },
                    this.httpService,
                );
            }
            case "csc": {
                const p = provider as Extract<
                    KmsProviderConfigDto,
                    { type: "csc" }
                >;
                return new CscKmsAdapter(
                    {
                        providerId: provider.id,
                        baseUrl: p.baseUrl,
                        tokenUrl: p.tokenUrl,
                        clientId: p.clientId,
                        clientSecret: p.clientSecret,
                        scope: p.scope,
                        credentialId: p.credentialId,
                        userId: p.userId,
                        apiPath: p.apiPath,
                        hashAlgorithmOid: p.hashAlgorithmOid,
                        signAlgorithmOid: p.signAlgorithmOid,
                        sad: p.sad,
                        useAuthorizeEndpoint: p.useAuthorizeEndpoint,
                        authorizeAuthData: p.authorizeAuthData,
                    },
                    this.httpService,
                );
            }
            default: {
                const _exhaustive: never = type;
                throw new Error(
                    `Unknown KMS provider type: ${String(_exhaustive)}`,
                );
            }
        }
    }
}

results matching ""

    No results matching ""