# MPM – Architekturdokumentation ## 1. Leitentscheidung Die Management-Plattform ist ein **Application Host und Gateway für Module** – nicht selbst die Fachanwendung. ``` Management-Plattform Module (ab Phase 3) ├── Identität & Login ├── eigene Oberfläche ├── Benutzer & Rollen ├── eigene Business-Logik ├── Rechte (plattformweit) ├── eigene API ├── Modul-Verwaltung ├── eigene Daten (eigenes Schema) ├── Routing / Gateway ├── eigene Migrationen ├── API & Administration └── optional eigene Modulrechte └── Audit & Monitoring ``` Die Plattform darf **niemals** von einem einzelnen Modul abhängig sein. Wird ein Modul entfernt, bleiben Login, Benutzerverwaltung, Administration, Rechteverwaltung und alle anderen Module voll funktionsfähig. ## 2. Komponenten & Container-Layout ``` Internet │ ▼ HTTPS (Produktion; lokal :8080) Docker Container "mpm-platform" (Benutzer: app, kein Root) ┌──────────────────────────────────────────────┐ │ Supervisor (Prozessmanager, Crash-Recovery) │ │ │ │ │ ├── nginx Reverse Proxy :8080 │ │ ├── platform-backend NestJS API 127.0.0.1:3000│ │ └── [ab Phase 3: Modul-Prozesse, │ │ z. B. Kalender 127.0.0.1:41001] │ └──────────────────────────────────────────────┘ │ ▼ PostgreSQL "mpm-postgres" (eigener Container, persistentes Volume "postgres-data") ``` Grundsätze: - **Ein** Applikationscontainer; Module laufen als interne Prozesse darin (kein Docker-in-Docker, kein Docker-Socket). - Interne Ports (z. B. 3000, 41001+) werden **nie** nach außen veröffentlicht; nur Nginx lauscht auf 8080. - PostgreSQL liegt außerhalb des Applikationscontainers → Container-Neustarts/Updates zerstören keine Nutzdaten. - Alle Prozesse laufen als unprivilegierter Benutzer `app`. ## 3. Prozessmodell Supervisor verwaltet Start/Stop/Restart, Crash-Recovery und Logging aller Prozesse: | Prozess | Adresse | Aufgabe | |---|---|---| | nginx | 0.0.0.0:8080 | Reverse Proxy, statisches Frontend, Security-Header | | platform-backend | 127.0.0.1:3000 | Management-API `/api/v1` | | Modul-Prozesse (ab Phase 3) | 127.0.0.1:41001+ | Fachanwendungen | Stürzt ein Modulprozess ab, startet Supervisor ihn automatisch neu – die Management-Plattform läuft weiter. ## 4. Request-Flow ### Login ``` Browser ──POST /api/v1/auth/login──▶ Nginx ──▶ NestJS │ Validierung (Zod) + Rate Limit + Lockout + Argon2id │ Session in PostgreSQL anlegen (Token nur gehasht gespeichert) ◀── Set-Cookie: mpm_session (HttpOnly, SameSite=Lax) Set-Cookie: mpm_csrf (lesbar für CSRF-Doppel-Submit) ``` ### Authentifizierter Request ``` Browser ──▶ Nginx ──▶ SessionGuard (Session gültig? User aktiv?) ├── CsrfGuard (bei POST/PATCH/DELETE: X-CSRF-Token) ├── RolesGuard (RBAC: ADMIN/USER) └── Controller/Service (Business-Logik) ``` ### Modul-Routing (ab Phase 4, geplant) ``` Browser ──▶ Nginx (/slug) ──▶ Management-Gateway ├── User identifizieren (Session) ├── Permission Check (user_module_permissions) ├── DENIED → 403 └── ALLOWED → Modul-Gateway → Modulprozess ``` Ein Modul vertraut **niemals** allein auf die URL; die Plattform übergibt die Identität sicher an das Modul (Modul-API-Vertrag, Phase 6). ## 5. Datenmodell (Phase 1) Alle Plattform-Tabellen liegen im Standard-Schema (ab Phase 3 Auslagerung in ein `management`-Schema und pro Modul ein eigenes Schema). | Tabelle | Zweck | |---|---| | `roles` | Globale Rollen: `ADMIN`, `USER` | | `users` | Benutzer mit Argon2id-Hash, Rollen-FK, Lockout-Feldern | | `sessions` | Serverseitige Sessions (Token SHA-256-gehasht, CSRF-Token, Gleitende Verlängerung) | | `audit_logs` | Zentrales Audit (LOGIN_SUCCESS, LOGIN_FAILED, LOGOUT, …) | | `schema_migrations` | Angewandte Migrationen (eigener Runner mit Advisory-Lock) | Geplant (Phase 3+): `modules`, `user_module_permissions`, `module_settings`, `system_settings`, `api_clients`. ## 6. Authentifizierung & Sessions - **Argon2id** (19 MiB, t=2, p=1 – OWASP-Empfehlung) für Passwort-Hashing; niemals Klartextpasswörter. - **Serverseitige Sessions**: 256-Bit-Zufalls-Token im `HttpOnly`-Cookie; in der DB wird nur der SHA-256-Hash gespeichert. `Secure` (produktiv), `SameSite=Lax`, kurze Laufzeit (`SESSION_TTL_MINUTES`), gleitende Verlängerung. - **CSRF-Schutz**: Doppel-Submit – Server speichert pro Session ein CSRF-Token; bei jedem zustandsändernden Request muss der Header `X-CSRF-Token` (konstantzeitvergleich) übereinstimmen. - **Rate Limiting**: In-Memory-Fenster pro IP für Login (Standard: 10 Versuche / 5 Minuten). - **Account Lockout**: Nach `LOGIN_MAX_ATTEMPTS` Fehlversuchen wird das Konto für `LOGIN_LOCKOUT_MINUTES` gesperrt. - **Keine User-Enumeration**: Unbekannter Benutzer, inaktiver Benutzer und falsches Passwort liefern dieselbe generische 401-Meldung. - **Audit-Log**: Jeder Login-Versuch (Erfolg/Misserfolg mit Grund), Logout. ## 7. Sicherheitsmaßnahmen (Phase 1) - Security-Header via Helmet (Backend) und Nginx (Frontend): CSP, `X-Frame-Options: DENY`, `X-Content-Type-Options`, `Referrer-Policy`. - Zod-Validierung **aller** Eingaben (serverseitig, clientseitige Validierung ist nur UX). - SQL nur parametrisiert (`pg`), keine String-Konkatenation. - Strukturierte Fehlermeldungen ohne Stack-Traces nach außen. - Secrets ausschließlich über Umgebungsvariablen (`.env` ist gitignored). - Interne Ports nicht veröffentlicht; PostgreSQL nur auf Loopback des Hosts gemappt. - Unprivilegierter Container-Benutzer; kein Root in der Laufzeit. Security Hardening (Penetrationstests, Dependency/Container-Scans, HSTS, Backup/Restore) ist gemäß Arbeitsplan Phase 10. ## 8. API-Übersicht (Phase 1) | Methode | Pfad | Auth | Beschreibung | |---|---|---|---| | POST | `/api/v1/auth/login` | Public | Anmeldung, setzt Session-Cookies | | POST | `/api/v1/auth/logout` | Session | Beendet die aktuelle Session | | GET | `/api/v1/auth/me` | Session | Angemeldeter Benutzer (Id, Rolle, …) | | GET | `/api/v1/health` | Public | Systemstatus (Backend, DB-Latenz, Version, Uptime) | OpenAPI/Swagger UI: `/api/docs` · Ab Phase 2: `/api/v1/users/*`, `/api/v1/modules/*`, `/api/v1/permissions/*`, `/api/v1/audit/*`. ## 9. Modul-Vertrag (Ausblick Phase 3+) Jedes Modul liefert ein Manifest (`module.json`) und einen minimalen API-Vertrag: ```json { "id": "calendar", "name": "Kalender", "version": "1.0.0", "slug": "kalender-tool", "description": "Kalenderverwaltung", "runtime": "node", "entrypoint": "server.js", "port": 41001, "healthcheck": "/health", "apiVersion": "v1" } ``` Minimal-API je Modul: `GET /health`, `GET /api/manifest`, `GET /api/me`. Installation als ZIP-Paket mit Validierung, Dependency-Installation, Migrationen und Healthcheck. Details: [`modules/README.md`](../modules/README.md). ## 10. Architektur-Entscheidungen (ADR-Kurzform) | # | Entscheidung | Begründung | |---|---|---| | 1 | Serverseitige Sessions statt reiner JWTs | Widerrufbar (Logout, Deaktivierung), kein Token-Diebstahl-Risiko, einfache Lockout-Logik; JWT/Access-Tokens für die Modul-Kommunikation ab Phase 6 | | 2 | Opaque 256-Bit-Token, DB speichert nur SHA-256-Hash | DB-Leck kompromittiert keine Sessions | | 3 | PostgreSQL außerhalb des Applikationscontainers | Persistenz bei Container-Updates, klare Trennung von Zustand und Compute | | 4 | Supervisor statt systemd im Container | Container-Standard, verwaltet mehrere Prozesse ohne Root, Crash-Recovery | | 5 | Nginx im Container als einziger öffentlicher Endpunkt | Zentrales Routing, Security-Header, interne Ports bleiben verborgen | | 6 | `pg` + eigener Migration-Runner statt ORM | Wenig Abhängigkeiten, volle SQL-Kontrolle, Migrationen transaktionssicher mit Advisory-Lock | | 7 | Zod statt class-validator | Ein Validierungs-Framework für Frontend und Backend, TypeScript-Inferenz | | 8 | Monorepo mit unabhängigen Apps (kein Workspace-Tooling) | Einfache, cache-freundliche Docker-Builds, keine Tool-Lock-in | ## 11. Frontend-Architektur - Schichten: `components/ui` (Design-System) → `components/layout` (App-Shell) → `features/*` (Fachseiten) → `pages` (Fehlerseiten). - Datenzugriff ausschließlich über `lib/api-client` (CSRF-Header, 401-Behandlung) und TanStack Query. - Routing: öffentliche Login-Seite, geschützter Bereich (`RequireAuth`), Admin-Bereich (`RequireAdmin`, RBAC im Frontend nur UX – erzwungen wird serverseitig). - Responsiv: Sidebar wird auf Mobilgeräten zur Overlay-Navigation.