Rechtemodell Etappe 1: toter Katalog begradigt, Selbst-Erhoehung geschlossen
Vorarbeit fuer die Rollen-Oberflaeche. Eine Checkbox-Liste ueber einem Katalog, der nicht stimmt, waere schlimmer als gar keine. 18 von 50 Rechten bewachten nichts: tariffs:*, cancellation-periods:*, contract-durations:* und email-providers:* standen im Katalog und waren anhakbar - die Routen prueften in Wahrheit providers:*, platforms:* und settings:*. Die Routen gaten jetzt granular; eine rein additive Migration vererbt jedes neue Recht an jede Rolle, die bisher das Sammelrecht hatte. Nachgerechnet: keine Rolle hat etwas verloren. Der Katalog stand dreifach und divergent - seed.ts, sync-roles.ts und, abweichend, factoryReset. Die dritte Kopie kannte weder audit:* noch gdpr:* und legte DSGVO, Audit-Betrieb und Gegenbuch gar nicht an: Nach einem Werksreset konnte niemand mehr eine Auskunft nach Art. 15 ausfuehren, und aufgefallen waere es erst, wenn eine Frist laeuft. Jetzt eine Quelle: src/config/rechte-katalog.ts. Im selben Code: factoryReset setzte das Admin-Kennwort fest auf "admin" mit bcrypt-Cost 10 - genau das, was seed.ts seit Pentest Runde 12 verbietet. Dieselbe Haertung war nur in einer der beiden Kopien angekommen. Jetzt zufaellig, Cost 12, einmalig im Log. Selbst-Erhoehung geschlossen: neues Recht roles:manage, getrennt von users:*, dazu die Teilmengenregel in rechte.service.ts - niemand kann ein Recht weitergeben, das er selbst nicht haelt. Sie greift auf allen vier Wegen: Rolle anlegen, Rolle aendern, Rollen zuweisen und die drei Haken. Der Haken-Weg war der wichtigste: Er brauchte nur users:create, ein zweites Konto mit "Entwicklerzugriff" anlegen und sich damit anmelden - die Developer-Rolle traegt alle Rechte. Geprueft wird der Zuwachs, nicht der Endzustand, damit Entziehen erlaubt bleibt; und die Rechte des Handelnden kommen frisch aus der Datenbank, nicht aus dem bis zu 15 Minuten alten Token. Systemrollen gesperrt (Role.isSystem): Admin liess sich bisher umbenennen oder leeren - und die versteckten Rollen haengen an ihrem Namen. Role.isHidden ersetzt die im Frontend hartkodierte Namensliste, in der "Gegenbuch" fehlte. Rechteaenderung wirkt sofort: updateRole/deleteRole melden alle Traeger ab. Werksreset und Backup-Restore verlangen zusaetzlich roles:manage - beide loeschen alle Rollen und legen ein frisches admin@admin.com an. Rollenpflege war der einzige Eingriff in die Rechtevergabe ohne SecurityEvent, obwohl sie viele Konten auf einmal trifft. Jetzt PERMISSION_CHANGED - ebenso fuer die bisher stummen Haken DSGVO und Entwicklerzugriff. Aussperr-Ausweg: prisma/rolle-zuweisen.ts. Umgeht die Regel bewusst - wer Shell-Zugang hat, hat ohnehin die Datenbank; ein gestohlener Web-Zugang hat ihn nicht. Schreibt ueber die Hash-Kette, nicht roh. Die Startwache nennt den Befehl im Klartext. Geprueft gegen Dev-DB und frische Wegwerf-DB: 7 Eskalationswege alle 403, 2 Gegenproben erlaubt; 6 Sperrtests auf Systemrollen alle 403, eigene Rollen weiter aenderbar; Stammdaten-Lesen unveraendert 200; Rechteaenderung sofort 401; Migration und sync-roles dreimal identisch; db:seed auf bestehender DB ohne Wirkung auf die Rollenmatrix; frische Installation mit allen 8 Systemrollen und ohne Hinweise. Beim Deploy: Die Migration beendet alle Sitzungen einmalig. Noetig, weil die Rechte im Token stehen - sonst liefen bis zu 15 Minuten 403er. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,83 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
|
||||
## ✅ Erledigt
|
||||
|
||||
|
||||
- [x] **🔑 Rechtemodell Etappe 1: Katalog begradigt, Selbst-Erhöhung geschlossen** (2026-09-04)
|
||||
- Vorarbeit für die Rollen-Oberfläche (Etappe 2). Eine Checkbox-Liste über
|
||||
einem Katalog, der nicht stimmt, wäre schlimmer als gar keine.
|
||||
- **18 von 50 Rechten bewachten nichts.** `tariffs:*`,
|
||||
`cancellation-periods:*`, `contract-durations:*` und `email-providers:*`
|
||||
standen im Katalog und waren anhakbar – die Routen prüften in Wahrheit
|
||||
`providers:*`, `platforms:*` und `settings:*`. Jetzt gaten die Routen auf
|
||||
ihre eigenen Rechte; eine rein additive Migration vererbt jedes neue Recht
|
||||
an jede Rolle, die bisher das Sammelrecht hatte. **Nachgerechnet: keine
|
||||
Rolle hat etwas verloren.**
|
||||
- **Der Katalog stand dreifach und divergent** – `seed.ts`, `sync-roles.ts`
|
||||
und, abweichend, `factoryReset`. Die dritte Kopie kannte weder `audit:*`
|
||||
noch `gdpr:*` und legte DSGVO, Audit-Betrieb und Gegenbuch gar nicht an:
|
||||
**Nach einem Werksreset konnte niemand mehr eine Auskunft nach Art. 15
|
||||
ausführen**, und aufgefallen wäre es erst, wenn eine Frist läuft. Jetzt
|
||||
eine Quelle: `backend/src/config/rechte-katalog.ts`.
|
||||
- **Nebenbefund im selben Code:** `factoryReset` setzte das Admin-Kennwort
|
||||
fest auf `"admin"` mit bcrypt-Cost 10 – genau das, was `seed.ts` seit
|
||||
Pentest Runde 12 verbietet. Dieselbe Härtung war nur in einer der beiden
|
||||
Kopien angekommen. Jetzt zufällig, Cost 12, einmalig im Log.
|
||||
- **Selbst-Erhöhung geschlossen** (der offene Punkt aus dem Audit-Betrieb-
|
||||
Eintrag vom 26.08.): Neues Recht `roles:manage`, getrennt von `users:*`.
|
||||
Dazu die Teilmengenregel in `rechte.service.ts` – niemand kann ein Recht
|
||||
weitergeben, das er selbst nicht hält. Sie greift auf **allen vier** Wegen:
|
||||
Rolle anlegen, Rolle ändern, Rollen zuweisen und die drei Haken
|
||||
(DSGVO/Developer/Audit-Betrieb). Der Haken-Weg war der wichtigste: Er
|
||||
brauchte nur `users:create` – ein zweites Konto mit „Entwicklerzugriff"
|
||||
anlegen und sich damit anmelden, und die Developer-Rolle trägt *alle*
|
||||
Rechte.
|
||||
- Geprüft wird der **Zuwachs**, nicht der Endzustand: Rechte entziehen bleibt
|
||||
jederzeit erlaubt, sonst könnte ein Admin einen übernommenen
|
||||
Developer-Zugang nicht mehr entschärfen. Und die Prüfung liest die Rechte
|
||||
des Handelnden **frisch aus der Datenbank**, nicht aus dem JWT – der ist
|
||||
bis zu 15 Minuten alt, und ein gerade entzogenes Recht darf nicht im
|
||||
Nachlauf noch weitergereicht werden.
|
||||
- **Systemrollen sind gesperrt** (`Role.isSystem`): Admin ließ sich bisher
|
||||
umbenennen oder leeren – und die versteckten Rollen hängen an ihrem
|
||||
*Namen*. `Role.isHidden` ersetzt die im Frontend hartkodierte Namensliste,
|
||||
in der „Gegenbuch" fehlte.
|
||||
- **Rechteänderung wirkt sofort**: `updateRole`/`deleteRole` melden alle
|
||||
Träger ab. Vorher behielten sie bis zu 15 Minuten die alten Rechte.
|
||||
- **Werksreset und Backup-Restore** verlangen jetzt zusätzlich
|
||||
`roles:manage`. Beide löschen alle Rollen und legen ein frisches
|
||||
`admin@admin.com` an – eine Rolle mit nur `settings:update` hätte damit die
|
||||
ganze Rechtevergabe zurücksetzen und sich anschließend anmelden können.
|
||||
- **Aussperr-Ausweg**: `npx tsx prisma/rolle-zuweisen.ts <email> <Rolle>`.
|
||||
Umgeht die Regel bewusst – wer Shell-Zugang hat, hat ohnehin die Datenbank;
|
||||
ein gestohlener Web-Zugang hat ihn nicht. Schreibt einen Eintrag über die
|
||||
Hash-Kette (nicht roh, sonst risse er eine Lücke) und meldet ab.
|
||||
Die Startwache nennt diesen Weg jetzt im Klartext.
|
||||
- **Rollenpflege ist nicht mehr der leiseste Eingriff**: Sie war der einzige
|
||||
Weg in die Rechtevergabe ohne SecurityEvent, obwohl sie viele Konten auf
|
||||
einmal trifft. Jetzt `PERMISSION_CHANGED` – ebenso für die Haken DSGVO und
|
||||
Entwicklerzugriff, die bisher stumm waren.
|
||||
- **Nachgeprüft** (Dev-Datenbank + frische Wegwerf-Datenbank): 7 Eskalations-
|
||||
wege → alle 403, 2 Gegenproben → erlaubt; 6 Sperrtests auf Systemrollen →
|
||||
alle 403, eigene Rollen weiter änder- und löschbar; Stammdaten-Lesen für
|
||||
Mitarbeiter unverändert 200; Rechteänderung → sofort 401; Migration und
|
||||
`sync-roles` dreimal hintereinander → identischer Bericht; `db:seed` auf
|
||||
bestehender Datenbank → Rollenmatrix unverändert; frische Installation →
|
||||
alle 8 Systemrollen, keine Hinweise.
|
||||
- **Neu**: `prisma/rechte-report.ts` – Bestandsaufnahme zum Vorher/Nachher-
|
||||
Vergleich. Läuft auf beiden Seiten der Migration. Meldet auch Waisen: acht
|
||||
Rechte aus alten Schemata (`*:access`, `settings:create/delete`) liegen
|
||||
noch in der Datenbank und bewachen nichts. Nicht gelöscht, sondern am
|
||||
Lesezugriff gefiltert – ein Filter ist die kleinere Behauptung als ein
|
||||
DELETE.
|
||||
- **Beim Deploy**: Die Migration beendet alle Sitzungen einmalig
|
||||
(`tokenInvalidatedAt`). Nötig, weil die Rechte im Token stehen – sonst
|
||||
liefen bis zu 15 Minuten 403er. Alle müssen sich einmal neu anmelden.
|
||||
- Dateien: `src/config/rechte-katalog.ts`, `src/services/rechte.service.ts`,
|
||||
`src/services/rollen-sync.service.ts`, `prisma/rechte-report.ts`,
|
||||
`prisma/rolle-zuweisen.ts` (alle neu); zwei Migrationen;
|
||||
`user.service.ts`, `user.controller.ts`, `backup.service.ts`,
|
||||
`pflichtrechte.service.ts`, `sanitize.ts`, 8 Route-Dateien, 5 Frontend-Seiten
|
||||
|
||||
- [x] **🔢 R188: Ungültige IDs im Pfad – zentral statt 181-mal** (2026-09-03)
|
||||
- Meldung der Pentesterin: `GET /api/users/:id` gibt bei nicht-numerischer
|
||||
ID **500** statt 400 (`/api/users/permissions` trifft `/:id`). Ihr Patch
|
||||
|
||||
Reference in New Issue
Block a user