- Added new documentation references for access layer governance in CLAUDE.md, including multi-tenancy and endpoint audit guidelines. - Updated ACCESS_LAYER_AND_GOVERNANCE_PLAN.md to include cursor and heuristic checks for access layer compliance. - Enhanced ACCESS_LAYER_ENDPOINT_AUDIT.md to clarify endpoint visibility and governance requirements, including exemptions for certain routers. - Introduced library_content_visible_to_profile function in club_tenancy.py to streamline visibility checks for library content. - Updated exercise progression graphs router to utilize the new visibility function, improving access control. - Bumped application version to 0.8.27 and updated changelog to reflect these changes.
8.3 KiB
Einheitliche Zugriffsschicht & Governance – Umsetzungsplan
Status: verbindliche Umsetzungsreihenfolge (nachgelagert zum Zielbild in MULTI_TENANCY_RBAC_ARCHITECTURE.md)
Stand: 2026-05-05
Zweck: Drift vermeiden – eine nachvollziehbare Schicht für Mandanten-Kontext, Sichtbarkeit und Berechtigungen, auf die alle inhaltsbezogenen Module konsistent aufbauen.
Explizit zurückgestellt (wie vereinbart): kostenpflichtiges Vereins-Membership / Tier-Limits pro Verein (club_subscriptions o. Ä.) – kommt nach stabiler Zugriffs- und Datenisolationsbasis.
Separates späteres Konzept: durch Vereinsadmins definierbare Rollen mit feingranularen Rechten (Capability-Bundles in DB/UI); dieser Plan bereitet nur die Capability-Idee und Erweiterungspunkte vor.
1. Leitprinzipien
- Ein Mandant für Datenisolierung:
club_idist die Grenze für „vereinsgeteilte“ Inhalte. Nur Ausnahmen: explizit plattformweite/offizielle Objekte und private Objekte des Erstellers. - Ein Kontext pro HTTP-Request: Mitgliedschaft und gewählter aktiver Verein werden einmal aufgelöst und validiert; Folgelogik nutzt nur noch dieses Objekt (kein verteiltes erneutes „Rates“ aus Headers).
- Eine fachliche Sichtbarkeits-Semantik über alle Bibliotheks- und Planungsartefakte (Übungen, Vorlagen, Rahmenprogramme, …): gleiche Enums, gleiche Leseprüfung, angepasste Listenfilter.
- Sparte optional verschärfen:
division_idauf Objekten und später auf Rollenzuweisung ausgewertet – ohne Vereinsgrenze zu sprengen. - Community später additive: neue Freigabeebene oder Flags ergänzen die bestehende Semantik, ohne
club-Isolation zu ersetzen.
2. Architektur der Zugriffsschicht (Schichtenmodell)
| Schicht | Verantwortung |
|---|---|
| Authentifizierung | Session / Token → profile_id, globale role (require_auth, bestehend). |
| TenantContext (neu, zentral) | Aus profile_id + Header X-Active-Club-Id (optional) + DB profiles.active_club_id: effektive effective_club_id nur wenn Mitgliedschaft aktiv existiert; sonch klaren Fehler (403/400 nach Konvention). Optional: Cache der Mitgliedschaftszeilen/Rollen einmal pro Request. |
| Governance / Objekt contra Account | Für jedes geschützte Objekt: visibility, club_id, division_id (nullable), created_by → eine zentrale Entscheidung can_read / can_write (interne Module, keine Copy-Paste-Logik pro Router). |
| Funktions-/Feature-Rechte | „Darf dieser Nutzer im Verein X Trainergruppe anlegen?“ → Capability-Checks (can_manage_club_org, can_plan_in_club, …); später durch dynamische Rollen → gleiche Capability-Namen. |
Ziel: Router werden dünn: laden Daten nur noch durch Hilfen, die TenantContext und Governance bereits berücksichtigen oder explizit prüfen.
3. Scope- und Sichtbarkeitsmodell (einheitlich)
Ausgangslage im Code: private | club | official (siehe club_tenancy).
Zielbild (phasenweise DB/API-Anpassung):
| Wert | Lesende Regel (kurz) |
|---|---|
private |
Nur created_by (+ Plattform-Admin nach Policy); keine Vereinsliste ohne Ownership. |
club |
Nur aktive Mitglieder des Objekt-club_id; Cross-Verein nie. |
division (optional neue Stufe oder club + Pflicht division_id) |
Nur Mitglieder, die dieser Sparte zugeordnet sind (Regeln gesondert spezifizieren: Mitgliedschaft vs. Gruppe vs. Rolle division_lead). |
official |
Plattform-weit lesbar (Superadmin publiziert/pflegt); weiterhin strikt von club-Daten getrennt. |
community (reserviert) |
Noch nicht implementieren; Design nur additive Felder/Enum-Einträge dokumentieren, wenn erste Stories starten. |
Trainer-Flow: „Privat anlegen, dann im Verein teilen“ = Transition von private zu club (oder Kopie + neue Visibility – Produktentscheidung; technisch muss club_id gesetzt und Mitgliedschaft geprüft werden).
4. Roadmap (verbindliche Reihenfolge)
Stufe A – Foundations & Audit
- Router-Inventar: Liste aller Endpoints mit Zugriff auf
club_id,visibility, organisationalem Bezug oder Listenfiltern (Excel/Markdown-Tabelle im Repo oder unter.claude/docs/working/). - Definition of Done je Endpoint: „Default deny“ für tenant-sensitive Listen wenn Kontext fehlt/ungültig.
- TenantContext-Spezifikation festhalten (Feldnamen, HTTP-Fehlercodes, Superadmin-Ausnahmen).
Stufe B – TenantContext (Backend zentral)
- FastAPI-Dependency z. B.
get_tenant_context(session, x_active_club_id)→ Objekt mitprofile_id,global_role,effective_club_id,club_memberships(optional gekürzt). - Alle neuen und bestehenden sicherheitsrelevanten Änderungen nur über diese Dependency oder darauf aufbauende Helfer.
- Abgleich mit Frontend:
X-Active-Club-Idund Persistenzactive_club_id(bereits vorhanden) als einzige variabler Vereinskontext-Quellen.
Stufe C – Governance vereinheitlichen
- Eine interne API-Stilebene (auch wenn mehrere Python-Funktionen):
content_readable(...),content_writable(...)oder zweischichtig „Governance“ vs „Org-Rechte“. - Module angleichen: Übungen, Trainingsplanung, Rahmenprogramme, Vorlagen – gleiche Regeln für
club/official/private;divisiondort einführen, wo fachlich nötig (einheitliche Filter-Chips in UI). - Regressionstests: zwei Vereine, zwei Nutzer, kein Kreuzzugriff auf
club-Objekte; Superadmin/Global-Path weiterhin getrennt testen.
Stufe D – Sparten-Durchsetzung
division_leadund optionaldivision_idauf Mitgliedschaft/Rolle auswerten bei Schreib-/Lesevorgängen für Objekte mitdivision_id.- Dokumentieren: Was gilt für Objekte mit
division_id=NULLinnerhalb eines Vereins (vereinsweit vs. nur „ohne Sparte“).
Stufe E – Capabilities dokumentieren (ohne UI für Custom Roles)
- Markdown-Tabelle Capability-Fingerprint: Kennungen wie
content.share_club,planning.edit_unit,org.manage_members, … mit Zuordnung zu den heutigen festen Vereinsrollen. - Ziel: später
club_custom_rolesnur noch andere Kombination derselben Kennungen – keine zweite Philosophie.
Stufe F – Community (eigenes Epic)
- Konzept: Freigabe additiv (Flag oder Enum), Moderation, Sichtbarkeit „öffentlich außerhalb meines Vereins“ ohne bestehende
club-Isolation zu brechen.
Zurückgestellt – Vereinsabo / Limits
- Wiederöffnen wenn ACCESS_LAYER Stufe C/D stabil; dann Enforcement vor ausgewählten Writes an einen Billing-Stripe binden.
5. Drift vermeiden (Arbeitsdisziplin)
| Mechanismus | Inhalt |
|---|---|
| Cursor / IDE | Projektregel .cursor/rules/access-layer.mdc (Router); Agents sollen nicht auf „nur require_auth“ ausweichen. |
| Heuristik-Check | python backend/scripts/check_access_layer_hints.py; CI optional mit ACCESS_LAYER_STRICT=1. |
| PR-Checkliste | Neuer/changed Endpoint: TenantContext verwendet? Governance für Listen + Detail? Tests für zweiten Verein? |
| Single Source of Truth | Sichtbarkeitsregeln nur in Zugriffsmodul(en), nicht in Routers dupliziert. |
| Änderungen am Enum | Nur zusammen mit Migration + Kurzbeschreibung in diesem Dokument (Datum/Changelog-Zeile). |
| Beziehung zu MULTI_TENANCY-Doc | Phasen 1–4 dort größtenteils umgesetzt; Gap-Analyse §3 im alten Dokument historisch lesen – fachlicher Zielabgleich bleibt dort, operative Reihenfolge hier. |
6. Nächste konkrete Artefakte (nach diesem Plan)
- TenantContext-Spezifikation (ein Abschnitt in diesem Dokument oder Kurz-ADR): Request-Lebenszyklus, Fehlerbilder, Superadmin.
- Endpoint-Audit-Tabelle (Working-Dokument, bei jedem Merge pflegen bis Stufe C abgeschlossen).
- Testplan „Cross-Tenant“ (manuell oder pytest): Minimalsetup zweier Vereine.
Audit-Tabelle (fortlaufend): .claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md
7. Referenzen
.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md– übergeordnetes Zielbild & Begriffe.backend/club_tenancy.py– bestehende Bausteine (assert_club_member,exercise_visible_to_profile, …); Ziel ist Deren schrittweise Zusammenführung unter die neue Zugriffsschicht ohne Big-Bang.
Letzte Aktualisierung: 2026-05-05