From 52b14b8ef7fa262abf004307aa0f399625da2921 Mon Sep 17 00:00:00 2001 From: alientim Date: Mon, 10 Aug 2026 16:01:39 +0200 Subject: [PATCH] =?UTF-8?q?Rechtesystem-L=C3=BCcke=20schlie=C3=9Fen:=20Liv?= =?UTF-8?q?e-Log=20war=20nur=20@login=5Frequired=20statt=20admin-gesch?= =?UTF-8?q?=C3=BCtzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vollständiges Audit aller Routen gegen das Rechtesystem durchgeführt. Die Nav-Sichtbarkeit stufte "Live" (Live-Log) bereits korrekt als admin-only ein (_nav_key_visible: logs_live -> user.is_admin), die zugehörigen Routen /logs und /get_log prüften das aber nie tatsächlich — nur @login_required. Jeder eingeloggte Benutzer, unabhängig von seinen Gruppen-Rechten, konnte das Live-Log damit direkt per URL aufrufen, obwohl die Sidebar es korrekt ausblendete. - /logs: fehlende is_admin-Prüfung ergänzt (Flash + Redirect, konsistent zu activity_log()/users()/groups()/settings()). - /get_log: fehlende is_admin-Prüfung ergänzt (403, da per AJAX abgerufen). Alle anderen neu hinzugekommenen Seiten (Benutzer, Gruppen, Systemein- stellungen, Im-/Export, Änderungslog, Jetzt-prüfen-Trigger) waren bereits korrekt und bewusst exklusiv auf is_admin geprüft — dort gibt es keine sinnvolle granularere Rechte-Stufe unterhalb von Admin (Gruppenverwaltung steuert das Rechtesystem selbst, Systemeinstellungen/Import-Export/manuelle Prüfung wirken global). Zugangsdaten bleiben bewusst an switches.*-Rechte gekoppelt (bestehende, dokumentierte Design-Entscheidung). README um eine Übersicht ergänzt, welche Bereiche bewusst ohne granulare Rechte auskommen und warum. Mit Testbenutzer ohne jede Gruppenzugehörigkeit verifiziert: /logs liefert jetzt 302 (Redirect), /get_log liefert 403 statt beide zuvor 200. Co-Authored-By: Claude Sonnet 5 --- README.md | 11 +++++++++++ srv/poe_manager/app.py | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 68de29f..148db91 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,17 @@ Aktion (Buttons, Toggle-Switches, Formulare) ist an das jeweilige Recht gekoppelt — im Frontend ausgeblendet **und** im Backend serverseitig durchgesetzt, unabhängig vom Frontend. +**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. + 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`, diff --git a/srv/poe_manager/app.py b/srv/poe_manager/app.py index 2a4079d..fe00e4a 100644 --- a/srv/poe_manager/app.py +++ b/srv/poe_manager/app.py @@ -1626,6 +1626,8 @@ def _ssh_terminal_inner(ws): @app.route("/get_log") @login_required def get_log(): + if not current_user.is_admin: + return "Keine Berechtigung.", 403 latest_log = _latest_log_file() if not latest_log: return "Keine Logfiles gefunden." @@ -1645,6 +1647,9 @@ def get_log(): @app.route("/logs") @login_required def logs(): + if not current_user.is_admin: + flash("Nur Admins dürfen das Live-Log einsehen.", "danger") + return redirect(url_for("index")) interval = int(get_setting("interval", 5)) latest_log = _latest_log_file()