Files
tessera-ctl/.planning/quick/260907-e8k-zugriffs-guard-greift-nicht-auf-modul-ei/260907-e8k-SUMMARY.md
T
schalli 5504931af3 docs(quick-260907-e8k): complete Zugriffs-Guard-Fix fuer modul-eigene Routen
Beide Tasks des Plans ausgefuehrt, verifiziert und committed
(4a23e13, 69b6418). Automatisierte Verifikation vollstaendig gruen:
Gate-Tests, Layout-Tests, volle Web-Suite (213/213), Typpruefung und
Produktionsbau. Browser-Gegenprobe (A/B/C) bleibt laut Plan Aufgabe
des Orchestrators nach Docker-Rebuild — WINDOWS #10 bleibt bis dahin
offen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:29:19 +02:00

11 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
quick-260907-e8k 01 auth
nextjs
app-router
server-components
next-intl
vitest
module-access
phase provides
15-modul-berechtigungen-gruppen-user-grants checkModuleAccess (module-access-actions.ts) — fails-closed session-cookie-based access check against GET /modules/active
Shared ModuleAccessDenied 403 markup component (translation-free, props-driven)
Reusable ModuleAccessGate server component wrapping checkModuleAccess with a second fail-closed layer
layout.tsx access gate for each of the four module-owned route trees (cert-manager, dkv-fleet, domaincheck, tender-radar)
Generic [category]/[moduleSlug]/page.tsx route rewired onto the shared gate
module-layouts.test.tsx guarding against future module directories missing a layout
15-modul-berechtigungen-gruppen-user-grants
module-routing
module-marketplace
tokens tasks commits
5709 2 2
added patterns
Server-side access gate as a reusable component (ModuleAccessGate) consumed both via App Router layout.tsx (covers whole route subtrees) and directly in a page — single source of truth for the 403 decision and markup
Fail-closed defense in depth: checkModuleAccess already fails closed; ModuleAccessGate adds a second try/catch layer so a future change to the check function cannot silently open the gate
created modified
apps/web/src/components/modules/module-access-denied.tsx
apps/web/src/components/modules/module-access-gate.tsx
apps/web/src/components/modules/module-access-gate.test.tsx
apps/web/src/app/(portal)/modules/cert-manager/layout.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/layout.tsx
apps/web/src/app/(portal)/modules/domaincheck/layout.tsx
apps/web/src/app/(portal)/modules/tender-radar/layout.tsx
apps/web/src/app/(portal)/modules/module-layouts.test.tsx
apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/page.tsx
apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/module-access.test.tsx
403 markup and translation lookup live once in ModuleAccessGate/ModuleAccessDenied; the generic route and all four module-owned layouts render the same component instead of each having its own inline check
ModuleAccessDenied stays translation-free (props for title/body/backToDashboard) — only ModuleAccessGate calls getTranslations, keeping the presentational component trivially testable without mocking next-intl
A layout.tsx per module directory is used instead of per-page checks, because a Next.js App Router layout automatically wraps every nested subroute — my-sources/settings/vehicles close without any file of their own
Module directory coverage test: module-layouts.test.tsx reads the modules/ directory via node:fs at test time and requires a layout.tsx for every non-dynamic subdirectory, so a future module added without a gate fails CI instead of silently reopening the vulnerability
PERM-04
id description requirement verification human_judgment
D1 ModuleAccessDenied — shared, translation-free 403 markup component PERM-04
kind ref status
unit apps/web/src/components/modules/module-access-gate.test.tsx#renders the 403 heading, body, and dashboard link and not the children when access is denied pass
false
id description requirement verification human_judgment
D2 ModuleAccessGate — server-side access gate calling checkModuleAccess, fails closed on denial and on thrown exceptions, passes children through only on explicit grant PERM-04
kind ref status
unit apps/web/src/components/modules/module-access-gate.test.tsx (4 cases: granted, denied, exception, slug pass-through) pass
false
id description requirement verification human_judgment rationale
D3 layout.tsx for cert-manager, dkv-fleet, domaincheck, tender-radar — each wraps its whole route subtree (including nested routes like my-sources, settings, vehicles) in ModuleAccessGate with the correct slug PERM-04
kind ref status
unit apps/web/src/app/(portal)/modules/module-layouts.test.tsx#module layouts — ModuleAccessGate slug wiring pass
kind ref status
unit apps/web/src/app/(portal)/modules/module-layouts.test.tsx#module directory coverage — every module has a layout pass
kind ref status
manual_procedural Browser-Gegenprobe (Abschnitt A/B/C in 260907-e8k-PLAN.md) — vom Orchestrator nach Docker-Rebuild auszufuehren unknown
true Der eigentliche Behebungserfolg (403 als Serveranwort statt Modulseite, keine Ueberdeckung fuer berechtigte Nutzer/Admins) ist nur im echten Browser gegen die Docker-Instanz mit drei realen Konten pruefbar — WINDOWS #10 bleibt laut Plan explizit offen, bis diese Gegenprobe bestanden ist.
id description requirement verification human_judgment
D4 Generische Route [category]/[moduleSlug]/page.tsx auf ModuleAccessGate umgestellt, inline-403-Markup entfernt PERM-04
kind ref status
unit apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/module-access.test.tsx#ExpandedModulePage — passes slug through to ModuleAccessGate pass
false
25min 2026-09-07 complete

Phase quick-260907-e8k Plan 01: Modul-Zugriffs-Guard auf Layout-Ebene Summary

Vier fest verdrahtete Modulrouten (cert-manager, dkv-fleet, domaincheck, tender-radar) liefen bislang komplett am 403-Guard vorbei — sie sind jetzt ueber ein layout.tsx pro Modulverzeichnis an dieselbe ModuleAccessGate-Komponente angeschlossen wie die generische Route.

Performance

  • Duration: 25 min
  • Started: 2026-09-07T08:03Z (ca.)
  • Completed: 2026-09-07T08:28Z
  • Tasks: 2/2 completed
  • Files modified: 10 (8 created, 2 modified)

Accomplishments

  • Das bisher inline in [category]/[moduleSlug]/page.tsx stehende 403-Markup existiert jetzt genau einmal, in ModuleAccessDenied — eine reine, uebersetzungsfreie Praesentationskomponente.
  • ModuleAccessGate buendelt die Zugriffspruefung als wiederverwendbare Server Component: ruft checkModuleAccess, faengt zusaetzlich jede geworfene Ausnahme als "kein Zugriff" ab (zweite Verteidigungslinie ueber die bereits geschlossen ausfallende Funktion), gibt Kinder nur bei explizit gewaehrtem Zugriff durch.
  • Vier neue layout.tsx-Dateien (eine pro Modulverzeichnis) schliessen jetzt auch tender-radar/my-sources, tender-radar/settings, dkv-fleet/settings und dkv-fleet/vehicles ab — ohne eigene Datei je Unterroute, weil ein App-Router-Layout automatisch alle verschachtelten Unterrouten mit umschliesst.
  • Ein Struktur-Test (module-layouts.test.tsx) liest das Modulverzeichnis zur Testzeit per node:fs aus und verlangt fuer jedes nicht-dynamische Unterverzeichnis eine layout.tsx — ein kuenftig hinzugefuegtes Modul ohne Gate faellt damit in der Testsuite auf, nicht erst in Produktion.
  • Die generische Route ist auf dieselben Bausteine umgestellt; ihr eigener Test prueft jetzt die Weitergabe des Slugs statt die 403-Darstellung erneut zu pruefen (die liegt seit Task 1 vollstaendig in module-access-gate.test.tsx).

Task Commits

Each task was committed atomically:

  1. Task 1: Gemeinsame 403-Komponente und ModuleAccessGate mit Tests - 4a23e13 (feat)
  2. Task 2: Vier Modul-Layouts, Strukturtest und Umstellung der generischen Route - 69b6418 (feat)

Files Created/Modified

  • apps/web/src/components/modules/module-access-denied.tsx - Uebersetzungsfreie 403-Praesentationskomponente (title/body/backToDashboard als Props)
  • apps/web/src/components/modules/module-access-gate.tsx - Server-Component-Gate: ruft checkModuleAccess, faengt Ausnahmen ab, rendert Kinder nur bei explizitem Zugriff
  • apps/web/src/components/modules/module-access-gate.test.tsx - 4 Testfaelle: durchgelassen, verweigert, Ausnahme, Slug-Weitergabe
  • apps/web/src/app/(portal)/modules/cert-manager/layout.tsx - Gate mit Slug "cert-manager"
  • apps/web/src/app/(portal)/modules/dkv-fleet/layout.tsx - Gate mit Slug "dkv-fleet"
  • apps/web/src/app/(portal)/modules/domaincheck/layout.tsx - Gate mit Slug "domaincheck"
  • apps/web/src/app/(portal)/modules/tender-radar/layout.tsx - Gate mit Slug "tender-radar"
  • apps/web/src/app/(portal)/modules/module-layouts.test.tsx - Slug-Verdrahtung je Layout + Verzeichnis-Abdeckungstest
  • apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/page.tsx - Auf ModuleAccessGate umgestellt, Inline-403 und direkter checkModuleAccess-Aufruf entfernt
  • apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/module-access.test.tsx - Seiten-Testfaelle auf Gate-Weitergabe umgeschrieben, checkModuleAccess- und ModuleShell-Whitelist-Bloecke unveraendert

Decisions Made

  • Keine neuen i18n-Schluessel: modules.accessDenied.{title,body,backToDashboard} existierten bereits in de.json/en.json und werden unveraendert weiterverwendet.
  • Layouts bleiben gewoehnliche synchrone Server Components ohne Client-Direktive, ohne Hooks — reiner Import + Return des Gates.

Deviations from Plan

None - plan executed exactly as written.

Issues Encountered

None.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

Automatisierte Verifikation vollstaendig gruen (Details unten). Die Browser-Gegenprobe aus Abschnitt A/B/C des Plans (drei Konten, acht+ Adressen, Docker-Rebuild) ist laut Plan explizit Aufgabe des Orchestrators und wurde in dieser Ausfuehrung nicht durchgefuehrt — WINDOWS #10 bleibt bis dahin offen.

Real verification results

  1. pnpm --filter @tessera/web exec vitest run src/components/modules/module-access-gate.test.tsx — 4/4 passed (Task 1 verify)
  2. pnpm --filter @tessera/web type-check — clean, no output (Task 1 verify)
  3. pnpm --filter @tessera/web exec vitest run 'src/app/(portal)/modules/module-layouts.test.tsx' 'src/app/(portal)/modules/[category]/[moduleSlug]/module-access.test.tsx' — 9/9 passed (Task 2 verify)
  4. pnpm --filter @tessera/web exec vitest run (full suite) — 213/213 passed, 36/36 test files (Task 2 verify)
  5. pnpm --filter @tessera/web type-check — clean, no output (Task 2 verify)
  6. pnpm --filter @tessera/web build — succeeded, all 27 routes generated including the four module routes and the generic route; only pre-existing, unrelated warning (jose Edge Runtime CompressionStream/DecompressionStream, not touched by this plan)
  7. Browser-Gegenprobe (human-check) — not run; per plan and per task instructions this is executed by the orchestrator after a Docker rebuild, not by the executor.

Phase: quick-260907-e8k Completed: 2026-09-07

Self-Check: PASSED

All 10 created/modified source files and this SUMMARY.md verified present on disk. Both task commits (4a23e13, 69b6418) verified present in git log.