52668c2c88
@UploadedFile()/@UploadedFiles() in cert-manager.controller auf UploadedFileLike. Der Dienst nimmt CertFileLike = Pick<UploadedFileLike, 'buffer' | 'originalname'> - genau die zwei Felder, die er liest; mimetype und size bleiben draussen, weil kein Zweig sie anfasst. Belegt statt behauptet: keiner der sechs FileInterceptor/FilesInterceptor- Aufrufe in apps/api/src setzt eine storage-Option, also gilt multers memoryStorage, also ist buffer ein Buffer. @types/multer bleibt uninstalliert (D-04). Damit fallen 25 Zusicherungen der Form file.buffer as Buffer und file.originalname as string ersatzlos weg - sie standen nur da, weil file ein any war. as unknown as bleibt bei 33, noNonNullAssertion bei 56. noExplicitAny in apps/api/src: 66 -> 56 (Ausgang der Aufgabe: 149, Schranke des Plans: 75). type-check 4/4, lint 5/5, apps/api 72/1143, apps/web 73/531. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
122 lines
3.7 KiB
TypeScript
122 lines
3.7 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
Body,
|
|
Controller,
|
|
Post,
|
|
UploadedFile,
|
|
UploadedFiles,
|
|
UseInterceptors,
|
|
} from '@nestjs/common';
|
|
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
|
|
import { UseModule } from '../module-registry/module.guard';
|
|
import type { UploadedFileLike } from '../auth/types/auth-user';
|
|
import { CertManagerService } from './cert-manager.service';
|
|
|
|
/**
|
|
* CertManagerController — 4 POST endpoints for certificate operations.
|
|
*
|
|
* All routes are protected by:
|
|
* - Global JwtAuthGuard (authentication)
|
|
* - Global TenantGuard (tenant context)
|
|
* - @UseModule('cert-manager') ModuleGuard (module activation check)
|
|
*
|
|
* File size limit: 5 MB per file (T-09-03 — DoS mitigation).
|
|
* Password parameter is never passed to a logger (T-09-02 — InfoDisc mitigation).
|
|
*/
|
|
@Controller('modules/cert-manager')
|
|
@UseModule('cert-manager')
|
|
export class CertManagerController {
|
|
constructor(private readonly certManagerService: CertManagerService) {}
|
|
|
|
/**
|
|
* POST /modules/cert-manager/parse
|
|
* Inspect a single certificate: subject, issuer, validity, SANs, fingerprints.
|
|
* Accepts multipart file upload OR JSON body with pemText.
|
|
*/
|
|
@Post('parse')
|
|
@UseInterceptors(
|
|
FileInterceptor('file', {
|
|
limits: { fileSize: 5 * 1024 * 1024 },
|
|
}),
|
|
)
|
|
async parseCert(
|
|
@UploadedFile() file: UploadedFileLike | undefined,
|
|
@Body('password') password?: string,
|
|
@Body('pemText') pemText?: string,
|
|
) {
|
|
if (!file && !pemText) {
|
|
throw new BadRequestException('No file or PEM text provided');
|
|
}
|
|
return this.certManagerService.parseCert({ file, pemText, password });
|
|
}
|
|
|
|
/**
|
|
* POST /modules/cert-manager/split
|
|
* Split a fullchain.pem or P7B bundle into individual certificates.
|
|
*/
|
|
@Post('split')
|
|
@UseInterceptors(
|
|
FileInterceptor('file', {
|
|
limits: { fileSize: 5 * 1024 * 1024 },
|
|
}),
|
|
)
|
|
async splitCerts(
|
|
@UploadedFile() file: UploadedFileLike | undefined,
|
|
@Body('password') password?: string,
|
|
) {
|
|
if (!file) {
|
|
throw new BadRequestException('No file provided');
|
|
}
|
|
return this.certManagerService.splitCerts({ file, password });
|
|
}
|
|
|
|
/**
|
|
* POST /modules/cert-manager/merge
|
|
* Merge multiple certificates into a PEM chain or PFX bundle.
|
|
* Uses FilesInterceptor (plural) to accept multiple files with field name "files".
|
|
*/
|
|
@Post('merge')
|
|
@UseInterceptors(
|
|
FilesInterceptor('files', 20, {
|
|
limits: { fileSize: 5 * 1024 * 1024 },
|
|
}),
|
|
)
|
|
async mergeCerts(
|
|
@UploadedFiles() files: UploadedFileLike[],
|
|
@Body('outputFormat') outputFormat: string,
|
|
@Body('password') password?: string,
|
|
) {
|
|
if (!files || files.length < 2) {
|
|
throw new BadRequestException('At least 2 files required for merge');
|
|
}
|
|
return this.certManagerService.mergeCerts({ files, outputFormat, password });
|
|
}
|
|
|
|
/**
|
|
* POST /modules/cert-manager/convert
|
|
* Convert a certificate between PEM, DER, and P7B formats.
|
|
* Accepts a multipart file upload OR a pemText body field.
|
|
*
|
|
* T-09-03: fileSize limit 5 MB (DoS mitigation)
|
|
* T-09-04: global JwtAuthGuard + @UseModule('cert-manager') ModuleGuard
|
|
* T-09-02: password is never passed to the logger
|
|
*/
|
|
@Post('convert')
|
|
@UseInterceptors(
|
|
FileInterceptor('file', {
|
|
limits: { fileSize: 5 * 1024 * 1024 },
|
|
}),
|
|
)
|
|
async convertCert(
|
|
@UploadedFile() file: UploadedFileLike | undefined,
|
|
@Body('targetFormat') targetFormat: string,
|
|
@Body('password') password?: string,
|
|
@Body('pemText') pemText?: string,
|
|
) {
|
|
if (!file && !pemText) {
|
|
throw new BadRequestException('No file or PEM text provided');
|
|
}
|
|
return this.certManagerService.convertCert({ file, pemText, targetFormat, password });
|
|
}
|
|
}
|