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>
42 lines
2.5 KiB
TypeScript
42 lines
2.5 KiB
TypeScript
import { Router } from 'express';
|
||
import * as userController from '../controllers/user.controller.js';
|
||
import { authenticate, requirePermission } from '../middleware/auth.js';
|
||
import { staffPasswordReAuthLimiter } from '../middleware/rateLimit.js';
|
||
|
||
const router = Router();
|
||
|
||
// Users (Admin only)
|
||
router.get('/', authenticate, requirePermission('users:read'), userController.getUsers);
|
||
router.post('/', authenticate, requirePermission('users:create'), userController.createUser);
|
||
router.get('/:id', authenticate, requirePermission('users:read'), userController.getUser);
|
||
router.put('/:id', authenticate, requirePermission('users:update'), userController.updateUser);
|
||
router.delete('/:id', authenticate, requirePermission('users:delete'), userController.deleteUser);
|
||
// Passwort-Reset durch Admin – dedizierter Endpoint (Pentest Runde 12).
|
||
// 47.3 verlangt Re-Auth (currentPassword), 48.3 wirft einen Rate-Limit
|
||
// davor, damit ein gestohlener JWT das Admin-Passwort nicht brute-forcen kann.
|
||
router.post('/:id/password', staffPasswordReAuthLimiter, authenticate, requirePermission('users:update'), userController.setUserPassword);
|
||
|
||
// Rollen und Rechte.
|
||
//
|
||
// Die Schreibwege haengen an `roles:manage`, nicht mehr an `users:*`:
|
||
// "Konten anlegen" und "festlegen, was ein Konto darf" sind zwei
|
||
// verschiedene Befugnisse. Wer beides hatte, konnte sich eine Rolle mit
|
||
// `developer:access` bauen und zuweisen - die Rollenverwaltung war damit
|
||
// faktisch eine Rechteerhoehung mit Zwischenschritt. Die zweite Haelfte des
|
||
// Riegels ist die Teilmengenregel in rechte.service.ts.
|
||
//
|
||
// Die LESEwege bleiben zusaetzlich mit `users:read` erreichbar: Das
|
||
// Benutzerformular braucht die Rollenliste zum Zuweisen, ohne dass der
|
||
// Bearbeiter Rollen pflegen koennen muss. `requirePermission` ist
|
||
// ODER-verknuepft, das traegt ohne Zusatzcode.
|
||
router.get('/roles/list', authenticate, requirePermission('roles:manage', 'users:read'), userController.getRoles);
|
||
router.post('/roles', authenticate, requirePermission('roles:manage'), userController.createRole);
|
||
router.get('/roles/:id', authenticate, requirePermission('roles:manage', 'users:read'), userController.getRole);
|
||
router.put('/roles/:id', authenticate, requirePermission('roles:manage'), userController.updateRole);
|
||
router.delete('/roles/:id', authenticate, requirePermission('roles:manage'), userController.deleteRole);
|
||
|
||
// Permissions
|
||
router.get('/permissions/list', authenticate, requirePermission('roles:manage', 'users:read'), userController.getPermissions);
|
||
|
||
export default router;
|