shinkan-jinkendo/.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md
Lars c778d21b26
Some checks failed
Deploy Development / deploy (push) Failing after 14s
Test Suite / pytest-backend (push) Successful in 5s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Failing after 2s
Test Suite / playwright-tests (push) Successful in 23s
feat: update application version to 0.8.37 and enhance training planning features
- Bumped application version to 0.8.37 in both backend and frontend files.
- Updated training planning API to include new session assignment features, allowing for lead trainer and assistant trainer assignments.
- Enhanced the TrainingPlanningPage to support dynamic loading of club member directories based on selected groups.
- Improved validation for trainer assignments, ensuring only active club members can be assigned as trainers.
- Updated changelog to reflect the new version and changes made in this release.
2026-05-05 23:35:41 +02:00

15 KiB
Raw Blame History

Multi-Tenancy, Vereins-Membership und Rollenmodell Zielarchitektur & Umsetzungsplan

Status: verbindliche Zielrichtung (Architekturpapier)
Stand: 2026-05-06
Bezug: functional/shinkan_anforderungsdokument_entwurf.md §5, §1718 · functional/DOMAIN_MODEL.md (Sichtbarkeit §5.5) · functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md (CURR-004008)

Operative Reihenfolge & einheitliche Zugriffsschicht: ACCESS_LAYER_AND_GOVERNANCE_PLAN.md dort sind Stufen AF, Drift-Prävention und die Zurückstellung von Vereinsabo/Limits festgehalten; dieses Dokument bleibt das übergeordnete Zielbild.


1. Zweck

Dieses Dokument fasst den Soll-Zustand für Mandantenfähigkeit (Verein = Mandant), rollenbasierte Zugriffskontrolle auf Vereinsebene und ein Membership-/Limitsystem zusammen und definiert einen phasenweisen Umsetzungsplan, der mit dem bestehenden Governance-Kern (visibility, club_id, created_by) konsistent bleibt.


2. Abgleich mit vorhandener Dokumentation

Quelle Inhalt relevant für Tenancy/Rollen Konsistenz mit Zielbild
shinkan_anforderungsdokument_entwurf.md §5.4 Rollen: Superadmin, Vereinsadmin, Trainer, Co-Trainer, Redakteur Deckt sich; „Superadmin“ entspricht fachlich Systemadmin
§17.1 Erweiterung: Systemadmin, Spartenadmin Entspricht den gewünschten Spartenverantwortlichen
§5.5 / §17 Sichtbarkeit: privat, Verein, Sparte, global, offiziell DOMAIN_MODEL listet ähnliche Stufen; Bibliothek private | club | official technisch über Zugriffsschicht durchgesetzt; Sparte/community folgt
§18.5 MVP: Datenmodell mandantenfähig, Rechte zunächst einfach Bestätigt schrittweise Verschärfung
DOMAIN_MODEL.md §5.5 Freigabeebenen inkl. Sparte Zielbild; DB/API nutzen derzeit überwiegend private | club | official
TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md CURR-004008: Governance-Kern, spätere Policy Datenfelder vorbereitet; Policy/Erzwingung folgt
working/SHINKAN_PROJECT_SETUP.md §6 „Multi-Tenant-Administration“ ausgeschlossen (MVP-Liste) Historisch; technische Mandanten sind dennoch Ziel UI-Komplexität kontrolliert einführen

Fazit: Die fachlichen Rollen und Sichtbarkeitsebenen sind in den funktionalen Docs skizziert. Für die Kernteile Bibliothek und Vereinskontext ist eine technische Zugriffsschicht (TenantContext, club_members, einheitliche Sichtbarkeits-SQL/-Prüfungen) umgesetzt — Details und Restarbeit (Sparte, Konsolidierung der Hilfen, Planungs-/Admin-Flows) siehe §3 und ACCESS_LAYER_AND_GOVERNANCE_PLAN.md.


3. Ist-Stand im Code (Gap-Analyse)

Hinweis: Die Unterabschnitte 3.13.6 enthalten weiterhin historische Problemstellungen (Ausgangsbild). Ergänzend beschreibt 3.0 den aktualisierten Umsetzungsstand nach Mitgliedschafts-, Tenant- und Bibliotheksarbeit. Verbindliche offene Arbeit und Reihenfolge: ACCESS_LAYER_AND_GOVERNANCE_PLAN.md.

3.0 Aktualisierung des Umsetzungsstands (kurz)

  • Mitgliedschaft: Tabellen club_members und club_member_roles; aktiver Verein über Profilfeld und Header X-Active-Club-Id; Auflösung in TenantContext (tenant_context.py).
  • Bibliothek (Übungen, Trainingsplan-Vorlagen, Rahmenprogramme u.a.): gemeinsame Leselogik library_content_visibility_sql / library_content_visible_to_profile — Vereinsinhalte club nur bei passendem club_id und aktiver Mitgliedschaft im Objekt-Verein (normale Nutzer ohne gültigen Vereinskontext: kein „beliebiges club“).
  • GET /api/clubs: Nicht-Admins sehen nur Vereine mit Mitgliedschaft; POST /api/clubs: nur Plattform-Admin, mit Pflicht primary_admin_profile_id.
  • Organisation (Sparten/Gruppen): Schreibzugriff über can_manage_club_org / can_plan_in_club auf Basis von club_member_roles (nicht mehr nur globales admin).
  • Profil-API: eingeschränktes GET /profiles/{id}, DELETE, POST /profiles (Plattform-Admin / Selbstzugriff) — Details backend/routers/profiles.py.
  • Tests: pytest inkl. optionaler Mandanten-Integration (ACCESS_LAYER_INTEGRATION); CI-Anbindung siehe .gitea/workflows/test.yml (Ausführung im Backend-Container wie Schwesterprojekt).

3.1 Identität und Rollen

  • profiles.role ist eine globale Kennzeichnung (admin, superadmin, trainer, user, …).
  • (Historisch) Fehlende Abbildung von Vereinsrollen ohne eigene Tabellen.
  • Ist: Zusätzlich club_member_roles pro Verein (z.B. club_admin, trainer, …); Sessions liefern weiter profile_id + globale role (auth.pyget_session), Vereinsrechte werden aus Mitgliedschaft abgeleitet.

Konsequenz: Globale Rolle und Vereinsrollen koexistieren; Produkt und Code sollten langfristig klar trennen, was nur global vs. nur über Mitgliedschaft gilt (vgl. Zielarchitektur §4).

3.2 Organisation & APIs

  • (Historisch) Zu offene Vereinsliste und Club-Anlage für jeden Trainer/User.
  • Ist: siehe 3.0 — gefilterte Liste, eingeschränktes Anlegen, kontextbezogene Organisationsrechte.

Konsequenz: Offene Punkte verlagern sich in feine Produktregeln und Sparten-/Community-Stufen (ACCESS_LAYER Stufe D bzw. spätere Epics).

3.3 Trainingsplanung

  • Zugriff auf Einheiten weiterhin stark gruppenbezogen (training_groups, optional lead_trainer_profile_id auf Einheiten).
  • Mitgliedschaft/TenantContext unterstützen andere Endpoints; GET /training-units hat keinen impliziten Filter nur auf effective_club_id (Multi-Verein-Kalender; bei Bedarf Query club_id).

Konsequenz: Vereinsweite oder „Administrations“-Planungsaufgaben können weiter ausgebaut werden (eigenes Produkt-Thema; nicht identisch mit Bibliotheks-Governance).

3.4 Governance / Sichtbarkeit (Bibliothek)

  • (Historisch) Risiko: club-Objekte ohne Bindung an club_id / Mitgliedschaft → mögliche Cross-Tenant-Sicht.
  • Ist: Listen und Detail für die genannten Bibliotheksmodule nutzen die einheitliche Logik in club_tenancy / tenant_context (siehe 3.0).

Konsequenz: Die historische „Leak“-Diagnose für Übungen und Rahmenprogramme in dieser Form ist überholt. Verbleibend: Konsolidierung auf wenige Hilfsfunktionen (ACCESS_LAYER Stufe C), Sparte als eigene Stufe, ggf. community.

3.5 Frontend

  • GET /api/profiles/me liefert u.a. clubs[], active_club_id; Client setzt X-Active-Club-Id. Geschützte Backend-Routen nutzen Depends(get_tenant_context) wo im Audit festgehalten.

3.6 Membership (kommerziell/limits)

  • Mitai-artige Tabellen (tiers, subscriptions, tier_limits, …) sind nutzerbezogen, nicht vereinsbezogen.
  • Kein konzipiertes club_subscription / club_plan im Schema — bewusst nach ACCESS_LAYER-Plan zurückgestellt.

Letzte Überarbeitung dieses Abschnitts (3.x): 2026-05-06.


4. Zielarchitektur

4.1 Begriffe

Begriff Definition
Mandant Immer ein Verein (clubs.id).
Systemadmin Global (profiles.role oder dediziertes Flag); darf Plattform-weite Objekte und Vereinslifecycle (Anlegen, Zuweisen Hauptverwalter).
Vereinskontext Pro Session gewählter aktiver Verein (active_club_id), wenn der User Mitglied ist.
Vereinsmitgliedschaft Zeile in einer Junction: User ↔ Verein ↔ eine oder mehrere Rollen.
Effektive Berechtigung Funktion aus: globale Rolle, Mitgliedschaft im aktiven Verein, optional Sparte/Gruppe, Sichtbarkeit des Objekts.

4.2 Rollenmodell (Schichten)

Schicht A Plattform (global, kleine Menge)

  • system_admin / superadmin (bestehende Semantik konsolidieren und benennen).

Schicht B Verein (pro Mitgliedschaft, mehrere Rollen möglich)

  • club_admin Hauptverwalter:in (ein Verein genau eine „primäre“ Admin-Zuweisung empfohlen, siehe 4.4).
  • division_lead Spartenverantwortliche:r (Scope: division_id optional an Mitgliedschaft gebunden).
  • trainer Trainer/Übungsleitung (Abgrenzung zu Co-Trainer siehe Gruppe).
  • content_editor Redakteur:in / Inhaltsverantwortliche:r (fachlich wie Anforderungsdoc).

Schicht C Abgeleitet aus Trainingsgruppe (bereits teilweise vorhanden)

  • Haupttrainer / Co-Trainer über training_groups.trainer_id und co_trainer_ids (und ggf. lead_trainer_profile_id auf Einheit).

Mapping Alt → Neu: Bestehendes profiles.role kann Übergangsweise als „Default-Rolle für Pilotverein“ dienen, soll aber mittelfristig nicht die einzige Quelle für Vereinsrechte sein.

4.3 Mitgliedschaft und aktiver Verein

  • Neue Kernstruktur (konzeptionell):
    club_members (profile_id, club_id, status, created_at, …)
    club_member_roles (club_member_id, role_code, optional division_id für spartenbezogene Rollen).

  • Aktiver Verein:

    • Persistenz: Nutzereinstellung (profiles.default_club_id oder eigene Tabelle profile_preferences).
    • Pro Request: Header X-Active-Club-Id oder Query (einheitlich dokumentieren); Server validiert Mitgliedschaft.
  • Tenant-Switch UI: Bei Mitgliedschaft in >1 Verein Auswahl im Frontend; alle Listen/Aktionen verwenden aktiven Kontext.

4.4 Vereins-Lebenszyklus

  • Neuer Verein: nur Systemadmin; Pflichtfelder: Name, initialer club_admin (bestehendes Profil zuweisen oder Einladungsflow später).
  • Vereinsadmin verwaltet in „Mein Verein“: Sparten, Gruppen, Trainerzuordnungen, Einladungen (später), interne Sichtbarkeit ohne andere Vereine zu sehen.

4.5 Daten- und Funktionssicht

Datenklasse Leseregel (Ziel)
Global offiziell Alle authentifizierten (ggf. später thematisch eingeschränkt).
Verein (visibility=club, club_id gesetzt) Nur Profile mit Mitgliedschaft in diesem club_id; optional zusätzlich Sparte, wenn division_id am Objekt gepflegt wird.
Privat Nur created_by (und explizite Shares später).
Geplante Einheiten Wie heute über Gruppe + Trainer/Co; zusätzlich Vereinskontext zur Navigation/Audit.

Einheitlicher Governance-Kern bleibt wie in CURR-005; Ergänzung: division_id auf Bibliotheksobjekten, wenn „Sparte“ technisch durchgesetzt werden soll (DOMAIN_MODEL / §17).

4.6 Membership-System (Vereinsabo / Limits) Konzept

Ziel: vereinszentrierte Vertrags- und Limitlogik, analog zur bestehenden Tier-Infrastuktur, aber an club_id gebunden.

Empfohlene Bausteine:

  1. club_plans Produktdefinition (Name, Features, implizite Limits).
  2. club_subscriptions (club_id, plan_id, Status, Laufzeit).
  3. club_usage_counters oder Ableitung aus DB z.B. aktive Nutzer, aktive Trainingsgruppen (periodisch oder on-write).
  4. Enforcement-Schicht zentrale Funktion assert_club_limit(club_id, metric) vor /groups-POST, Einladungen, etc.

Offene Produktentscheidungen (vor Implementierung festlegen):

  • Zählen „Nutzer“ alle Mitglieder oder nur aktive Trainer?
  • Soft-Limits vs. Hard-Stops; Übergang für Pilotverein.

5. Umsetzungsplan (Phasen)

Phase 0 Foundations (kurz, risikoarm)

  • Begriffe und Enums in einem Ort dokumentieren (dieses Dokument + Eintrag in DATABASE_SCHEMA.md nach Migration).
  • Audit-Liste aller Router mit club_id / visibility / Listen-Endpunkten.

Phase 1 Datenmodell Mitgliedschaft & Hauptverwalter

  • Migration: club_members, club_member_roles; optional clubs.primary_admin_profile_id (oder Primär-Flag auf Mitgliedschaft).
  • Backfill: bestehende Trainer aus training_groups → minimale Mitgliedschaft trainer im jeweiligen Verein (Skript/Migration mit dokumentierter Annahme CURR-008).
  • Breaking/API: keine nur erweiterte Datenbasis.

Phase 2 Aktiver Vereinskontext & API-Kontrakte

  • Backend: Validierung X-Active-Club-Id gegen Mitgliedschaft; Hilfsfunktion get_effective_club_context(session, header).
  • GET /api/clubs für Nicht-Systemadmins: nur Vereine mit Mitgliedschaft.
  • POST /api/clubs: nur Systemadmin; Vergabe club_admin-Mitgliedschaft.
  • Frontend: Club-Switcher + Persistenz.

Phase 3 Effektive Berechtigungen (RBAC)

  • Mitgliederverwaltung per API (ohne UI): GET/POST /api/clubs/{club_id}/members, GET/PUT/DELETE /api/clubs/{club_id}/members/{profile_id} — nur Plattform-Admin oder club_admin im Zielverein (Stand Code 0.8.16).
  • Zentrale Modulfunktion z.B. authorization/club_permissions.py:
    can(club_id, profile_id, permission, division_id=None) — optional später; aktuell club_tenancy.can_manage_club_org / has_club_role.
  • Router schrittweise umbinden: Sparten/Gruppen CRUD nach Rolle club_admin im Kontext; Systemadmin unverändert Vollzugriff.

Phase 4 Sichtbarkeit & Leaks schließen

  • Übungen: club-Sichtbarkeit nur bei Übereinstimmung exercise.club_id mit Mitgliedschaft (und später division).
  • Trainingsplan-Vorlagen (training_plan_templates) und Rahmenprogramme (training_framework_programs): gleiches Muster für Listen/GET (Stand 0.8.17); Schreiben weiterhin nur Ersteller oder Plattform-Admin.
  • Gleiches Muster für Progressionsgraphen, ggf. Medien (offen).
  • Tests: zwei Vereine, zwei Nutzer, keine Kreuzzugriffe.

Phase 5 Mitgliedschaft / Limits

  • Tabellen club_plans, club_subscriptions; Integration mit Enforcement vor relevanten Writes.
  • UI „Mein Verein“: Kennzahlenteaser oder Hinweise bei Limit (minimal).

Phase 6 Verfeinerung

  • Einladungsflow (E-Mail), Mehrfachrollen-UI, Audit-Log für Admin-Aktionen.
  • Optionale thematische Sperren (Karate vs. Gewaltschutz) als eigene Policy-Schicht.

6. Abhängigkeiten und Risiken

  • Übergang: Pilot mit einem Verein nutzt weiterhin einfache Defaults; Multi-Verein erfordert Pflicht aktiver Kontext.
  • Performance: Mitgliedschaft und Rolle sollten einmal pro Request geladen und gecacht werden (Request-Scope).
  • Konsistenz mit Mitai: Nutzer-Tiers können parallel bleiben; vereinsbezogene Limits sind die neue Quelle für Shinkan-spezifische Kaufmotive.

7. Nächste konkrete Artefakte

  1. TenantContext-Spezifikation & Endpoint-Audit (siehe ACCESS_LAYER_AND_GOVERNANCE_PLAN.md §6).
  2. Aktualisierung DATABASE_SCHEMA.md bei neuen Governance-/Scope-Feldern.
  3. Sicherheits-Review der list_*-Endpunkte mit club-Visibility (fortlaufend bis Governance vereinheitlicht).

8. Verwandtes Dokument

  • ACCESS_LAYER_AND_GOVERNANCE_PLAN.md verbindliche Umsetzungsstufen AF, einheitliche Zugriffsschicht, Scope-Erweiterung (division, später Community), Capability-Vorbereitung ohne Custom-Rollen-UI; Vereinsabo explizit zurückgestellt.

Letzte Aktualisierung: 2026-05-05