src/crypto/key/key-chain.controller.ts

Prefix

key-chain

Description

KeyChainController manages unified key chains.

A key chain encapsulates:

  • An optional root CA key (for internal certificate chains)
  • An active signing key with its certificate
  • Rotation policy and previous keys (for grace period)

Index

Methods

Methods

Async create
create(token: TokenPayload, body: KeyChainCreateDto)
Decorators :
@Post()
@ApiOperation({summary: 'Create a new key chain'})
@ApiResponse({status: 201, description: 'Key chain created successfully', type: KeyChainIdResponseDto})

Create a new key chain.

Parameters :
Name Type Optional
token TokenPayload No
body KeyChainCreateDto No
Async delete
delete(token: TokenPayload, id: string)
Decorators :
@Delete(':id')
@ApiOperation({summary: 'Delete a key chain'})
@ApiResponse({status: 204, description: 'Key chain deleted successfully'})
@ApiResponse({status: 404, description: 'Key chain not found'})
@HttpCode(204)

Delete a key chain.

Parameters :
Name Type Optional
token TokenPayload No
id string No
Returns : Promise<void>
deleteTenantKmsConfig
deleteTenantKmsConfig(token: TokenPayload)
Decorators :
@Delete('providers/config')
@ApiOperation({summary: 'Delete tenant KMS provider configuration', description: 'Removes <CONFIG_FOLDER>/<tenantId>/kms.json and falls back to global KMS config.'})
@ApiResponse({status: 204, description: 'Tenant-specific KMS config removed.'})
@HttpCode(204)
Parameters :
Name Type Optional
token TokenPayload No
Returns : void
export
export(token: TokenPayload, id: string)
Decorators :
@Get(':id/export')
@ApiOperation({summary: 'Export a key chain in config-import format', description: 'Returns the key chain including private key material in the same format used by config import JSON files.'})
@ApiResponse({status: 200, description: 'Key chain export data', type: KeyChainExportDto})
@ApiResponse({status: 404, description: 'Key chain not found'})

Export a key chain in config-import-compatible format. The response includes private key material and can be saved as a JSON file for provisioning via the config import mechanism.

Parameters :
Name Type Optional
token TokenPayload No
id string No
getAll
getAll(token: TokenPayload, usageType?: KeyUsageType)
Decorators :
@Get()
@ApiOperation({summary: 'List all key chains for the tenant'})
@ApiResponse({status: 200, description: 'List of key chains', type: undefined})
@ApiQuery({name: 'usageType', required: false, enum: KeyUsageType, description: 'Optional usage type filter'})

List all key chains for the tenant.

Parameters :
Name Type Optional
token TokenPayload No
usageType KeyUsageType Yes
getById
getById(token: TokenPayload, id: string)
Decorators :
@Get(':id')
@ApiOperation({summary: 'Get a key chain by ID'})
@ApiResponse({status: 200, description: 'The key chain', type: KeyChainResponseDto})
@ApiResponse({status: 404, description: 'Key chain not found'})

Get a specific key chain by ID.

Parameters :
Name Type Optional
token TokenPayload No
id string No
getProviders
getProviders(token: TokenPayload)
Decorators :
@Get('providers')
@ApiOperation({summary: 'Get available KMS providers'})
@ApiResponse({status: 200, description: 'List of available KMS providers with capabilities', type: KmsProvidersResponseDto})

Get available KMS providers and their capabilities.

Parameters :
Name Type Optional
token TokenPayload No
getProvidersHealth
getProvidersHealth(token: TokenPayload)
Decorators :
@Get('providers/health')
@ApiOperation({summary: 'Health probe for every KMS provider'})
@ApiResponse({status: 200, description: 'Per-provider health result (ok, latencyMs, optional error).', type: undefined})

Liveness/readiness probe for every registered KMS provider.

Parameters :
Name Type Optional
token TokenPayload No
getTenantKmsConfig
getTenantKmsConfig(token: TokenPayload)
Decorators :
@Get('providers/config')
@ApiOperation({summary: 'Get tenant KMS provider configuration', description: 'Returns tenant-specific KMS config (if present) and the effective merged runtime config.'})
@ApiResponse({status: 200, description: 'Tenant and effective KMS configuration.', type: KmsTenantConfigResponseDto})
Parameters :
Name Type Optional
token TokenPayload No
Async import
import(token: TokenPayload, body: KeyChainImportDto)
Decorators :
@Post('import')
@ApiOperation({summary: 'Import an existing key chain'})
@ApiResponse({status: 201, description: 'Key chain imported successfully', type: KeyChainIdResponseDto})

Import an existing key chain with provided key material and optional certificate.

Parameters :
Name Type Optional
token TokenPayload No
body KeyChainImportDto No
Async rotate
rotate(token: TokenPayload, id: string)
Decorators :
@Post(':id/rotate')
@ApiOperation({summary: 'Rotate the signing key in a key chain'})
@ApiResponse({status: 204, description: 'Key chain rotated successfully'})
@ApiResponse({status: 404, description: 'Key chain not found'})
@HttpCode(204)

Manually trigger key rotation for a key chain.

Parameters :
Name Type Optional
token TokenPayload No
id string No
Returns : Promise<void>
Async update
update(token: TokenPayload, id: string, body: KeyChainUpdateDto)
Decorators :
@Put(':id')
@ApiOperation({summary: 'Update key chain metadata and rotation policy'})
@ApiResponse({status: 204, description: 'Key chain updated successfully'})
@ApiResponse({status: 404, description: 'Key chain not found'})
@HttpCode(204)

Update a key chain.

Parameters :
Name Type Optional
token TokenPayload No
id string No
body KeyChainUpdateDto No
Returns : Promise<void>
updateTenantKmsConfig
updateTenantKmsConfig(token: TokenPayload, body: KmsConfigDto)
Decorators :
@Put('providers/config')
@ApiOperation({summary: 'Create or replace tenant KMS provider configuration'})
@ApiBody({type: KmsConfigDto})
@ApiResponse({status: 200, description: 'Updated tenant KMS config.', type: KmsTenantConfigResponseDto})
Parameters :
Name Type Optional
token TokenPayload No
body KmsConfigDto No
import {
    Body,
    Controller,
    Delete,
    Get,
    HttpCode,
    Param,
    Post,
    Put,
    Query,
} from "@nestjs/common";
import {
    ApiBody,
    ApiOperation,
    ApiQuery,
    ApiResponse,
    ApiTags,
} from "@nestjs/swagger";
import { Role } from "../../auth/roles/role.enum";
import { Secured } from "../../auth/secure.decorator";
import { Token, TokenPayload } from "../../auth/token.decorator";
import { KeyChainCreateDto } from "./dto/key-chain-create.dto";
import { KeyChainExportDto } from "./dto/key-chain-export.dto";
import { KeyChainIdResponseDto } from "./dto/key-chain-id-response.dto";
import { KeyChainImportDto } from "./dto/key-chain-import.dto";
import { KeyChainResponseDto } from "./dto/key-chain-response.dto";
import { KeyChainUpdateDto } from "./dto/key-chain-update.dto";
import { ProviderHealthResponseDto } from "./dto/provider-health-response.dto";
import { KmsProvidersResponseDto } from "./dto/kms-providers-response.dto";
import { KmsConfigDto } from "./dto/kms-config.dto";
import { KmsTenantConfigResponseDto } from "./dto/kms-tenant-config-response.dto";
import { KeyUsageType } from "./types/key-usage-type";
import { KeyChainService } from "./key-chain.service";
import { KmsTenantConfigService } from "./kms/kms-tenant-config.service";

/**
 * KeyChainController manages unified key chains.
 *
 * A key chain encapsulates:
 * - An optional root CA key (for internal certificate chains)
 * - An active signing key with its certificate
 * - Rotation policy and previous keys (for grace period)
 */
@ApiTags("Key Chain")
@Secured([Role.Issuances, Role.Presentations])
@Controller("key-chain")
export class KeyChainController {
    constructor(
        private readonly keyChainService: KeyChainService,
        private readonly kmsTenantConfigService: KmsTenantConfigService,
    ) {}

    /**
     * Get available KMS providers and their capabilities.
     */
    @Get("providers")
    @ApiOperation({ summary: "Get available KMS providers" })
    @ApiResponse({
        status: 200,
        description: "List of available KMS providers with capabilities",
        type: KmsProvidersResponseDto,
    })
    getProviders(@Token() token: TokenPayload): KmsProvidersResponseDto {
        return this.keyChainService.getProviders(token.entity!.id);
    }

    /**
     * Liveness/readiness probe for every registered KMS provider.
     */
    @Get("providers/health")
    @ApiOperation({ summary: "Health probe for every KMS provider" })
    @ApiResponse({
        status: 200,
        description:
            "Per-provider health result (ok, latencyMs, optional error).",
        type: [ProviderHealthResponseDto],
    })
    getProvidersHealth(
        @Token() token: TokenPayload,
    ): Promise<ProviderHealthResponseDto[]> {
        return this.keyChainService.getProviderHealth(token.entity!.id);
    }

    @Get("providers/config")
    @ApiOperation({
        summary: "Get tenant KMS provider configuration",
        description:
            "Returns tenant-specific KMS config (if present) and the effective merged runtime config.",
    })
    @ApiResponse({
        status: 200,
        description: "Tenant and effective KMS configuration.",
        type: KmsTenantConfigResponseDto,
    })
    getTenantKmsConfig(
        @Token() token: TokenPayload,
    ): KmsTenantConfigResponseDto {
        const tenantId = token.entity!.id;
        return {
            tenantConfig: this.kmsTenantConfigService.getTenantConfig(tenantId),
            effectiveConfig:
                this.kmsTenantConfigService.getEffectiveConfig(tenantId),
        };
    }

    @Put("providers/config")
    @ApiOperation({
        summary: "Create or replace tenant KMS provider configuration",
    })
    @ApiBody({ type: KmsConfigDto })
    @ApiResponse({
        status: 200,
        description: "Updated tenant KMS config.",
        type: KmsTenantConfigResponseDto,
    })
    updateTenantKmsConfig(
        @Token() token: TokenPayload,
        @Body() body: KmsConfigDto,
    ): KmsTenantConfigResponseDto {
        const tenantId = token.entity!.id;
        const effectiveConfig = this.kmsTenantConfigService.saveTenantConfig(
            tenantId,
            body,
        );

        return {
            tenantConfig: this.kmsTenantConfigService.getTenantConfig(tenantId),
            effectiveConfig,
        };
    }

    @Delete("providers/config")
    @ApiOperation({
        summary: "Delete tenant KMS provider configuration",
        description:
            "Removes <CONFIG_FOLDER>/<tenantId>/kms.json and falls back to global KMS config.",
    })
    @ApiResponse({
        status: 204,
        description: "Tenant-specific KMS config removed.",
    })
    @HttpCode(204)
    deleteTenantKmsConfig(@Token() token: TokenPayload): void {
        this.kmsTenantConfigService.deleteTenantConfig(token.entity!.id);
    }

    /**
     * List all key chains for the tenant.
     */
    @Get()
    @ApiOperation({ summary: "List all key chains for the tenant" })
    @ApiResponse({
        status: 200,
        description: "List of key chains",
        type: [KeyChainResponseDto],
    })
    @ApiQuery({
        name: "usageType",
        required: false,
        enum: KeyUsageType,
        description: "Optional usage type filter",
    })
    getAll(
        @Token() token: TokenPayload,
        @Query("usageType") usageType?: KeyUsageType,
    ): Promise<KeyChainResponseDto[]> {
        return this.keyChainService.getAll(token.entity!.id, usageType);
    }

    /**
     * Get a specific key chain by ID.
     */
    @Get(":id")
    @ApiOperation({ summary: "Get a key chain by ID" })
    @ApiResponse({
        status: 200,
        description: "The key chain",
        type: KeyChainResponseDto,
    })
    @ApiResponse({ status: 404, description: "Key chain not found" })
    getById(
        @Token() token: TokenPayload,
        @Param("id") id: string,
    ): Promise<KeyChainResponseDto> {
        return this.keyChainService.getById(token.entity!.id, id);
    }

    /**
     * Export a key chain in config-import-compatible format.
     * The response includes private key material and can be saved as a JSON file
     * for provisioning via the config import mechanism.
     */
    @Get(":id/export")
    @ApiOperation({
        summary: "Export a key chain in config-import format",
        description:
            "Returns the key chain including private key material in the same format used by config import JSON files.",
    })
    @ApiResponse({
        status: 200,
        description: "Key chain export data",
        type: KeyChainExportDto,
    })
    @ApiResponse({ status: 404, description: "Key chain not found" })
    export(
        @Token() token: TokenPayload,
        @Param("id") id: string,
    ): Promise<KeyChainExportDto> {
        return this.keyChainService.export(token.entity!.id, id);
    }

    /**
     * Create a new key chain.
     */
    @Post()
    @ApiOperation({ summary: "Create a new key chain" })
    @ApiResponse({
        status: 201,
        description: "Key chain created successfully",
        type: KeyChainIdResponseDto,
    })
    async create(
        @Token() token: TokenPayload,
        @Body() body: KeyChainCreateDto,
    ): Promise<KeyChainIdResponseDto> {
        const id = await this.keyChainService.create(token.entity!.id, body);
        return { id };
    }

    /**
     * Import an existing key chain with provided key material and optional certificate.
     */
    @Post("import")
    @ApiOperation({ summary: "Import an existing key chain" })
    @ApiResponse({
        status: 201,
        description: "Key chain imported successfully",
        type: KeyChainIdResponseDto,
    })
    async import(
        @Token() token: TokenPayload,
        @Body() body: KeyChainImportDto,
    ): Promise<KeyChainIdResponseDto> {
        const id = await this.keyChainService.importKeyChain(
            token.entity!.id,
            body,
        );
        return { id };
    }

    /**
     * Update a key chain.
     */
    @Put(":id")
    @ApiOperation({ summary: "Update key chain metadata and rotation policy" })
    @ApiResponse({ status: 204, description: "Key chain updated successfully" })
    @ApiResponse({ status: 404, description: "Key chain not found" })
    @HttpCode(204)
    async update(
        @Token() token: TokenPayload,
        @Param("id") id: string,
        @Body() body: KeyChainUpdateDto,
    ): Promise<void> {
        await this.keyChainService.update(token.entity!.id, id, body);
    }

    /**
     * Delete a key chain.
     */
    @Delete(":id")
    @ApiOperation({ summary: "Delete a key chain" })
    @ApiResponse({ status: 204, description: "Key chain deleted successfully" })
    @ApiResponse({ status: 404, description: "Key chain not found" })
    @HttpCode(204)
    async delete(
        @Token() token: TokenPayload,
        @Param("id") id: string,
    ): Promise<void> {
        await this.keyChainService.delete(token.entity!.id, id);
    }

    /**
     * Manually trigger key rotation for a key chain.
     */
    @Post(":id/rotate")
    @ApiOperation({ summary: "Rotate the signing key in a key chain" })
    @ApiResponse({ status: 204, description: "Key chain rotated successfully" })
    @ApiResponse({ status: 404, description: "Key chain not found" })
    @HttpCode(204)
    async rotate(
        @Token() token: TokenPayload,
        @Param("id") id: string,
    ): Promise<void> {
        await this.keyChainService.rotate(token.entity!.id, id);
    }
}

results matching ""

    No results matching ""