Rechtesystem: Löschen überall in Ändern falten, Gruppen-Tabelle transponieren

- "Ändern" deckt jetzt ausnahmslos überall Löschen mit ab (auch bei
  Clients) — es gibt kein eigenes *.delete-Recht mehr. Betroffen:
  devices/switches/credentials/users/groups.
- "D" in der Rechtetabelle bedeutet ausschließlich Dashboard-Neustart
  (devices.restart, PoE-Neustart über das Dashboard-Popup) und existiert
  nur bei Clients. Automatische Neustarts bei Ausfall (poe.sh) bleiben
  davon komplett unabhängig.
- Gruppen-Rechtetabelle transponiert: Kategorien jetzt als Zeilen,
  Rechte R(ead)/W(rite)/E(dit)/D(ashboard-Neustart) nebeneinander als
  Spalten statt untereinander. Deckt sich besser mit dem Legenden-Format
  und braucht weniger vertikalen Platz.
- Neue, einmalig laufende Migration (_migrated_permission_delete_fold_v2)
  überführt bestehende *.delete-Grants verlustfrei in *.edit, bevor die
  veralteten Rechte-Zeilen entfernt werden.
- Routen (delete_device/delete_switch/delete_credential/delete_user/
  delete_group) und die zugehörigen can_delete-Template-Variablen prüfen
  jetzt einheitlich *.edit statt *.delete.
- README-Rechtesystem-Abschnitt und Legende an das neue Tabellen-Layout
  angepasst.
- Live getestet: Kill-Switch, alle 5 Delete-Foldings (mit/ohne Recht),
  devices.restart-Gating unabhängig von devices.edit, Eskalationsschutz
  (Admin-Löschschutz, Systemgruppen-Schutz) — alles bestätigt korrekt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 19:49:28 +02:00
co-authored by Claude Sonnet 5
parent 4b7403f476
commit 813b17d002
8 changed files with 291 additions and 136 deletions
+39 -28
View File
@@ -109,34 +109,43 @@ Ein Benutzer kann mehreren Gruppen angehören, die Rechte addieren sich
### Zweistufiger Rechtebaum
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei Sidebar-Gruppen:
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei Sidebar-Gruppen.
Pro Bereich gibt es eine kompakte Tabelle: Zeilen = der Bereich selbst
„(gesamt)“ + je ein Unterpunkt, Spalten = die Rechte **R**(ead/Lesen),
**W**(rite/Schreiben, Anlegen), **E**(dit/Ändern) nebeneinander. **„Ändern“
deckt Löschen überall mit ab** — es gibt bewusst kein eigenes
Löschen-Recht. Eine vierte Spalte **D** existiert nur bei Clients und
bedeutet dort **nicht** Löschen, sondern *PoE-Neustart über das
Dashboard-Popup auslösen* (automatische Neustarts bei Ausfall über
`poe.sh` sind davon komplett unabhängig und laufen immer). Für Zeilen/Spalten
ohne passendes Recht (z.B. „W“ beim Bereich selbst, oder „D“ bei
Switche/Zugangsdaten/Benutzer/Gruppen) wird eine ausgegraute, nicht
anklickbare Checkbox angezeigt statt die Zelle wegzulassen:
```
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)
Geräte (gesamt) R
├─ Clients R · W · E · D (D = PoE-Neustart über Dashboard)
├─ Switche R · W · E
└─ Zugangsdaten R · W · E
Logs (gesamt) R
├─ Live R
└─ Änderungen R
Einstellungen (gesamt) R
├─ Benutzer R · W · E
├─ Gruppen R · W · E
├─ Systemeinstellungen R · E
└─ Im-/Export R (Export) · E (Import)
```
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`).
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.
`logs_group.view` / `settings_group.view`, die „(gesamt)“-Zeile) 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`). Auf der Gruppen-Seite sind die Unterpunkt-Zeilen einer Tabelle per
JavaScript so lange gesperrt (ausgegraut, nicht anklickbar), bis die
„(gesamt)“-Zeile angehakt ist.
### Systemgruppen
@@ -150,8 +159,8 @@ beiden frei verwaltbar:
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.
Alle anderen Gruppen sind vom jeweiligen Rechteinhaber (`groups.edit`, das
auch Anlegen und Löschen von Gruppen umfasst) frei konfigurierbar.
### Eskalationsschutz
@@ -174,10 +183,12 @@ Formulare) ist an das jeweilige Recht gekoppelt — im Frontend ausgeblendet
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`,
(`_ensure_schema()` in `app.py`): `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-Rechte für bereits vergebene Unterrechte, sowie (spätere Revision)
sämtliche `*.delete`-Rechte → `*.edit` (Löschen wurde nachträglich überall in
Ändern gefaltet). Jede dieser Migrationen läuft **nur einmalig** über einen
eigenen Guard in der `settings`-Tabelle, damit z.B. ein bewusst deaktiviertes
Bereichs-Recht nicht bei jedem Neustart automatisch wieder gesetzt wird.
## Zugangsdaten (wiederverwendbare SSH-Logins)