Files
mpm/docs/ARCHITECTURE.md

179 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.