R190-01: Rollentausch zerstoerte, wo er ablehnte
Befund der Pentesterin. updateUser machte deleteMany + createMany ohne Transaktion und ohne skipDuplicates. Eine doppelte roleId ([4,4]) oder eine erfundene ([99999]) kam durch die Teilmengenregel - sie bringt keine Rechte mit, faellt dort also nicht auf - und schlug erst beim Schreiben fehl, nach dem Loeschen. Antwort HTTP 400, Konto danach ohne jede Rolle. Die Antwort log ueber sich selbst: "400" liest sich als abgelehnt, nichts passiert. Zerstoert wurde trotzdem. Damit war die Letzter-Admin-Sperre umgangen, ohne sie anzugreifen: Sie prueft die Absicht (die Admin-Rolle steht im Body, also "bleibt Admin"), der Schreibvorgang scheiterte danach. Aussperrung, Rueckweg nur per CLI. Auch versehentlich ausloesbar durch doppelte IDs aus dem Frontend. Es war meine eigene Haertung, die ich nicht mitgenommen habe: updateRole hatte Dedup und skipDuplicates seit Etappe 1, das Geschwister updateUser/createUser nicht. Genau das Muster, das in derselben Runde dreimal aufgeraeumt wurde - eine Absicherung, die nur in einer von zwei Kopien ankommt. normalisiereRollenIds() prueft jetzt vor jeder Entscheidung und jedem Schreibvorgang gegen die Datenbank, dedupliziert, und der Tausch laeuft in einer Transaktion. Die Letzter-Admin-Sperre arbeitet damit auf einer Liste, die auch einloesbar ist. Nebenbefund beim Nachstellen: Die rohe Prisma-Fehlermeldung ging wortwoertlich an den Client, inklusive Dateipfad des Servers. Wird jetzt protokolliert statt ausgeliefert. Nachgestellt: [22,22] -> 200 mit erhaltener Rolle; [99999] und [22,99999] -> 400 mit Rollen unveraendert; [-1] -> 400; [22,23] -> 200 korrekt gesetzt. createUser ebenso, abgelehnte Anlagen hinterlassen kein Konto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,42 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
## ✅ Erledigt
|
||||
|
||||
|
||||
|
||||
- [x] **🧨 R190-01: Rollentausch zerstörte, wo er ablehnte** (2026-09-09)
|
||||
- Befund der Pentesterin: `updateUser` machte `deleteMany` + `createMany`
|
||||
**ohne Transaktion und ohne `skipDuplicates`**. Eine doppelte roleId
|
||||
(`[4,4]`) oder eine erfundene (`[99999]`) kam durch die Teilmengenregel –
|
||||
sie bringt ja keine Rechte mit – und schlug erst beim Schreiben fehl,
|
||||
**nach** dem Löschen. Antwort: HTTP 400. Konto danach: **keine Rolle**.
|
||||
- Die Antwort log über sich selbst: „400" liest sich als *abgelehnt, nichts
|
||||
passiert*. Zerstört wurde trotzdem.
|
||||
- **Die Letzter-Admin-Sperre wurde damit umgangen**, ohne sie anzugreifen:
|
||||
Sie prüft die *Absicht* (die Admin-Rolle steht im Body → „bleibt Admin"),
|
||||
der Schreibvorgang scheiterte danach. Aussperrung, Rückweg nur per CLI.
|
||||
Auch versehentlich auslösbar durch doppelte IDs aus dem Frontend.
|
||||
- **Es war meine Härtung, die ich nicht mitgenommen habe.** `updateRole`
|
||||
hatte Dedup und `skipDuplicates` seit Etappe 1 – das Geschwister
|
||||
`updateUser`/`createUser` nicht. Genau das Muster, das ich in derselben
|
||||
Runde dreimal angeprangert habe: eine Absicherung, die nur in einer von
|
||||
zwei Kopien ankommt.
|
||||
- Fix: `normalisiereRollenIds()` prüft **vor** jeder Entscheidung und jedem
|
||||
Schreibvorgang gegen die Datenbank (unbekannte ID → 400 mit Klartext),
|
||||
dedupliziert, und der Tausch läuft in `$transaction` mit
|
||||
`skipDuplicates`. Die Letzter-Admin-Sperre arbeitet damit auf einer Liste,
|
||||
die auch einlösbar ist.
|
||||
- **Nebenbefund beim Nachstellen**: Die rohe Prisma-Fehlermeldung ging
|
||||
wortwörtlich an den Client – inklusive Dateipfad des Servers. Wird jetzt
|
||||
protokolliert statt ausgeliefert.
|
||||
- Nachgestellt und gegengeprüft: `[22,22]` → 200, Rolle erhalten;
|
||||
`[99999]` und `[22,99999]` → 400, Rollen **unverändert**; `[-1]` → 400;
|
||||
`[22,23]` → 200 korrekt gesetzt. `createUser` ebenso, abgelehnte Anlagen
|
||||
hinterlassen kein Konto.
|
||||
- Bestätigt dicht gemeldet: Restore/Factory-Reset ohne `roles:manage` → 403,
|
||||
roleIds+Haken gleichzeitig → 403 ohne Teil-Write, alle vier Vergabewege →
|
||||
403, isSystem-Sperre inkl. `" aDmIn "` → 403.
|
||||
- Dateien: `src/services/rechte.service.ts`, `src/services/user.service.ts`,
|
||||
`src/controllers/user.controller.ts`
|
||||
|
||||
- [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.
|
||||
|
||||
Reference in New Issue
Block a user