Vorher: resolve_approval_request pruefte nur Mandant + pending — NICHT
Ablaufdatum, NICHT den vorgesehenen Genehmiger, und ueberschrieb
approver_id mit dem tatsaechlichen Entscheider (Zuordnung verloren).
Astra-Repro: Eine abgelaufene Anfrage konnte von einem anderen
Entscheider genehmigt werden; konkurrierende Entscheidungen waren
moeglich.
Fix:
- Migration 0146: neue Spalte resolved_by (Zuordnung vs. Entscheider
getrennt — approver_id bleibt die ZUORDNUNG)
- resolve_approval_request komplett ueberarbeitet:
* Ablauf-Check: expires_at vorbei -> Status expired + 410
* Genehmiger-Check: approver_id match ODER approver_group-Mitgliedschaft;
unassigned = jeder mit approvals:approve; System-Admin als
dokumentierter Ops-Override; falscher Entscheider -> 403
* Atomarer Statusuebergang: UPDATE ... WHERE status=pending —
konkurrierende Entscheidung -> 409
* approver_id wird NIE ueberschrieben; resolved_by dokumentiert den
Entscheider
- ApprovalDecisionError mit HTTP-Status-Codes; approve/reject-Routen
fangen sie sauber ab (404/409/410/403 statt Flat-404)
- ApprovalResponse + Mapper um resolved_by ergaenzt
Abnahme (Astra): Falscher Entscheider, abgelaufene Anfrage und doppelte
Entscheidung werden abgewiesen — erfuellt (6 Tests).
Hinweis: workflows.py approve/reject-Aufrufer waren bereits kaputt
(F12, S2-Welle: approval[id] auf ORM-Objekt) und werden dort gefixt.
Tests: test_s1_security_guards.py 18/18 (6 neue F11-Tests). ruff clean.
Damit ist S1 — ALLE 11 Sicherheits-Findings der Astra-Welle 1 gefixt.
Vorher: POST /api/v1/backups/{id}/restore war ueber automation:admin
eines Mandanten erreichbar — der Restore bearbeitet aber die GESAMTE
geteilte Datenbank ohne Mandantenfilter. Ein Tenant-Admin haette den
Zustand aller Mandanten ueberschreiben koennen.
Fix: Restore-Route auf require_admin umgestellt (echter System-Admin:
is_system_admin oder *:* via RBAC). Listen/Erstellen/Loeschen von
Backups bleibt mandantenbezogen auf automation:admin.
Abnahme (Astra): Ein Tenant-Admin kann keinen Gesamtrestore ausloesen —
erfuellt (Route-Introspektions-Tests pinnen die Verdrahtung).
Tests: test_s1_security_guards.py 12/12 (2 neue F23-Tests: restore nutzt
require_admin, restore nutzt NICHT require_permission).
Vorher: require_permission machte bei passendem Token-Scope ein
early-return — die User-Rechte wurden NIE geprueft. Ein Token mit
mail:write erlaubte mail:write selbst dann, wenn der Benutzer die
Berechtigung nie hatte oder sie entzogen bekam (Astra-Repro isoliert
bestaetigt).
Fix: Nach bestandenem Scope-Check in den normalen User-Rechte-Check
fallen. Effektives Recht = User-Rechte UND Token-Scope. Rechteentzug
wirkt sofort auf bestehende Tokens. System-Admin-Bypass unveraendert.
Tests: tests/test_s1_security_guards.py 7/7 (neue Suite):
Scope-ohne-User-Recht 403, beide-present pass, Scope-fehlt-User-hat 403
insufficient_scope, Deny-Revocation wirkt, Session-Pfad unveraendert,
*:*-Scope umgeht nicht, Admin-Bypass bleibt. ruff clean.