shinkan-jinkendo/.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.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

236 lines
15 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`](./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`](./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.py` → `get_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.
- **`CAPABILITY_CATALOG.v1.md`** Rollen, Capability-IDs, Account-Lifecycle, Endpoint-Mapping.
- **`CLUB_MEMBERSHIP_AND_FEATURES.v1.md`** Vereinsabo, Feature-Registry (Mitai-v9c-Pattern), Kontingente.
---
**Letzte Aktualisierung:** 2026-06-06