Commit Graph

4 Commits

Author SHA1 Message Date
Matthias Hochmeister
c099b3acd9 Workstream 11: Tests & Sicherheitshärtung (Phase 7)
Beweist die Auth-Gating-Garantie und härtet das System ab (Definition of
Done #1, #2, #3, #7, #8):

- Routen-Manifest (tests/e2e/routes.manifest.ts) als einzige Quelle der
  Wahrheit; anonyme Seite -> Redirect /login, anonyme API -> 401.
- Kritische auth-gating.spec.ts: genau ein Fall je Manifest-Eintrag, ohne
  Daten-Leak.
- Driftschutz (routes.manifest.spec.ts + tests/unit/routes-manifest.test.ts):
  keine ungetestete neue Route unter src/app/**.
- Default-Deny-Beweis für Server Actions (server-actions-guard.spec.ts +
  tests/unit/server-actions-guard.test.ts): jede "use server"-Funktion ruft
  als erste Anweisung einen Guard; Login-Actions per Allowlist ausgenommen.
- Wiederverwendbare reine Scanner unter tests/support (route-scan, guard-scan)
  — offline lauffähig, in Vitest und Playwright geteilt.
- rbac-scoping, search-eta, login-ratelimit, security-headers Specs (gegen
  geseedeten Server; in der Sandbox deferred, per test.skip abgesichert).
- global-setup (Migration + Seed) und auth.setup (Login je Konto ->
  storageState); Playwright-Projekte setup -> chromium verdrahtet.
- src/lib/security/headers.test.ts: statischer Beleg für CSP, HSTS,
  X-Frame-Options DENY, nosniff, Permissions-Policy.
- vitest.config.ts: Coverage-Schwellen (>=90 %) für src/lib/search + src/lib/geo.
- package.json: Scripts test:unit, test:coverage, test:e2e, test:e2e:gating.
- docs/reference/sicherheitshaertung-checkliste.md: jeder Härtungspunkt mit
  Test/Befehl und Negativ-Probe.

Offline verifiziert: tsc --noEmit (0), vitest run (229 passed / 7 db-skipped),
drizzle-kit check (ok), next build (exit 0), next lint (0 Fehler),
playwright --list (98 Tests, 15 Dateien). DB-/Server-/Browser-abhängige
E2E-Läufe sind deferred (kein Postgres/Server in der Sandbox).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:17:10 +02:00
Matthias Hochmeister
d50ec765ab Workstream 10: Deployment (Docker + externes Traefik) (Phase 7)
Liefert das reproduzierbare Compose-Setup hinter EXTERNEM Traefik:

- Dockerfile (multi-stage deps/builder/runner, Next.js standalone, non-root
  UID/GID 1001, HEALTHCHECK gegen /api/health).
- docker/entrypoint.sh: wartet via pg_isready auf Postgres, wendet Migrationen
  idempotent an (docker/migrate.mjs, plain ESM ohne tsx/drizzle-kit), optionaler
  Seed (RUN_SEED), dann exec node server.js.
- docker-compose.yml: genau vier Services (app, postgres, osrm, nominatim),
  KEIN Proxy-Service; externes traefik-Netz + internes Netz; Traefik-Labels
  (Host, websecure, tls.certresolver, Security-Header-Middleware);
  Postgres-/App-Healthchecks; AUTH_URL/AUTH_TRUST_HOST/Forwarded-Header.
- docker-compose.override.yml.example: lokal :3000 ohne TLS (http AUTH_URL).
- .dockerignore, Makefile (build/up/down/logs/deploy/data/config).
- .env.example: voller Vertrag inkl. APP_HOST, TRAEFIK_*, POSTGRES_*, RUN_SEED.
- docs/reference/deployment-traefik.md: externes Netz, Authentik-Redirect-URI
  https://${APP_HOST}/api/auth/callback/authentik, Forwarded-Header/Cookies,
  /api/health-Allowlist.
- tests/unit/deployment.test.ts (TDD): statische Offline-Verifikation der
  Artefakte; vitest.config.ts nimmt tests/unit/** auf.

Offline verifiziert: tsc --noEmit sauber; vitest run grün (200 passed,
7 db-roundtrip skipped); next build erzeugt .next/standalone/server.js;
sh -n docker/entrypoint.sh ok; make -n deploy zeigt build->up.
Deferred (kein Docker/Postgres in der Sandbox): docker build/run id -u=1001,
docker compose config --services, /api/health anonym 200, End-to-End Traefik.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 12:35:45 +02:00
Matthias Hochmeister
ae5d3589c3 Workstream 3: Authentifizierung & Zugriffskontrolle (Phase 2)
Einheitliche Auth.js-v5-Sitzung (role + brigadeId) über Authentik-OIDC
(Platform-Admins) und Credentials/argon2id (Wehr-Konten). Default-deny
dreifach: Middleware + Layout-Guard im (app)-Segment + 401/403 in API-Routen
und Guards in Server Actions.

- src/lib/auth/roles.ts: Rollen als Single Source of Truth (aus DB-Enum
  abgeleitet) + canAccessBrigade-Wehr-Scoping.
- src/lib/auth/password.ts: argon2id mit OWASP-Minima (Node-only).
- src/lib/auth/rate-limit.ts: Sliding Window (5 Fehlversuche/15 min) auf
  login_attempts; greift im authorize-Callback (beide Login-Pfade).
- src/auth.config.ts: Edge-sicher (kein @/db, kein argon2), Cookie-secure/
  __Secure- umgebungsabhaengig (isHttps).
- src/auth.ts: Credentials + DB-Lookup + Rate-Limit + Authentik-signIn-Gate.
- src/middleware.ts: Allowlist inkl. api/auth, api/health, login, Statics.
- src/lib/auth/guards.ts: requireSession/requireRole/requirePlatformAdmin/
  requireWehrAdmin/requireOwnBrigade + API-Varianten (401/403 ohne Daten-Leak).
- (app)/layout.tsx: requireSession() als erste Zeile aktiviert.
- (auth)/login: page + login-form + Server Actions (Zod, Authentik + lokal).
- api/auth/[...nextauth]/route.ts; api/health/route.ts (anonym 200).
- scripts/seed-auth.ts: idempotenter Erst-Admin-Seed.
- Typen: src/types/next-auth.d.ts.
- Tests: Unit (password/roles/rate-limit) gruen; E2E-Gating-Spec geschrieben
  (deferred, kein Server/DB in Sandbox).

Offline verifiziert: tsc --noEmit, next lint, next build, drizzle-kit check,
vitest (13 Unit-Tests) je ohne Fehler; Edge-Safety-Grep ohne DB/argon2-Import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 09:17:02 +02:00
Matthias Hochmeister
4707844dbc Workstream 1: Projekt-Fundament & Design-System (Phase 0)
Greenfield-Next.js-15-App-Router-Gerüst (TS strict) mit:
- Route-Groups (auth)/(app) inkl. loading/error/not-found je Group;
  Guard-Slot-Kommentar im (app)/layout.tsx (vom Auth-WS zu füllen).
- "Amtlich"/Netzknoten-Designsystem: Tailwind-Tokens (Navy #1B3A5B,
  Signalrot #E2231A, Anthrazit, Nebelgrau, bereit/Wartung), tabular-nums,
  Serif-Display/Inter-Sans via CSS-Variablen, Inline-SVG-Logo.
- Radix-Basiskomponenten (button/input/label/badge/tabs/dialog/select/
  switch/slider); StatusBadge entspricht asset_status.
- Kanonisches src/lib/env.ts (Zod, Fail-Fast) mit ALLEN DB-/Auth-/Geo-Slots
  inkl. AUTH_URL; isHttps-Ableitung. Zentrale i18n-Tabelle de.ts + t().
- Drizzle-Setup: client.ts (Pool-Singleton), leeres schema/index.ts-Barrel
  (KEIN Migrations-Eigentümer), drizzle.config.ts, .env.example.
- next.config.ts: output:standalone, experimental.authInterrupts,
  Security-Header. Vitest + Fail-Fast-Env-Test (TDD, 5/5 grün).

Bewusst KEINE Auth-Logik und KEINE fachlichen Tabellen.

Verifikation: typecheck/lint/test grün; npm run build erzeugt
.next/standalone/server.js; curl /anmelden -> lang="de" + FlorianNetz.
next/font/google durch CSS-Variablen ersetzt (air-gapped-Build).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:57:01 +02:00