diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 72da4af..bdebd91 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -294,7 +294,7 @@ Decimal phases appear between their surrounding integers in numeric order. 5. Password-protected PFX/PKCS12 files can be opened (password prompt) and created (password input) 6. Module appears in the module registry with slug `cert-manager` -**Plans**: 4/6 plans executed +**Plans**: 5/6 plans executed Plans: **Wave 1** @@ -312,7 +312,7 @@ Plans: **Wave 4** *(blocked on Wave 3 completion)* -- [ ] 09-05-PLAN.md — Convert slice: convertCert (PEM/DER/P7B round-trips) + POST /convert + Convert tab (CERT-04) +- [x] 09-05-PLAN.md — Convert slice: convertCert (PEM/DER/P7B round-trips) + POST /convert + Convert tab (CERT-04) **Wave 5** *(blocked on Wave 4 completion)* @@ -335,4 +335,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 | 6. Desktop Client & CI/CD | 2/3 | In Progress| | | 7. DKV Fleet Module | 6/6 | Complete | 2026-06-27 | | 8. Dashboard Widgets Vollimplementierung | 4/4 | Complete | 2026-07-01 | -| 9. Cert Manager Module | 4/6 | In Progress| | +| 9. Cert Manager Module | 5/6 | In Progress| | diff --git a/.planning/STATE.md b/.planning/STATE.md index 39f151e..0cb2c15 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -6,14 +6,14 @@ current_phase: 9 current_phase_name: cert-manager-module status: executing stopped_at: context exhaustion at 75% (2026-07-01) -last_updated: "2026-07-02T05:17:41.739Z" +last_updated: "2026-07-02T05:41:29.230Z" last_activity: 2026-07-01 last_activity_desc: Phase 9 execution started progress: total_phases: 9 completed_phases: 7 total_plans: 40 - completed_plans: 37 + completed_plans: 38 percent: 78 --- @@ -29,7 +29,7 @@ See: .planning/PROJECT.md (updated 2026-06-18) ## Current Position Phase: 9 (cert-manager-module) — EXECUTING -Plan: 3 of 6 +Plan: 4 of 6 Status: Ready to execute Last activity: 2026-07-01 — Phase 9 execution started @@ -69,6 +69,7 @@ Progress: [█████████░] 93% | Phase 07-dkv-fleet-module P06 | 5min | 2 tasks | 7 files | | Phase 09 P03 | 17 | 3 tasks | 6 files | | Phase 09-cert-manager-module P04 | 4 | 3 tasks | 5 files | +| Phase 09-cert-manager-module P05 | 8 | 3 tasks | 6 files | ## Accumulated Context @@ -156,6 +157,6 @@ Items acknowledged and carried forward from previous milestone close: ## Session Continuity -Last session: 2026-07-02T05:17:32.493Z +Last session: 2026-07-02T05:41:29.220Z Stopped at: context exhaustion at 75% (2026-07-01) Resume file: .planning/phases/08-dashboard-widgets-vollimplementierung/08-CONTEXT.md diff --git a/.planning/phases/09-cert-manager-module/09-05-SUMMARY.md b/.planning/phases/09-cert-manager-module/09-05-SUMMARY.md new file mode 100644 index 0000000..5b63d60 --- /dev/null +++ b/.planning/phases/09-cert-manager-module/09-05-SUMMARY.md @@ -0,0 +1,178 @@ +--- +phase: 09-cert-manager-module +plan: "05" +subsystem: api+frontend +tags: [cert-manager, convertCert, node-forge, p7b, convert-tab, tdd, vitest, file-response] +dependency_graph: + requires: [09-01, 09-02, 09-03, 09-04] + provides: [cert-manager-convert-endpoint, file-response-interface, convert-tab-ui] + affects: + - apps/api/src/cert-manager/cert-manager.service.ts + - apps/api/src/cert-manager/cert-manager.controller.ts + - apps/api/src/cert-manager/cert-manager.service.spec.ts + - apps/web/src/app/(portal)/modules/cert-manager/actions.ts + - apps/web/src/app/(portal)/modules/cert-manager/components/ConvertTab.tsx + - apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx +tech_stack: + added: [] + patterns: [tdd-red-green, node-forge-der-hex-base64, file-response-download, vi.spyOn-downloadBase64] +key_files: + created: [] + modified: + - apps/api/src/cert-manager/cert-manager.service.ts + - apps/api/src/cert-manager/cert-manager.controller.ts + - apps/api/src/cert-manager/cert-manager.service.spec.ts + - apps/web/src/app/(portal)/modules/cert-manager/actions.ts + - apps/web/src/app/(portal)/modules/cert-manager/components/ConvertTab.tsx + - apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx +decisions: + - "DER output: bytesToHex → Buffer.from(hex,'hex') → base64 to avoid utf-8 corruption (Pitfall 1, T-09-06)" + - "P7B output: pkcs7.createSignedData + asn1.toDer + pem.encode (PEM-wrapped PKCS7)" + - "FORMAT_MIME map at module level for pem/der/p7b mimeType lookup" + - "Controller convert: adds @Body('pemText') so text-paste path works; rejects when neither file nor pemText" + - "ConvertTab shows empty state + format selector simultaneously (mirrors InspectTab button-always-visible pattern)" +metrics: + duration: "~8 minutes" + completed: "2026-07-02T07:39:00Z" + tasks_completed: 3 + files_created: 0 + files_modified: 6 +requirements: [CERT-04] +status: complete +--- + +# Phase 09 Plan 05: Convert Vertical Slice Summary + +**One-liner:** convertCert converts PEM/DER/P7B with byte-identical round trips using bytesToHex→hex→base64 for DER safety; POST /convert wired; ConvertTab renders format selector + Konvertieren button + blob download. + +## Objective + +Third functional vertical slice: certificate format conversion. Delivers CERT-04 (PEM/DER/P7B targets). Reuses parse helpers from Plans 03–04 and downloadBase64 pattern from Plan 04. + +## Tasks Completed + +| # | Name | Type | Commit | Status | +|---|------|------|--------|--------| +| 1 | RED — failing convertCert spec | test | 37db58b | done | +| 2 | GREEN — implement convertCert + wire POST /convert | feat | 5969464 | done | +| 3 | Convert tab UI + convertCertAction + render test | feat | f89d656 | done | + +## What Was Built + +### Task 1: RED — failing convertCert spec + +Added 4 new convertCert tests to `cert-manager.service.spec.ts`: + +- **PEM→DER round-trip:** calls `convertCert({ pemText, targetFormat: 'der' })`, decodes base64 DER, re-parses via `forge.asn1.fromDer` + `forge.pki.certificateFromAsn1`, asserts CN matches original +- **DER→PEM round-trip:** calls `convertCert({ file: { originalname: 'c.der', buffer }, targetFormat: 'pem' })`, decodes base64 PEM, re-parses via `forge.pki.certificateFromPem`, asserts CN matches +- **PEM→P7B:** calls `convertCert({ pemText, targetFormat: 'p7b' })`, decodes base64 PKCS7 PEM, parses via `forge.pkcs7.messageFromPem`, asserts ≥1 certificate enclosed +- **Malformed input:** expects BadRequestException for garbage PEM text + +All 4 fail against NotImplementedException stub (RED confirmed). 19 prior tests remain green. + +### Task 2: GREEN — convertCert implementation + +**`cert-manager.service.ts`:** +- Exported `FileResponse` interface: `{ filename, content (base64), mimeType }` +- Added `FORMAT_MIME` map: `{ pem: 'application/x-pem-file', der: 'application/x-x509-ca-cert', p7b: 'application/x-pkcs7-certificates' }` +- Implemented `convertCert({ file?, pemText?, targetFormat, password? }): Promise`: + - Validates `targetFormat` against FORMAT_MIME (400 if unsupported) + - Input resolution: PEM text → `parsePemChain`; DER file → `forge.asn1.fromDer(toForgeBuffer(...))`; PFX → `pkcs12FromAsn1` + getBags; P7B → sniff + `messageFromPem`/`messageFromAsn1` + - PEM output: `certificateToPem(cert)` → `Buffer.from(str, 'utf-8').toString('base64')` + - DER output: `forge.util.bytesToHex(toDer(certificateToAsn1(cert)).getBytes())` → `Buffer.from(hex, 'hex').toString('base64')` — avoids Pitfall 1 / T-09-06 + - P7B output: `pkcs7.createSignedData()` + `addCertificate(cert)` + `asn1.toDer(toAsn1())` + `pem.encode({ type:'PKCS7' })` → base64 + - All forge ops in try/catch → BadRequestException + +**`cert-manager.controller.ts`:** +- Added `@Body('pemText') pemText?: string` to `convertCert` endpoint +- Changed guard from "reject if no file" to "reject if no file AND no pemText" +- Passes `pemText` through to service + +**Result: all 23 API tests pass (19 prior + 4 new convertCert).** + +### Task 3: ConvertTab UI + convertCertAction + render tests + +**`actions.ts`:** +- Added `FileResponse` interface (mirrors API response) +- Added `convertCertAction(input, targetFormat): Promise` — builds FormData with file/pemText/password + targetFormat, delegates to `postForm('convert', form)` + +**`components/ConvertTab.tsx`:** +- `'use client'` component with `useState` for loading/error/targetFormat +- Native `