35 KiB
35 KiB
SysAdmin-Authority-Modell konsolidieren: Flag + Rolle → zwei klar getrennte Flags
Beschreibung und Kontext
Das PORTA-Berechtigungsmodell kennt heute zwei parallel gepflegte SysAdmin-Marker, die sich überschneiden:
-
User.isSysAdminFLAG (Spalte inUser-Tabelle)- Wirkt als RBAC-Engine-Bypass in
rbac.py:getUserPermissions(Zeile 88) → umgeht alle AccessRules und gibt vollen Zugriff. - Geprüft via
ctx.isSysAdmin,requireSysAdmin, direktesgetattr(user, 'isSysAdmin', False). - Verwendet in Routen für System-Operationen (Tokens, Logs, DB-Health, i18n-Master, Teams-SystemBot, global-scoped Files/Sources).
- Wirkt als RBAC-Engine-Bypass in
-
sysadminROLE (UserMandateRole im Root-Mandant)- Gedacht als normale Rolle im Root-Mandant.
- Gibt implizit mandanten-übergreifende Admin-Rechte, weil die zugehörigen AccessRules auf User-/Mandate-/RBAC-Verwaltung eingestellt sind.
- Geprüft via
ctx.hasSysAdminRole,_hasSysAdminRole(userId),requireSysAdminRole. - Verwendet in Routen für cross-mandate Admin-Operationen (User-Management, Mandate-Management, RBAC-Rules, i18n, Subscription).
Warum der aktuelle Zustand problematisch ist
- Architektonisch inkonsistent: Alle anderen Rollen sind mandanten-scoped.
Nur
sysadminist eine Rolle im Root-Mandant, die Autorität über andere Mandanten verleiht. Das durchbricht die Mandanten-Isolation konzeptionell. - Doppelte Quelle der Wahrheit: Flag und Rolle können auseinander driften. Siehe konkreter Security-Bug (Issue 4): Admin entfernt Flag über UI, Rolle bleibt → User behält alle Cross-Mandate-Rechte.
- Verwirrende Semantik: „SysAdmin" kann zwei unterschiedliche Dinge bedeuten — je nachdem, wer fragt.
- Fehlende Differenzierung: Es ist heute nicht möglich, jemanden
auszustatten mit
- „darf die Plattform betreiben" (Logs, Tokens, DB-Health) aber nicht mit
- „darf über alle Mandanten User und Rollen verwalten". Oder umgekehrt. Beides hängt am selben Flag bzw. an derselben Rolle.
Risiko bei Nicht-Umsetzung:
- SECURITY: Ex-SysAdmin mit entferntem Flag behält Cross-Mandate-Admin-Rechte (aktiver Bug).
- Aufbau technischer Schuld: jede neue Admin-Route muss zwischen Flag und Rolle wählen — Entwickler wählen falsch.
- Audit/Compliance: schwer nachzuweisen, wer welche Autorität warum hat.
Fokus und kritische Details
- Drei Kategorien von Autorität sind heute konzeptionell vermischt:
- Infrastruktur-Autorität (Kategorie A): Logs, Tokens, DB-Health, Registry-Sync, i18n-Master, Billing-Webhooks, Teams-SystemBot.
- Plattform-Governance-Autorität (Kategorie B–E): Cross-Mandate User-Mgmt, Mandate-Mgmt, RBAC-Catalog, Feature-Registry, User-Access-Overview, Mandanten-Deblockierung.
- Mandanten-Autorität: alles innerhalb eines spezifischen Mandanten (inkl. Root-Mandant) — hier sind Rollen das richtige Konzept.
- Die
sysadmin-Rolle existiert heute nur im Root-Mandant (interfaceBootstrap.py:_initSysAdminRole,isSystemRole=False). Ihre AccessRules regeln aber Autorität über alle Mandanten. - Die RBAC-Engine hat für
isSysAdmin=Trueeinen harten Bypass (rbac.py:88). Das ist kein Missbrauch, sondern eine bewusste Design- Entscheidung — so funktionieren „system-ops" unabhängig vom RBAC-Schema. - Bestehende DB-Stände können inkonsistent sein → Einmalige Migration nötig.
Ziel und Nicht-Ziele
Ziel (Empfehlung, siehe Entscheidungen unten)
- Zwei klar getrennte, einzeln zuteilbare Berechtigungs-Dimensionen:
User.isSysAdmin(umbenannt von Bedeutung her → „Infrastructure/System Operator"): RBAC-Bypass für Infrastruktur-Operationen, keine implizite Autorität über Mandanten-Daten oder -Verwaltung.User.isPlatformAdmin(neu): Cross-Mandate-Governance ohne RBAC- Bypass. Prüfung in den jeweiligen Admin-Routen explizit.
- Eliminierung der
sysadmin-Rolle aus dem Root-Mandant. Die Autorität, die bisher an der Rolle hing, wird anisPlatformAdmingebunden. - Beide Flags einzeln vergebbar, einzeln sichtbar im UI, einzeln auditierbar.
- Beispiele für eine klare Trennung:
- „Operations-Engineer" →
isSysAdmin=true,isPlatformAdmin=false(kann Logs und DB prüfen, aber keine User/Mandate verwalten). - „Customer-Success-Admin" →
isSysAdmin=false,isPlatformAdmin=true(kann Mandanten deblockieren, User-Access-Overview sehen, aber nicht in Server-Logs schauen). - „Plattform-Super-Admin" → beide auf
true.
- „Operations-Engineer" →
Explizit NICHT
- Einführung einer neuen Rollen-Tabelle (
PlatformRole) — Overkill für zwei heute bekannte Autoritäts-Achsen; bei Bedarf später zusätzliche Flags. - Änderung der Mandanten-scoped Rollen (admin/user/viewer etc.)
- Änderung der RBAC-Engine-Semantik —
isSysAdmin-Bypass bleibt; er beschränkt sich jetzt aber eindeutig auf Infrastruktur-Zwecke. - Wiederverwendung des Begriffs „sysadmin" in der Namensgebung für Cross- Mandate-Governance (explizit vermeiden — eindeutige Namen, keine Ambiguität).
Architektur-Analyse: verworfene Alternativen
Alternative A (verworfen): Rolle behalten, Flag eliminieren
- Nur
sysadmin-Rolle, Flag weg. - Verworfen, weil:
- Rolle bleibt konzeptionell im Mandanten-Kontext gefangen.
- RBAC-Bypass für Infrastruktur-Operationen wäre schwerer umzusetzen (müsste an Rolle hängen → Root-Mandant-Query bei jedem Request).
- Infrastruktur-Routen benötigen oft keinen Mandanten-Kontext.
Alternative B (verworfen): Flag behalten, Rolle eliminieren, eine Flag-Dimension
- Nur
isSysAdminflag, Rolle weg. Eine einzige Dimension. - Verworfen, weil:
- Keine Trennung „pure Infrastruktur" vs. „Cross-Mandate-Governance".
- Wer Logs lesen darf, kann automatisch auch Mandanten deblockieren — das ist zu grob und macht Auditierbarkeit schwierig.
- Vom User explizit gewünschte Differenzierung geht verloren („sysadmin flag sauber = keine Daten-Berechtigung, nur Admin-Zwecke").
Alternative C (verworfen): Separate PlatformRole-Tabelle
- Neues Schema
UserPlatformRolemit EnumSYSTEM_OPERATOR+PLATFORM_ADMIN(+ Zukunft). - Verworfen für Phase 1, weil:
- Zwei Autoritäts-Achsen heute bekannt → zwei Flags sind die einfachste, ausreichende Lösung.
- Einführung einer neuen Tabelle = Migration + zusätzliche Query-Overhead.
- Kann später eingeführt werden, wenn eine dritte Achse entsteht — die beiden Flags migrieren dann trivial in die neue Struktur.
Alternative D (EMPFOHLEN): Zwei orthogonale Flags, Rolle eliminiert
isSysAdminbehält Bedeutung + Bypass, wird aber semantisch auf Infrastruktur eingeschränkt.isPlatformAdminneu für Cross-Mandate-Governance.sysadmin-Rolle wird aus Root-Mandant + AccessRules entfernt.- Vorteile:
- Eine Autorität = genau ein Marker (keine Sync-Pflicht).
- Einzeln vergebbar → feinere Governance.
- Keine Rollen mehr, die Mandanten-Grenzen durchbrechen.
- RBAC-Engine-Logik wird einfacher (kein root-mandate-role-lookup mehr).
- Nachteile:
- Migration nötig.
- Alle
ctx.hasSysAdminRole/requireSysAdminRoleCallsites müssen umgeschrieben werden.
→ Entscheidung: Alternative D.
Zielarchitektur im Detail
Autoritäts-Matrix (nach Umsetzung)
| Autorität | Scope | Marker | Prüfung in Route |
|---|---|---|---|
| Logs, Tokens, DB-Health, i18n-Master | global (ohne Mandant) | isSysAdmin=true |
requireSysAdmin |
| RBAC-Engine-Bypass | global (Datenzugriff) | isSysAdmin=true |
rbac.py:getUserPermissions (unverändert) |
| User-Mgmt cross-mandate | cross-mandate | isPlatformAdmin=true |
requirePlatformAdmin (neu) |
| Mandate-Mgmt (create/edit/block) | cross-mandate | isPlatformAdmin=true |
requirePlatformAdmin |
| RBAC-Rules-Catalog | cross-mandate (Templates) | isPlatformAdmin=true |
requirePlatformAdmin |
| Feature-Registry (Admin-Features-UI) | cross-mandate | isPlatformAdmin=true |
requirePlatformAdmin |
| User-Access-Overview | cross-mandate | isPlatformAdmin=true |
requirePlatformAdmin |
| Billing-Admin-Overview (alle Mandate) | cross-mandate | isPlatformAdmin=true |
requirePlatformAdmin |
| Billing eines Mandanten | 1 Mandant | Mandanten-Rolle admin |
requireMandateAdmin(mandateId) (bestehend) |
| User-Verwaltung innerhalb Mandant | 1 Mandant | Mandanten-Rolle admin |
requireMandateAdmin(mandateId) |
| Feature-Instance-Verwaltung | 1 Mandant | Mandanten-Rolle admin |
requireMandateAdmin(mandateId) |
Neue Auth-Helpers (in modules/auth/authentication.py)
@property
def isPlatformAdmin(self) -> bool:
return getattr(self.user, 'isPlatformAdmin', False)
def requirePlatformAdmin(currentUser: User = Depends(getCurrentUser)) -> User:
if not getattr(currentUser, 'isPlatformAdmin', False):
raise HTTPException(403, "Platform admin privileges required")
audit_logger.logSecurityEvent(userId=str(currentUser.id),
mandateId="system", action="platform_admin_action",
details="Cross-mandate governance operation")
return currentUser
requireSysAdmin (bestehend) wird enger definiert — nur noch für
Infrastruktur-Routen.
Migration (einmalig, beim Boot, idempotent)
for user in alle User:
hadSysadminRole = _hasSysAdminRole_legacy(user.id)
# Alte Rolle impliziert sowohl Cross-Mandate-Admin als auch (historisch)
# impliziten Infrastruktur-Zugriff. Setze neuen Flag konservativ.
if hadSysadminRole and not user.isPlatformAdmin:
user.isPlatformAdmin = True
log.warning(f"Migration: granted isPlatformAdmin to {user.id}")
# Flag bleibt wie es ist (unabhängig).
after:
drop AccessRules WHERE roleId = sysadmin_root_role
drop UserMandateRole WHERE roleId = sysadmin_root_role
drop Role WHERE id = sysadmin_root_role
Referenzen (anzupassende Stellen)
Backend — Datamodel
| Datei | Was | Änderung |
|---|---|---|
gateway/modules/datamodels/datamodelUam.py |
User Pydantic + UserInDB |
Neues Feld isPlatformAdmin: bool = False + Validator |
gateway/modules/datamodels/datamodelUam.py |
Docstring isSysAdmin |
Neue Semantik („Infrastructure/System Operator") |
Backend — Auth
| Datei | Was | Änderung |
|---|---|---|
gateway/modules/auth/authentication.py |
_hasSysAdminRole, requireSysAdminRole, _getRootMandateRoleIds |
Löschen (bzw. als deprecated Stub behalten für transitional Phase) |
gateway/modules/auth/authentication.py |
RequestContext.hasSysAdminRole |
Löschen |
gateway/modules/auth/authentication.py |
RequestContext.isPlatformAdmin (neu) |
Property |
gateway/modules/auth/authentication.py |
requirePlatformAdmin (neu) |
Dependency |
gateway/modules/auth/__init__.py |
Exporte | requirePlatformAdmin exportieren |
Backend — Bootstrap
| Datei | Was | Änderung |
|---|---|---|
gateway/modules/interfaces/interfaceBootstrap.py |
_initSysAdminRole + _createSysAdminAccessRules + _ensureSysAdminAccessRules |
Löschen (inkl. aller damit verbundener AccessRules) |
gateway/modules/interfaces/interfaceBootstrap.py |
_ensureAdminUser, _ensureEventUser |
Setze isPlatformAdmin=True zusätzlich zu isSysAdmin=True |
gateway/modules/interfaces/interfaceBootstrap.py |
Neu: _migrateSysAdminRoleToPlatformAdminFlag() |
Einmalige Migration beim Boot; idempotent |
Backend — Routen (alle Callsites von hasSysAdminRole / requireSysAdminRole)
| Datei | Callsites | Änderung |
|---|---|---|
gateway/modules/routes/routeDataUsers.py |
Z.106, 159, 236, 325, 445, 517, 571, 586, 919 | hasSysAdminRole → isPlatformAdmin |
gateway/modules/routes/routeDataUsers.py |
_syncSysAdminRole (aus Sync-Phase 1) |
Löschen (nicht mehr nötig) |
gateway/modules/routes/routeDataUsers.py |
update_user: Flag-Sync-Logik |
Vereinfachen (kein Rolle-Sync mehr) |
gateway/modules/routes/routeDataMandates.py |
Z.104, 226, 258, 380, 437, 510, 1054 | hasSysAdminRole/requireSysAdminRole → isPlatformAdmin/requirePlatformAdmin |
gateway/modules/routes/routeDataMandates.py |
Reverse-Sync (Z.916–944 aus Sync-Phase 1) | Löschen |
gateway/modules/routes/routeAdminRbacRules.py |
11 Stellen (Z.245, 365, 490, 537, 588, 668, 756, 839, 1020, 1079, 1140, 1204, 1360) | hasSysAdminRole/requireSysAdminRole → isPlatformAdmin/requirePlatformAdmin |
gateway/modules/routes/routeAdminFeatures.py |
22 Stellen | analog |
gateway/modules/routes/routeAdminUserAccessOverview.py |
Z.78, 126, 220 | analog |
gateway/modules/routes/routeAdminDatabaseHealth.py |
Z.44, 56, 68, 93 | bleibt requireSysAdmin (Infrastruktur) |
gateway/modules/routes/routeAdminLogs.py |
Z.66, 107 | bleibt requireSysAdmin (Infrastruktur) |
gateway/modules/routes/routeAdminDemoConfig.py |
Z.28, 44, 69 | isPlatformAdmin (Data-Change-Operation) |
gateway/modules/routes/routeBilling.py |
Z.89, 145, 737, 1464, 1487 | Cross-Mandate-Views → isPlatformAdmin; Mandate-eigene → unverändert |
gateway/modules/routes/routeI18n.py |
Z.829, 847, 860, 876, 914, 942 | i18n-Master/System → requireSysAdmin; Übersetzungs-Management → isPlatformAdmin |
gateway/modules/routes/routeSubscription.py |
Z.49, 306, 488 | isPlatformAdmin |
gateway/modules/routes/routeInvitations.py |
Z.189, 894 | isPlatformAdmin |
gateway/modules/routes/routeSystem.py |
Z.481, 299, 306, 360, 370, 375, 385, 398, 408 | isPlatformAdmin für Navigations-Sichtbarkeit |
gateway/modules/routes/routeAudit.py |
Z.134 | isPlatformAdmin (Cross-Mandate-Audit) |
gateway/modules/routes/routeNotifications.py |
518 | unverändert (addRoleToUserMandate betrifft keine sysadmin mehr) |
gateway/modules/routes/routeDataFiles.py |
Z.11, 548, 850, 1044 | Global-Scope → isSysAdmin bleibt (Infra-Daten) |
gateway/modules/routes/routeDataSources.py |
Z.10, 56 | analog |
gateway/modules/routes/routeWorkflowDashboard.py |
9 Stellen | Cross-Mandate-Übersicht → isPlatformAdmin |
gateway/modules/features/trustee/routeFeatureTrustee.py |
Z.107, 141, 159, 161, 1814 | prüfen pro Callsite (meist isPlatformAdmin) |
gateway/modules/features/teamsbot/routeFeatureTeamsbot.py |
8 Stellen | System-Bot-Registrierung → isSysAdmin bleibt; User-Mgmt → isPlatformAdmin |
gateway/modules/features/chatbot/routeFeatureChatbot.py |
Z.105 | isPlatformAdmin |
gateway/modules/features/realEstate/routeFeatureRealEstate.py |
Z.119 | isPlatformAdmin |
gateway/modules/routes/routeRealEstate.py |
Z.124 | isPlatformAdmin |
gateway/modules/features/workspace/... |
(suchen) | pro Callsite prüfen |
gateway/modules/interfaces/interfaceDbManagement.py |
Z.637–643 _isSysAdmin |
Bleibt an Flag gebunden (RBAC-Bypass in Data-Management) |
Backend — Services / Demo / Tests
| Datei | Was | Änderung |
|---|---|---|
gateway/modules/demoConfigs/investorDemo2026.py |
Z.188, 222 | isPlatformAdmin=True statt Rolle; isSysAdmin wie gehabt |
gateway/modules/serviceCenter/services/serviceAgent/... |
Z.568 | Liest Flag — kein Change |
gateway/tests/** |
alle hasSysAdminRole/requireSysAdminRole Mentions |
Tests anpassen |
gateway/tests/integration/rbac/test_sysadmin_sync.py (geplant Phase 1) |
entfällt bzw. wird zu test_platform_admin_flag.py |
Frontend
| Datei | Was | Änderung |
|---|---|---|
frontend_nyla/src/types/mandate.ts |
User-Typ Z.143 | isPlatformAdmin: boolean ergänzen |
frontend_nyla/src/api/userApi.ts / authApi.ts |
User-Schema | isPlatformAdmin?: boolean ergänzen |
frontend_nyla/src/utils/userCache.ts |
User-Cache | isPlatformAdmin ergänzen |
frontend_nyla/src/pages/admin/AdminUsersPage.tsx |
Edit-Formular | Zwei separate Toggles: "Systemadmin" und "Plattformadmin"; Tooltip mit Erklärung |
frontend_nyla/src/pages/admin/AdminUserAccessOverviewPage.tsx |
User-Grid, Detail-Ansicht | Beide Flags anzeigen; Spalten + Filter |
frontend_nyla/src/pages/billing/BillingAdmin.tsx |
Z.449, 505, 508, 669 | isSysAdmin → isPlatformAdmin für Cross-Mandate-View |
frontend_nyla/src/pages/views/teamsbot/*.tsx |
Z.23, 24, 446, 329 | prüfen; meist isPlatformAdmin für Admin-UI, isSysAdmin für SystemBot-Config |
frontend_nyla/src/hooks/useUsers.ts |
Z.35, 39, 74, 88, 92, 218, 222 | Beide Flags mitgeben |
Entscheidungen
| Datum | Entscheidung | Begründung |
|---|---|---|
| 2026-04-17 | sysadmin-Rolle im Root-Mandant wird eliminiert |
Eine Rolle im Mandanten-Kontext, die Autorität über andere Mandanten gibt, ist architektonisch falsch |
| 2026-04-17 | Zwei orthogonale Flags: isSysAdmin (Infrastruktur) + isPlatformAdmin (Cross-Mandate-Governance) |
Deckt die heute bekannten zwei Autoritäts-Achsen sauber ab; einzeln vergebbar, einzeln auditierbar |
| 2026-04-17 | Keine neue PlatformRole-Tabelle |
Zwei Achsen → zwei Flags reichen; Tabelle wäre Over-Engineering; spätere Migration trivial möglich |
| 2026-04-17 | isSysAdmin-RBAC-Bypass bleibt erhalten |
Bewusste Engine-Design-Entscheidung; Infrastruktur-Ops brauchen Unabhängigkeit vom RBAC-Schema |
| 2026-04-17 | requirePlatformAdmin prüft Flag ohne RBAC-Bypass |
Plattform-Governance ist Admin-UI-Recht, nicht Daten-Bypass-Recht |
| 2026-04-17 | Migration idempotent beim Boot, nicht als separates Migrations-Skript | Konsistent mit bisherigem Bootstrap-Stil; kein Down-Time-Risiko |
| 2026-04-17 | isPlatformAdmin gibt keinen impliziten Daten-Zugriff auf Mandante |
Separation of concerns: Governance ≠ Daten |
| 2026-04-17 | Phase-1-Sync-Code (Flag↔Rolle) wird zurückgerollt | Wird durch finale Lösung obsolet; aktuell committete Helper _syncSysAdminRole etc. entfernen |
Umsetzungs-Checkliste
Phase A — Datamodel + Auth
User.isPlatformAdminFeld in Pydantic + DB-Schema_normalizePlatformAdminValidatorRequestContext.isPlatformAdminPropertyrequirePlatformAdminDependency + Audit- Export in
modules/auth/__init__.py - Docstring-Update
isSysAdminField: neue enge Semantik
Phase B — Migration + Bootstrap
_migrateSysAdminRoleToPlatformAdminFlag()ininterfaceBootstrap.py- Aufruf nach
_initSysAdminRole(vor dessen Entfernung) - Test: vorhandene sysadmin-Rolle + User mit UserMandateRole → Flag=True
_ensureAdminUser,_ensureEventUser:isPlatformAdmin=TruesetzeninvestorDemo2026.py: analog
Phase C — Routen umstellen
- Alle Callsites laut Referenz-Tabelle bearbeiten
- Nach Umstellung: Code-Such nach
hasSysAdminRole/requireSysAdminRole→ muss 0 Treffer ergeben (ausser deprecated-Stub) - Navigation (
routeSystem.py): Menü-Sichtbarkeit korrekt anpassen
Phase D — Phase-1-Sync-Code zurückrollen
routeDataUsers.py:_syncSysAdminRoleentfernenrouteDataUsers.py:update_userSync-Aufruf entfernenrouteDataUsers.py:create_userSync-Aufruf entfernenrouteDataMandates.py:update_user_roles_in_mandateReverse-Sync entfernen
Phase E — sysadmin-Rolle entfernen
_initSysAdminRoleentfernen_createSysAdminAccessRules,_ensureSysAdminAccessRulesentfernen- Migration: sysadmin-Role + AccessRules + UserMandateRole-Einträge löschen
_hasSysAdminRole,requireSysAdminRoleentfernen (oder als deprecated für 1 Release behalten)
Phase F — Frontend
- Typen + userCache erweitern
AdminUsersPage: zwei separate Toggles + TooltipAdminUserAccessOverviewPage: Flag-Spalten + Filter- Callsites Frontend (BillingAdmin, Teamsbot-Pages etc.)
Querschnitt
- RBAC / Permissions: Keine neuen AccessRules. Alte sysadmin-AccessRules gelöscht.
- Neutralisierung: User-Neutralisierung setzt beide Flags auf False.
- Navigation / Routing: Admin-Menü-Sichtbarkeit via
isPlatformAdmin. - Billing-Impact: keiner.
- Tests: Unit-Tests für
requirePlatformAdmin, Integrations-Tests für Migration.
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given User X mit isSysAdmin=true, isPlatformAdmin=false, When X ruft GET /api/admin/mandates auf, Then 403 |
must |
| 2 | Given User X mit isSysAdmin=false, isPlatformAdmin=true, When X ruft GET /api/admin/database-health auf, Then 403 |
must |
| 3 | Given User X mit isPlatformAdmin=true, When X ruft GET /api/admin/mandates auf, Then 200 mit allen Mandanten |
must |
| 4 | Given DB-Migration: User Y hatte sysadmin-Rolle in Root-Mandant, When Gateway bootet, Then Y hat isPlatformAdmin=true; sysadmin-Rolle/AccessRules sind weg |
must |
| 5 | Given Admin entzieht User X den isPlatformAdmin-Flag, When X im nächsten Request GET /api/admin/rbac-rules aufruft, Then 403 (ohne Token-Refresh, nächster Request prüft live DB) |
must |
| 6 | Given Admin entzieht User X den isSysAdmin-Flag, When X im nächsten Request GET /api/admin/logs aufruft, Then 403 |
must |
| 7 | Given Admin > USER-Formular, When Admin bearbeitet User, Then zwei separate Toggles „Systemadmin" und „Plattformadmin" sind sichtbar + dokumentiert |
must |
| 8 | Given User X versucht sich selbst isSysAdmin oder isPlatformAdmin zu verändern, Then 403 (Self-Protection für beide Flags) |
must |
| 9 | Given _hasSysAdminRole / requireSysAdminRole werden importiert, When Codebase gescannt wird, Then 0 Treffer (oder nur deprecated-Stub mit Logger-Warning) |
should |
| 10 | Given sysadmin-Rolle existiert noch in DB, When Migration läuft, Then Rolle + zugehörige UserMandateRole + AccessRules werden gelöscht; Flag der betroffenen User ist konsistent gesetzt |
must |
| 11 | Given „Operations-Engineer"-User mit isSysAdmin=true, isPlatformAdmin=false, When er die Admin-UI öffnet, Then sieht er System-Menüpunkte (Logs, DB-Health), aber keine Mandate-/User-Verwaltung |
should |
| 12 | Given „Customer-Success-Admin" mit isSysAdmin=false, isPlatformAdmin=true, When er die Admin-UI öffnet, Then sieht er User-/Mandate-Verwaltung, aber keine Logs |
should |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1 | api-integration | ja | gateway/tests/integration/rbac/test_platform_admin_flag.py |
done |
| T2 | 2 | api-integration | ja | dito | done |
| T3 | 3 | api-integration | ja | dito | done |
| T4 | 4,10 | migration-unit | ja | gateway/tests/unit/rbac/test_sysadmin_migration.py |
done |
| T5 | 5,6 | api-integration | ja | gateway/tests/integration/rbac/test_platform_admin_flag.py |
done |
| T6 | 7 | frontend (Playwright) | optional | frontend_nyla/e2e/admin-users-flags.spec.ts (neu) |
open |
| T7 | 8 | api-unit | ja | gateway/tests/integration/rbac/test_platform_admin_flag.py |
done |
| T8 | 9 | codebase-scan | ja (CI-Check) | scripts/check_no_sysadmin_role.py (CI-Gate) |
done |
| T9 | 11,12 | manuelle UI-Regression | manuell | Siehe Security-Regression-Manual | open |
Security-Regression-Manual (T9)
- User A:
isSysAdmin=true,isPlatformAdmin=false. Login.Admin > Logs→ ok.Admin > DB-Health→ ok.Admin > Mandate→ 403/nicht sichtbar im Menü.Admin > User→ 403/nicht sichtbar.
- User B:
isSysAdmin=false,isPlatformAdmin=true. Login.Admin > Mandate→ ok.Admin > User→ ok.Admin > User Access Overview→ ok.Admin > Logs→ 403.Admin > DB-Health→ 403.
- User C: beide
false. Login.- Keine Admin-Menüpunkte sichtbar.
- Flag-Entzug im laufenden Betrieb: Admin entzieht B das
isPlatformAdmin-Flag. B's nächster Request aufAdmin > Mandate→ 403 (ohne Re-Login). - sysadmin-Rolle-Check: kein User hat mehr eine sysadmin-Rolle in DB
(
SELECT COUNT(*) FROM "Role" WHERE "roleLabel" = 'sysadmin'= 0).
Links
- RBAC-Referenz:
wiki/b-reference/platform/rbac.md - Auth-Modul:
gateway/modules/auth/authentication.py - Ausgangspunkt Testing PORTA 2026-04-17 (Issue 4: Security-Bug)
Abschluss
wiki/b-reference/platform/rbac.md: Abschnitt „Platform-Governance-Autoritaet" ergaenztwiki/TOPICS.md: Topic „Authority-Modell: System/Platform/Mandate" anlegen- Dieses Dokument nach Abschluss →
wiki/c-work/4-done/verschoben - CI-Gate
gateway/scripts/check_no_sysadmin_role.py(Acceptance T8 / AC#9)
Umsetzungs-Status
- Phase A (Datamodel + Auth) : done
- Phase B (Migration + Bootstrap) : done
- Phase C (Routen umstellen) : done — 0 Treffer fuer hasSysAdminRole/requireSysAdminRole/_hasSysAdminRole
- Phase D (Phase-1-Sync zurueckgerollt) : done
- Phase E (sysadmin-Rolle entfernt) : done — Stubs entfernt, Migration entfernt Rolle/AccessRules/Memberships
- Phase F (Frontend) : done — Typen + AdminUserAccessOverview + AdminMandates + BillingAdmin + useUsers
- Tests + CI-Gate : done — T1-T5, T7, T8 automatisiert (13 grün); T6 (Playwright) + T9 (manuell) optional/offen