shinkan-jinkendo/.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md
Lars c294c27de8
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 40s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m12s
Update Access Layer and Governance Documentation
- Enhanced the ACCESS_LAYER_AND_GOVERNANCE_PLAN.md with new specifications for capability documentation and community features.
- Added references to new documents detailing capability IDs and club membership features.
- Updated MULTI_TENANCY_RBAC_ARCHITECTURE.md to include links to the new specifications.
- Marked certain features as deprecated in backend/auth.py, indicating migration paths for club feature access.
- Incremented DB_SCHEMA_VERSION to 20260606078 in version.py to reflect recent changes.
2026-06-06 20:44:51 +02:00

10 KiB
Raw Blame History

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 private 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_byeine 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 §56).
  • 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: Freigabelevelprivate | club | official
Lesen exercise_visible_to_profileofficial 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; clubclub_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