# Einheitliche Zugriffsschicht & Governance – Umsetzungsplan **Status:** verbindliche Umsetzungsreihenfolge (nachgelagert zum Zielbild in `MULTI_TENANCY_RBAC_ARCHITECTURE.md`) **Stand:** 2026-05-06 **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 1. **Ein Mandant für Datenisolierung:** `club_id` ist die Grenze für „vereinsgeteilte“ Inhalte. Nur Ausnahmen: explizit **plattformweite/offizielle** Objekte und **privat**e Objekte des Erstellers. 2. **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). 3. **Eine fachliche Sichtbarkeits-Semantik** über alle Bibliotheks- und Planungsartefakte (Übungen, Vorlagen, Rahmenprogramme, …): gleiche Enums, gleiche Leseprüfung, angepasste Listenfilter. 4. **Sparte optional verschärfen:** `division_id` auf Objekten und später auf Rollenzuweisung ausgewertet – ohne Vereinsgrenze zu sprengen. 5. **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 mit `profile_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-Id` und Persistenz `active_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`; **`division`** dort 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_lead` und optional `division_id` auf Mitgliedschaft/Rolle auswerten bei Schreib-/Lesevorgängen für Objekte mit `division_id`. - Dokumentieren: Was gilt für Objekte mit `division_id=NULL` innerhalb eines Vereins (vereinsweit vs. nur „ohne Sparte“). ### Stufe E – Capabilities dokumentieren (ohne UI für Custom Roles) - **Verbindliche Spez v1:** `CAPABILITY_CATALOG.v1.md` — Capability-IDs, Account-Lifecycle, Rollen-Matrix, Endpoint-Mapping. - Markdown-Tabelle **Capability-Fingerprint**: Kennungen wie `exercises.ai.suggest`, `org.members.manage`, … mit Zuordnung zu den **heutigen** festen Vereinsrollen (siehe Katalog §5–6). - Ziel: später `club_custom_roles` nur 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 (Konzept liegt vor) - **Spez v1:** `CLUB_MEMBERSHIP_AND_FEATURES.v1.md` — Feature-Registry (Mitai-v9c-Pattern), `club_plans`/`club_subscriptions`, Kontingente an `club_id`. - Implementierung/Billing (Stripe) weiter zurückgestellt; Schema- und Enforcement-Hooks gemäß 4-Phasen-Rollout (Mitai-Vorbild) vorbereiten, sobald Stufe C/D stabil. --- ## 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`. Optional danach: `cd backend && pytest tests/` (nach `pip install -r requirements-dev.txt`). | | **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** | Zielbild und Gap-Analyse §3 dort pflegen (**§3.0** = aktueller Umsetzungsstand); **operative Reihenfolge** hier. | --- ## 6. Nächste konkrete Artefakte (nach diesem Plan) 1. **TenantContext-Spezifikation** (ein Abschnitt in diesem Dokument oder Kurz-ADR): Request-Lebenszyklus, Fehlerbilder, Superadmin. 2. **Endpoint-Audit-Tabelle** (Working-Dokument, bei jedem Merge pflegen bis Stufe C abgeschlossen). 3. **Testplan „Cross-Tenant“** (manuell oder pytest): Minimalsetup zweier Vereine — Unit-Tests `backend/tests/test_access_layer.py`; Integration `backend/tests/test_access_layer_integration.py` bei `ACCESS_LAYER_INTEGRATION=1` / CI im Backend-Container. **Audit-Tabelle (fortlaufend):** `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` --- ## 7. Referenzen - **`CAPABILITY_CATALOG.v1.md`** – Rollen, Capabilities, CRUD-Mapping, `GET /api/me/entitlements`. - **`CLUB_MEMBERSHIP_AND_FEATURES.v1.md`** – Vereinsabo, Feature-Limits, Mitai-Mapping, Ziel-Schema. - `.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md` – übergeordnetes Zielbild & Begriffe. - `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` – verbindliche Domänenregeln für **Medien-Assets** (gleiche Sichtbarkeit wie Übungen, Promotion-Kopplung, Copyright, Papierkorb/Lebenszyklus, externer Speicher). Bei Widerspruch zur Sichtbarkeits-Tabelle in §3 dieses Dokuments: §3 für Enums/`library_content_*`-Semantik, Medien-Spez für Asset-spezifische Zusatzregeln. - `backend/club_tenancy.py` – bestehende Bausteine (`assert_club_member`, `exercise_visible_to_profile`, `can_plan_in_club`, …); Ziel ist Deren schrittweise Zusammenführung unter die neue Zugriffsschicht ohne Big-Bang. --- ## 8. Anhang – Übungen (Ist-Implementierung, Referenz) **Stand:** 2026-05-20 · **Detail:** `EXERCISES_API_SPEC.md` Permissions, `FEATURES_DELIVERED_2026-Q2.md` §16 | Feld / Konzept | Semantik | |----------------|----------| | `created_by` | Owner der Übung; Varianten erben Rechte | | `visibility` | UI: **Freigabelevel** — `private` \| `club` \| `official` | | Lesen | `exercise_visible_to_profile` — `official` global; `private` Ersteller + Plattform-Admin; `club` aktive Mitglieder (+ Plattform-Admin Audit) | | Bearbeiten | Ersteller; Plattform-Admin; bei `club` zusätzlich `can_plan_in_club` (Trainer, Content-Editor, Spartenleitung, Vereins-Admin) | | Löschen | `official` → Plattform-Admin; `club` → `club_admin` im Objekt-Verein; `private` → Ersteller oder Vereins-Admin mit gemeinsamem Verein | **Hinweis:** Dieser Anhang dokumentiert den **produktiven Code-Pfad** in `exercises.py`; die Roadmap in §4 bleibt für die langfristige Vereinheitlichung aller Bibliotheksartefakte maßgeblich. --- **Letzte Aktualisierung:** 2026-05-20