wiki/c-work/4-done/2026-04-sysadmin-authority-split.md

35 KiB
Raw Blame History

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:

  1. User.isSysAdmin FLAG (Spalte in User-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, direktes getattr(user, 'isSysAdmin', False).
    • Verwendet in Routen für System-Operationen (Tokens, Logs, DB-Health, i18n-Master, Teams-SystemBot, global-scoped Files/Sources).
  2. sysadmin ROLE (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

  1. Architektonisch inkonsistent: Alle anderen Rollen sind mandanten-scoped. Nur sysadmin ist eine Rolle im Root-Mandant, die Autorität über andere Mandanten verleiht. Das durchbricht die Mandanten-Isolation konzeptionell.
  2. 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.
  3. Verwirrende Semantik: „SysAdmin" kann zwei unterschiedliche Dinge bedeuten — je nachdem, wer fragt.
  4. 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 BE): 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=True einen 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 an isPlatformAdmin gebunden.
  • 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.

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 isSysAdmin flag, 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 UserPlatformRole mit Enum SYSTEM_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

  • isSysAdmin behält Bedeutung + Bypass, wird aber semantisch auf Infrastruktur eingeschränkt.
  • isPlatformAdmin neu 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 / requireSysAdminRole Callsites 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 hasSysAdminRoleisPlatformAdmin
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/requireSysAdminRoleisPlatformAdmin/requirePlatformAdmin
gateway/modules/routes/routeDataMandates.py Reverse-Sync (Z.916944 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/requireSysAdminRoleisPlatformAdmin/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.637643 _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 isSysAdminisPlatformAdmin 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.isPlatformAdmin Feld in Pydantic + DB-Schema
  • _normalizePlatformAdmin Validator
  • RequestContext.isPlatformAdmin Property
  • requirePlatformAdmin Dependency + Audit
  • Export in modules/auth/__init__.py
  • Docstring-Update isSysAdmin Field: neue enge Semantik

Phase B — Migration + Bootstrap

  • _migrateSysAdminRoleToPlatformAdminFlag() in interfaceBootstrap.py
  • Aufruf nach _initSysAdminRole (vor dessen Entfernung)
  • Test: vorhandene sysadmin-Rolle + User mit UserMandateRole → Flag=True
  • _ensureAdminUser, _ensureEventUser: isPlatformAdmin=True setzen
  • investorDemo2026.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:_syncSysAdminRole entfernen
  • routeDataUsers.py:update_user Sync-Aufruf entfernen
  • routeDataUsers.py:create_user Sync-Aufruf entfernen
  • routeDataMandates.py:update_user_roles_in_mandate Reverse-Sync entfernen

Phase E — sysadmin-Rolle entfernen

  • _initSysAdminRole entfernen
  • _createSysAdminAccessRules, _ensureSysAdminAccessRules entfernen
  • Migration: sysadmin-Role + AccessRules + UserMandateRole-Einträge löschen
  • _hasSysAdminRole, requireSysAdminRole entfernen (oder als deprecated für 1 Release behalten)

Phase F — Frontend

  • Typen + userCache erweitern
  • AdminUsersPage: zwei separate Toggles + Tooltip
  • AdminUserAccessOverviewPage: 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)

  1. 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.
  2. 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.
  3. User C: beide false. Login.
    • Keine Admin-Menüpunkte sichtbar.
  4. Flag-Entzug im laufenden Betrieb: Admin entzieht B das isPlatformAdmin-Flag. B's nächster Request auf Admin > Mandate → 403 (ohne Re-Login).
  5. sysadmin-Rolle-Check: kein User hat mehr eine sysadmin-Rolle in DB (SELECT COUNT(*) FROM "Role" WHERE "roleLabel" = 'sysadmin' = 0).
  • 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" ergaenzt
  • wiki/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