Vollständig granulares, an der Navbar gespiegeltes Rechtesystem

Kompletter Umbau des Permission-Systems weg von groben is_admin-Gates hin zu
einem zweistufigen, an die Sidebar-Struktur gespiegelten Rechtebaum:

  Geräte (devices_group.view)
   ├─ Clients      Lesen/Schreiben/Ändern/Löschen (+ PoE-Neustart)
   ├─ Switche      Lesen/Schreiben/Ändern/Löschen
   └─ Zugangsdaten Lesen/Schreiben/Ändern/Löschen (jetzt eigene Rechte,
                    vorher an switches.* gekoppelt)
  Logs (logs_group.view)
   ├─ Live         Lesen
   └─ Änderungen   Lesen
  Einstellungen (settings_group.view)
   ├─ Benutzer            Lesen/Schreiben/Ändern/Löschen
   ├─ Gruppen             Lesen/Schreiben/Ändern/Löschen
   ├─ Systemeinstellungen Lesen/Ändern
   └─ Im-/Export          Lesen (Export)/Ändern (Import)

- User.has_permission() ist jetzt hierarchisch: das "Bereich anzeigen"-Recht
  einer Top-Level-Gruppe wirkt als Kill-Switch für alle Kind-Rechte
  darunter, auch wenn ein Kind-Recht einzeln noch gesetzt ist. Mit
  Testgruppe verifiziert (devices.view ohne devices_group.view -> /devices
  liefert 302, "Geräte" verschwindet komplett aus der Sidebar; nach
  Zurücksetzen sofort wieder 200).
- devices.toggle entfällt, ist jetzt Teil von devices.edit (Ändern).
- Neue eigenständige credentials.*-Rechte statt Kopplung an switches.*.
- Benutzer- und Gruppenverwaltung sind jetzt ebenfalls granular/delegierbar
  (users.*/groups.*) statt fest is_admin-exklusiv — dafür neue,
  fest einprogrammierte Eskalationsschranken: Admin-Konten anlegen/ändern/
  löschen sowie Admin-Zuweisung bleiben unabhängig von delegierten Rechten
  echten Admins vorbehalten (mit Testgruppe verifiziert: Anlegen als Admin,
  Bearbeiten/Löschen bestehender Admin-Konten und Zuweisen zu "admin"
  wurden alle korrekt blockiert, normale Benutzerverwaltung funktioniert).
- "Admin" (virtuell) und "Benutzer" (Standardgruppe, neues is_system-Flag)
  sind jetzt echte Systemgruppen: weder umbenennbar noch in ihren Rechten
  änderbar, auch nicht durch Admins über die UI — Mitgliedschaft bleibt frei
  verwaltbar. Mit direktem POST verifiziert: Umbenennen/Löschen/Rechte-Reset
  von "Benutzer" werden blockiert, Mitgliederverwaltung funktioniert weiter.
- groups.html zeigt den Baum jetzt als 3 Zeilen (Geräte/Logs/Einstellungen)
  mit eingerückten Unterpunkten statt einer flachen Liste von Kategorien mit
  wiederholtem Bereichsnamen im Label.
- Migration in _ensure_schema() (Altrechte übertragen, neue Bereichs-Rechte
  für bestehende Gruppen nachtragen) läuft jetzt über einen Einmal-Guard in
  der settings-Tabelle — lief anfangs bei jedem Neustart erneut und hat
  damit den Kill-Switch-Mechanismus untergraben (ein deaktiviertes
  Bereichs-Recht wäre bei jedem Neustart automatisch wieder gesetzt worden,
  solange irgendein Kind-Recht noch aktiv war); im Test entdeckt und behoben.
- create_db.py synchronisiert (is_system-Spalte, neuer Rechtesatz für
  Frischinstallationen).
- README: Rechtesystem-Abschnitt komplett neu beschrieben (Baum, Kill-Switch,
  Systemgruppen, Eskalationsschutz).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 18:28:42 +02:00
co-authored by Claude Sonnet 5
parent 52b14b8ef7
commit 4b7403f476
11 changed files with 580 additions and 173 deletions
+72 -54
View File
@@ -101,66 +101,84 @@ Die App ermöglicht:
## Rechtesystem (Gruppen & Berechtigungen)
Admins dürfen wie bisher alles, inklusive Benutzer-/Gruppen-/
Settingsverwaltung — das bleibt exklusiv Admins vorbehalten. Zusätzlich gibt
es **Gruppen**: eine Gruppe bündelt einzelne Verwaltungsrechte, die dann
normalen Benutzern zugewiesen werden können, ohne sie zu Admins zu machen.
Ein Benutzer kann nur einer Gruppe/Rolle gleichzeitig zugeordnet sein
(über den Button „Gruppe zuweisen“, analog zur Switch-Zuordnung bei
Devices) — technisch sind mehrere Gruppen pro Benutzer möglich (Rechte
würden sich addieren), die UI bildet aber bewusst nur eine 1:1-Zuordnung ab.
Admins dürfen immer alles. Zusätzlich gibt es **Gruppen** mit einem
**vollständig granularen, an der Navbar gespiegelten Rechtebaum** — jeder
Bereich der App (nicht nur Devices/Switches) lässt sich einzeln freischalten.
Ein Benutzer kann mehreren Gruppen angehören, die Rechte addieren sich
(Vereinigung, nicht Schnittmenge).
Auf der **Gruppen**-Seite (nur für Admins) werden zur Übersicht immer auch
die beiden Systemrollen mit aufgeführt:
- **Admin** — eine feste, nicht editierbare Zeile mit allen Rechten;
Mitgliedschaft wird direkt hier verwaltet (Button „Mitglieder verwalten“),
intern über den `is_admin`-Schalter der Benutzer. Mindestens ein Admin
muss immer bestehen bleiben (serverseitig erzwungen).
- **Benutzer** — die **Standardgruppe**, mit der alle Ansichtsrechte
(`devices.view`, `switches.view`) vorbelegt sind. Jeder neu angelegte,
nicht-admin Benutzer wird ihr automatisch zugeordnet; sie kann nicht
gelöscht werden.
### Zweistufiger Rechtebaum
Jede Gruppe lässt sich über „Rechte anzeigen/bearbeiten“ aufklappen (wie ein
Akkordeon) und zeigt dort die volle Checkbox-Liste; Mitglieder werden über
einen eigenen Button/Modal verwaltet (nur die Anzahl steht in der Tabelle).
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei Sidebar-Gruppen:
Verfügbare Rechte:
```
Geräte (devices_group.view — Bereich an/aus)
├─ Clients Lesen · Schreiben (Anlegen) · Ändern (inkl. Akt./Deakt.) · Löschen · PoE-Neustart
├─ Switche Lesen · Schreiben · Ändern · Löschen
└─ Zugangsdaten Lesen · Schreiben · Ändern · Löschen
Logs (logs_group.view — Bereich an/aus)
├─ Live Lesen
└─ Änderungen Lesen
Einstellungen (settings_group.view — Bereich an/aus)
├─ Benutzer Lesen · Schreiben · Ändern · Löschen
├─ Gruppen Lesen · Schreiben · Ändern · Löschen
├─ Systemeinstellungen Lesen · Ändern
└─ Im-/Export Lesen (Export) · Ändern (Import)
```
| Bereich | Recht | Bedeutung |
|----------|---------------------|-----------------------------------------------|
| Devices | `devices.view` | Devices-Seite ansehen |
| Devices | `devices.toggle` | Geräte aktivieren/deaktivieren |
| Devices | `devices.create` | Geräte anlegen |
| Devices | `devices.edit` | Geräte bearbeiten (inkl. Switch-Zuordnung) |
| Devices | `devices.delete` | Geräte löschen |
| Devices | `devices.restart` | PoE-Neustart/Aktivieren über das Dashboard |
| Switches | `switches.view` | Switches- und Zugangsdaten-Seite ansehen |
| Switches | `switches.create` | Switche und Zugangsdaten anlegen |
| Switches | `switches.edit` | Switche und Zugangsdaten bearbeiten |
| Switches | `switches.delete` | Switche und Zugangsdaten löschen |
Das jeweilige „Bereich an/aus“-Recht (`devices_group.view` /
`logs_group.view` / `settings_group.view`) wirkt als **Kill-Switch**: ist es
für eine Gruppe nicht gesetzt, greift kein einziges Recht darunter mehr —
selbst wenn z.B. `devices.view` einzeln noch angehakt ist. So lässt sich ein
ganzer Bereich mit einem Klick sperren, ohne jedes Unterrecht einzeln
zurücknehmen zu müssen (`User.has_permission()` in `app.py`).
Sowohl das Anzeigen der Devices-/Switches-Seiten als auch jede einzelne
Aktion (Buttons, Toggle-Switches, Formulare) ist an das jeweilige Recht
gekoppelt — im Frontend ausgeblendet **und** im Backend serverseitig
durchgesetzt, unabhängig vom Frontend.
Auf der **Gruppen**-Seite wird der Baum als Akkordeon pro Gruppe angezeigt:
eine Zeile pro Top-Level-Bereich mit eigenem Kästchen, darunter eingerückt
die Unterpunkte mit ihren Einzelrechten — ohne den Bereichsnamen in jedem
Unterpunkt zu wiederholen.
**Bewusst ohne granulare Rechte, exklusiv Admins vorbehalten:** Benutzer,
Gruppen, Systemeinstellungen (Prüfintervall), Im-/Export, Änderungslog,
Live-Log und der manuelle „Jetzt prüfen“-Trigger. Für diese Bereiche gibt es
keine sinnvolle Teilmenge unterhalb von „Admin“ — Gruppenverwaltung steuert
direkt das Rechtesystem selbst (Delegation wäre eine Privilegien-Eskalation),
Systemeinstellungen/Import-Export/manuelle Prüfung wirken sich global aus,
und das Änderungslog protokolliert alle Benutzer systemweit. Live-Log und
`/get_log` waren zwischenzeitlich nur über `@login_required` statt echter
Admin-Prüfung abgesichert (die Sidebar blendete den Punkt zwar korrekt aus,
die Route selbst nicht) — inzwischen serverseitig nachgezogen.
### Systemgruppen
Datenmodell: `groups` (inkl. `is_default`-Flag), `group_permissions`
(Gruppe → Recht), `user_groups` (Benutzer → Gruppe). Bestehende Datenbanken
werden beim App-Start automatisch migriert (`_ensure_schema()` in `app.py`,
inkl. Nachrüsten der Standardgruppe und Zuordnung bestehender Benutzer ohne
Gruppe) — kein manuelles Migrations-Skript nötig.
Zwei Gruppen sind fest und **weder umbenennbar noch in ihren Rechten
änderbar** (auch nicht durch Admins über die UI) — Mitgliedschaft bleibt bei
beiden frei verwaltbar:
- **Admin** — virtuell (kein echter `groups`-Datensatz), intern über den
`is_admin`-Schalter der Benutzer gesteuert, immer alle Rechte. Mindestens
ein Admin muss bestehen bleiben (serverseitig erzwungen).
- **Benutzer** — die Standardgruppe (`is_system`-Flag), der jeder neu
angelegte Nicht-Admin automatisch zugeordnet wird. Fester Rechtesatz:
Geräte-Bereich + Clients/Switche lesen, Logs-Bereich + Live-Log lesen.
Alle anderen Gruppen sind vom jeweiligen Rechteinhaber (`groups.edit` bzw.
`groups.create`/`groups.delete`) frei konfigurierbar.
### Eskalationsschutz
Da jetzt auch **Benutzer-** und **Gruppenverwaltung** delegierbar sind (z.B.
eine Gruppe mit nur `users.edit`, ohne Admin zu sein), gelten zusätzlich zu
den granularen Rechten diese fest einprogrammierten Schranken, unabhängig
davon, was eine Gruppe an Rechten hat:
- Ein Benutzer als Admin anlegen/dazu machen (`group_id=admin` bei
Anlegen/Zuweisen) bleibt echten Admins vorbehalten.
- Ein bestehendes Admin-Konto bearbeiten, löschen oder ihm die Gruppe ändern
bleibt echten Admins vorbehalten.
- Wer Admin ist, wird ausschließlich über „Admin-Mitglieder verwalten“ auf
der Gruppen-Seite gesteuert — bleibt admin-exklusiv, unabhängig von
`groups.edit`.
Sowohl das Anzeigen jeder Seite als auch jede einzelne Aktion (Buttons,
Formulare) ist an das jeweilige Recht gekoppelt — im Frontend ausgeblendet
**und** im Backend serverseitig durchgesetzt, unabhängig vom Frontend.
Datenmodell: `groups` (inkl. `is_default`- und `is_system`-Flag),
`group_permissions` (Gruppe → Recht), `user_groups` (Benutzer → Gruppe).
Bestehende Datenbanken werden beim App-Start automatisch migriert
(`_ensure_schema()` in `app.py`, u.a. `devices.toggle``devices.edit`,
`switches.*` → gespiegelte `credentials.*`, nachträgliches Setzen der neuen
Bereichs-Rechte für bereits vergebene Unterrechte) — läuft **nur einmalig**
über einen Guard in der `settings`-Tabelle, damit ein bewusst deaktiviertes
Bereichs-Recht nicht bei jedem Neustart automatisch wieder gesetzt wird.
## Zugangsdaten (wiederverwendbare SSH-Logins)
@@ -179,7 +197,7 @@ am Switch) werden beim ersten Start automatisch migriert.
Jede Anlage, Bearbeitung, Löschung sowie jedes Aktivieren/Deaktivieren von
Geräten, Switchen, Zugangsdaten, Benutzern und Gruppen wird in der Tabelle
`audit_log` protokolliert (Zeitpunkt, Benutzer, Aktion, Ziel, Details) —
einsehbar unter **Logs → Änderungslog** (nur für Admins). Zusätzlich merken
einsehbar unter **Logs → Änderungen** (Recht `logs_activity.view`). Zusätzlich merken
sich Geräte und Switche direkt am Datensatz (`last_modified_by`,
`last_modified_at`), wer sie zuletzt geändert hat, damit man das nicht erst
im Log nachschlagen muss.