src/crypto/key/key-chain.controller.ts
key-chain
KeyChainController manages unified key chains.
A key chain encapsulates:
Methods |
|
| Async create | |||||||||
create(token: TokenPayload, body: KeyChainCreateDto)
|
|||||||||
Decorators :
@Post()
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:222
|
|||||||||
|
Create a new key chain.
Parameters :
Returns :
Promise<KeyChainIdResponseDto>
|
| Async delete | |||||||||
delete(token: TokenPayload, id: string)
|
|||||||||
Decorators :
@Delete(':id')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:275
|
|||||||||
|
Delete a key chain.
Parameters :
Returns :
Promise<void>
|
| deleteTenantKmsConfig | ||||||
deleteTenantKmsConfig(token: TokenPayload)
|
||||||
Decorators :
@Delete('providers/config')
|
||||||
|
Defined in src/crypto/key/key-chain.controller.ts:143
|
||||||
|
Parameters :
Returns :
void
|
| export | |||||||||
export(token: TokenPayload, id: string)
|
|||||||||
Decorators :
@Get(':id/export')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:205
|
|||||||||
|
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 :
Returns :
Promise<KeyChainExportDto>
|
| getAll | |||||||||
getAll(token: TokenPayload, usageType?: KeyUsageType)
|
|||||||||
Decorators :
@Get()
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:163
|
|||||||||
|
List all key chains for the tenant.
Parameters :
Returns :
Promise<KeyChainResponseDto[]>
|
| getById | |||||||||
getById(token: TokenPayload, id: string)
|
|||||||||
Decorators :
@Get(':id')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:181
|
|||||||||
|
Get a specific key chain by ID.
Parameters :
Returns :
Promise<KeyChainResponseDto>
|
| getProviders | ||||||
getProviders(token: TokenPayload)
|
||||||
Decorators :
@Get('providers')
|
||||||
|
Defined in src/crypto/key/key-chain.controller.ts:63
|
||||||
|
Get available KMS providers and their capabilities.
Parameters :
Returns :
KmsProvidersResponseDto
|
| getProvidersHealth | ||||||
getProvidersHealth(token: TokenPayload)
|
||||||
Decorators :
@Get('providers/health')
|
||||||
|
Defined in src/crypto/key/key-chain.controller.ts:78
|
||||||
|
Liveness/readiness probe for every registered KMS provider.
Parameters :
Returns :
Promise<ProviderHealthResponseDto[]>
|
| getTenantKmsConfig | ||||||
getTenantKmsConfig(token: TokenPayload)
|
||||||
Decorators :
@Get('providers/config')
|
||||||
|
Defined in src/crypto/key/key-chain.controller.ts:95
|
||||||
|
Parameters :
Returns :
KmsTenantConfigResponseDto
|
| Async import | |||||||||
import(token: TokenPayload, body: KeyChainImportDto)
|
|||||||||
Decorators :
@Post('import')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:240
|
|||||||||
|
Import an existing key chain with provided key material and optional certificate.
Parameters :
Returns :
Promise<KeyChainIdResponseDto>
|
| Async rotate | |||||||||
rotate(token: TokenPayload, id: string)
|
|||||||||
Decorators :
@Post(':id/rotate')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:290
|
|||||||||
|
Manually trigger key rotation for a key chain.
Parameters :
Returns :
Promise<void>
|
| Async update | ||||||||||||
update(token: TokenPayload, id: string, body: KeyChainUpdateDto)
|
||||||||||||
Decorators :
@Put(':id')
|
||||||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:259
|
||||||||||||
|
Update a key chain.
Parameters :
Returns :
Promise<void>
|
| updateTenantKmsConfig | |||||||||
updateTenantKmsConfig(token: TokenPayload, body: KmsConfigDto)
|
|||||||||
Decorators :
@Put('providers/config')
|
|||||||||
|
Defined in src/crypto/key/key-chain.controller.ts:116
|
|||||||||
|
Parameters :
Returns :
KmsTenantConfigResponseDto
|
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);
}
}