- Introduced tenant context resolution in the profiles API, allowing for effective club identification based on user memberships. - Updated the `GET /profiles/me` endpoint to return `effective_club_id` and removed reliance on the deprecated `X-Active-Club-Id` header. - Bumped application version to 0.8.22 in both backend and frontend files. - Enhanced changelog to document the new version and changes made in this release.
13 KiB
Multi-Tenancy, Vereins-Membership und Rollenmodell – Zielarchitektur & Umsetzungsplan
Status: verbindliche Zielrichtung (Architekturpapier)
Stand: 2026-05-05
Bezug: functional/shinkan_anforderungsdokument_entwurf.md §5, §17–18 · functional/DOMAIN_MODEL.md (Sichtbarkeit §5.5) · functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md (CURR-004–008)
Operative Reihenfolge & einheitliche Zugriffsschicht: ACCESS_LAYER_AND_GOVERNANCE_PLAN.md – dort sind Stufen A–F, 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; technische Durchsetzung ist noch lückenhaft |
| §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-004–008: 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 bereits skizziert. Es fehlt die stringente technische Schicht: Vereinszugehörigkeit, aktiver Vereinskontext, effektive Berechtigungen pro Anfrage und konsequente Filterung bei club-sichtbaren Objekten.
3. Ist-Stand im Code (Gap-Analyse)
Hinweis: Dieser Abschnitt beschreibt den Ausgangspunkt vor Ausbauschritten (Mitgliedschaften, gefilterte Vereinsliste, Teilen von Governance für Übungen/Rahmen/Planung sind bereits angegangen). Verbindliche offene Arbeit und Reihenfolge sind im Dokument
ACCESS_LAYER_AND_GOVERNANCE_PLAN.mdfestgehalten.
3.1 Identität und Rollen
profiles.roleist eine globale Kennzeichnung (admin,superadmin,trainer,user, …).- Keine Tabelle für Vereinsmitgliedschaft mit Mehrfachrollen pro Verein.
- Sessions liefern nur
profile_id+ globalerole(auth.py→get_session).
Konsequenz: Mehrere Vereine mit unterschiedlichen Rollen pro User sind nicht modelliert; ein „Vereinsadmin“ kann nicht sauber von einem reinen Trainer unterschieden werden, sobald beides nur über profiles.role laufen soll.
3.2 Organisation & APIs
clubs,divisions,training_groupsexistieren (002_organization.sql).GET /api/clubslistiert alle Vereine für jeden eingeloggten Nutzer.POST /api/clubserlaubt Anlage fürtrainerunduser– nicht nur Systemadmin.- Sparten/Gruppen: Schreibzugriff über globale
admin/superadmin, nicht über Vereinsadmin im Kontext „sein Verein“.
Konsequenz: Weder Datenisolation noch Produktdifferenzierung „nur Systemadmin legt Verein an“ sind umgesetzt.
3.3 Trainingsplanung
- Zugriff auf Einheiten gruppenbasiert: Trainer/Co-Trainer der
training_groups, pluslead_trainer_profile_id(Migration/Pfadtraining_planning). _assert_club_visible_for_trainerbindet Vereinssicht für Teile der Planung an „aktive Gruppe als Trainer/Co im Verein“ – kein generelles Mitgliedschaftsmodell.
Konsequenz: Planung ist gruppenzentriert, nicht mitgliedschaftszentriert; Vereinsweite Aufgaben des Vereinsadmins fehlen als konsistentes Recht.
3.4 Governance / Sichtbarkeit (kritisch)
- Übungen (
list_exercises): Bedingung sinngemäß „official OR club OR created_by = ich“ –clubgilt für alle Mandanten, ohne Prüfungexercise.club_id∈ Vereine des Nutzers. - Detailzugriff
private: nur Owner – ok. - Rahmenprogramme (
training_framework_programs): Lesen fremder Rahmen übervisibility=clubist in_framework_accessnicht gelöst (faktisch stark creator-basiert für Nicht-Admins).
Konsequenz: Cross-Tenant-Leaks bei als club markierten Bibliotheksobjekten sind möglich bzw. Leselogik ist inkonsistent zwischen Modulen.
3.5 Frontend
- Stand 2026-05:
GET /api/profiles/meliefertclubs[],active_club_id; Frontend setztX-Active-Club-Id. Details und Pflicht zur serverseitigen TenantContext-Validierung sieheACCESS_LAYER_AND_GOVERNANCE_PLAN.md.
3.6 Membership (kommerziell/limits)
- Mitai-artige Tabellen (
tiers,subscriptions,tier_limits, …) sind nutzerbezogen, nicht vereinsbezogen. - Kein konzipiertes
club_subscription/club_planim Schema.
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_idoptional 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_idundco_trainer_ids(und ggf.lead_trainer_profile_idauf 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, optionaldivision_idfür spartenbezogene Rollen). -
Aktiver Verein:
- Persistenz: Nutzereinstellung (
profiles.default_club_idoder eigene Tabelleprofile_preferences). - Pro Request: Header
X-Active-Club-Idoder Query (einheitlich dokumentieren); Server validiert Mitgliedschaft.
- Persistenz: Nutzereinstellung (
-
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:
club_plans– Produktdefinition (Name, Features, implizite Limits).club_subscriptions– (club_id,plan_id, Status, Laufzeit).club_usage_countersoder Ableitung aus DB – z. B. aktive Nutzer, aktive Trainingsgruppen (periodisch oder on-write).- 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.mdnach Migration). - Audit-Liste aller Router mit
club_id/visibility/ Listen-Endpunkten.
Phase 1 – Datenmodell Mitgliedschaft & Hauptverwalter
- Migration:
club_members,club_member_roles; optionalclubs.primary_admin_profile_id(oder Primär-Flag auf Mitgliedschaft). - Backfill: bestehende Trainer aus
training_groups→ minimale Mitgliedschafttrainerim 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-Idgegen Mitgliedschaft; Hilfsfunktionget_effective_club_context(session, header). GET /api/clubsfür Nicht-Systemadmins: nur Vereine mit Mitgliedschaft.POST /api/clubs: nur Systemadmin; Vergabeclub_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 oderclub_adminim 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; aktuellclub_tenancy.can_manage_club_org/has_club_role. - Router schrittweise umbinden: Sparten/Gruppen CRUD nach Rolle
club_adminim Kontext; Systemadmin unverändert Vollzugriff.
Phase 4 – Sichtbarkeit & Leaks schließen
- Übungen:
club-Sichtbarkeit nur bei Übereinstimmungexercise.club_idmit Mitgliedschaft (und späterdivision). - 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
- TenantContext-Spezifikation & Endpoint-Audit (siehe
ACCESS_LAYER_AND_GOVERNANCE_PLAN.md§6). - Aktualisierung
DATABASE_SCHEMA.mdbei neuen Governance-/Scope-Feldern. - Sicherheits-Review der
list_*-Endpunkte mitclub-Visibility (fortlaufend bis Governance vereinheitlicht).
8. Verwandtes Dokument
ACCESS_LAYER_AND_GOVERNANCE_PLAN.md– verbindliche Umsetzungsstufen A–F, einheitliche Zugriffsschicht, Scope-Erweiterung (division, später Community), Capability-Vorbereitung ohne Custom-Rollen-UI; Vereinsabo explizit zurückgestellt.
Letzte Aktualisierung: 2026-05-05