All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 43s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m13s
- Updated the Membership RBAC Decisions document to reflect the latest implementation status and roadmap, including new features and enhancements. - Incremented application version to 0.8.200 and updated database schema version to 20260606083. - Added a new API endpoint to clear capability grants for club roles, improving admin rights management. - Enhanced the Admin Rights page in the frontend to display enforcement status and feature consumption details for capabilities. - Improved the user interface for better clarity on rights and capabilities management.
240 lines
9.8 KiB
Markdown
240 lines
9.8 KiB
Markdown
# Membership, RBAC & Kontingente — Produktentscheidungen
|
||
|
||
**Status:** Verbindlich (Zielbild & Roadmap-Priorisierung)
|
||
**Stand:** 2026-06-06
|
||
**Bezüge:** `CAPABILITY_CATALOG.v1.md`, `CLUB_MEMBERSHIP_AND_FEATURES.v1.md`, `ACCESS_LAYER_AND_GOVERNANCE_PLAN.md`
|
||
|
||
Dieses Dokument hält **getroffene Produktentscheidungen** fest (Session 2026-06-06) und ergänzt die v1-Konzept-Specs um Umsetzungsrichtung. Technischer Implementierungsstand: Abschnitt 2.
|
||
|
||
---
|
||
|
||
## 1. Getroffene Entscheidungen
|
||
|
||
### 1.1 Onboarding: `verified_pending_club`
|
||
|
||
Nutzer **ohne aktive Vereinsmitgliedschaft** (E-Mail verifiziert) dürfen **nur**:
|
||
|
||
| Erlaubt | Nicht erlaubt (Zielbild) |
|
||
|---------|---------------------------|
|
||
| Konto / Einstellungen | Übungen, Planung, KI, Medien |
|
||
| Vereinsverzeichnis lesen | Vereinsinterne Inhalte (`club`), private Fremdinhalte |
|
||
| **Beitrittsantrag** an bestehenden Verein | Vollzugriff auf Bibliothek / offizielle Inhalte (Lesen) — **bewusst gesperrt** bis Mitgliedschaft |
|
||
| **Vereinsgründung beantragen** (Prozess M7, Superadmin-Freigabe) | |
|
||
|
||
**Kein** „Bibliothek durchstöbern“ für Bewerber — reduziert Datenexposition und vereinfacht UX („erst Verein, dann Arbeit“).
|
||
|
||
Technischer Zustand: `account_state = verified_pending_club` (siehe `CAPABILITY_CATALOG.v1.md` §3).
|
||
|
||
---
|
||
|
||
### 1.2 Rollenmodell: Risikoarm statt Big-Bang
|
||
|
||
**Zielbild (langfristig):**
|
||
|
||
- **Fest:** nur `superadmin` (Plattform) als nicht konfigurierbare Systemrolle.
|
||
- **Dynamisch konfigurierbar:** alle Vereinsrollen und deren Capability-Bundles (später `club_custom_roles`).
|
||
- Optional: `admin` (Plattform) als abgeschwächter Portal-Admin bleibt vorerst bestehen (Ist-Code).
|
||
|
||
**Entscheidung v1 (risikoarm):**
|
||
|
||
| Maßnahme | Jetzt | Später |
|
||
|----------|-------|--------|
|
||
| Alte Helfer (`can_plan_in_club`, `if (club_admin)` in JSX) | **Behalten** — weiter produktiv | Schrittweise durch `entitlements` ersetzen |
|
||
| Neue Endpoints / Features | Nur über **Capability-IDs** + Audit | — |
|
||
| Neue Vereinsrollen | Als **Systemrollen** ergänzen (z. B. `co_trainer`) | Custom Roles UI |
|
||
| `club_custom_roles` | **Nicht** in v1 | v2 Epic |
|
||
|
||
**Begründung:** Backend und Frontend haben hunderte Verdrahtungen auf `trainer` / `club_admin` / Plattform-Rollen. Parallelbetrieb Capability-System + Legacy-Helfer ist sicherer als einmaliges Aufbrechen.
|
||
|
||
**Co-Trainer (geplant als Systemrolle):** weniger Capabilities als `trainer` (z. B. kein `planning.*`, kein `exercises.create`) — Umsetzung nach Onboarding-Gates + Entitlements-Rollout, nicht vorher.
|
||
|
||
---
|
||
|
||
### 1.3 Vereins-Kontingente (Membership-Pakete)
|
||
|
||
**Jetzt:** Schema und Anzeige vorbereiten; **keine** detaillierte Paket-Logik (z. B. „3 Trainer + 10 Co-Trainer“) implementieren.
|
||
|
||
| Vorbereitet (DB/Module) | Bewusst zurückgestellt |
|
||
|-------------------------|-------------------------|
|
||
| `features`, `club_plans`, `club_subscriptions` | Eigene Feature-IDs `trainer_seats` / `co_trainer_seats` |
|
||
| Bestands-Limits (`exercises`, `training_groups`, `ai_calls`, …) | Zählregel „nur planungsberechtigte Mitglieder“ vs. alle Mitglieder |
|
||
| `GET /me/entitlements` Feature-Teil | Stripe / Rechnung (M8) |
|
||
|
||
**Prinzip:** Neue Kontingent-Typen = neue `features`-Zeile + Plan-Limits + optional Capability-`linked_feature_id` — ohne Schema-Bruch.
|
||
|
||
---
|
||
|
||
### 1.4 Trainer-Budget innerhalb Vereins-Kontingent (v2)
|
||
|
||
**Anforderung:** Vereins-KI-Kontingent liegt beim Verein; **Vereinsadmin** kann pro Trainer ein **Sub-Budget** vergeben (Fairness, „Kontingent-Fresser“).
|
||
|
||
**Entscheidung:**
|
||
|
||
- v1: nur **Vereins-Ebene** (`club_plan_limits`, `club_feature_usage`).
|
||
- v2: neue Tabellen (Skizze):
|
||
|
||
```sql
|
||
-- Skizze — noch nicht migriert
|
||
club_member_feature_budgets (club_id, profile_id, feature_id, limit_value, …)
|
||
club_member_feature_usage (club_id, profile_id, feature_id, usage_count, reset_at, …)
|
||
```
|
||
|
||
**Prüf-Kette v2:** Capability → Mitglieds-Budget (falls gesetzt) → Vereins-Kontingent.
|
||
|
||
**Fairness-Modell (offen, Tendenz):** harte Sub-Budgets (Modell A) — Trainer darf sein Budget nicht überschreiten, auch wenn Verein noch Rest hat.
|
||
|
||
---
|
||
|
||
### 1.5 Enforcement-Phasen (unverändert, bestätigt)
|
||
|
||
| Phase | Verhalten | Nutzer sichtbar |
|
||
|-------|-----------|-----------------|
|
||
| 2 (M2/M3) | JSON-Log, kein Block | Nein (außer Logs) |
|
||
| 3 (M4) | `GET /me/entitlements` + Badge | Kontingent-Anzeige |
|
||
| 4 (M5+) | HTTP 403 + `increment` | Hard-Block |
|
||
|
||
Env-Schalter: `ACCOUNT_GATE_ENFORCE` (Default `1`, Endpoint-Helfer), `ACCOUNT_GATE_API_ENFORCE` (Default `1`, API-Middleware Phase A), `CAPABILITY_ENFORCE` / `CLUB_FEATURE_ENFORCE` (Default `0`).
|
||
|
||
---
|
||
|
||
## 2. Implementierungsstand (Ist, Codebase)
|
||
|
||
**DB-Schema:** `20260606083` · App **0.8.199** (`backend/version.py`)
|
||
**Roadmap (detailliert):** `docs/working/RBAC_ENFORCEMENT_ROADMAP.md`
|
||
|
||
### M1 — Feature-Schema v9c ✅
|
||
|
||
| Deliverable | Status |
|
||
|-------------|--------|
|
||
| Migration `078_club_features_and_plans.sql` | ✅ |
|
||
| Legacy `001` archiviert | ✅ |
|
||
| `club_plans`, `club_subscriptions`, Usage-Tabellen | ✅ |
|
||
| Seed Features + Pläne (`free`, …) | ✅ |
|
||
| `club_features.py`: `check_club_feature_access`, `get_effective_club_plan` | ✅ |
|
||
| Backfill Vereine → Plan `free` | ✅ |
|
||
|
||
### M2 — Feature-Probe (Log only) ✅
|
||
|
||
| Deliverable | Status |
|
||
|-------------|--------|
|
||
| `club_feature_logger.py` → `club-feature-usage.log` | ✅ |
|
||
| `probe_club_feature_access()` | ✅ |
|
||
| Hooks: KI-Endpoints, `POST /exercises`, Medien-Upload, Planungs-KI | ✅ |
|
||
| Consume-Standard + `feature_usage` in Response (`ai_calls`) | ✅ |
|
||
| `CLUB_FEATURE_ENFORCE=0` (Default) | ✅ |
|
||
|
||
### M3 — Account-Lifecycle + Capability-Grants ⚠️ teilweise
|
||
|
||
| Deliverable | Status | Lücke |
|
||
|-------------|--------|-------|
|
||
| Migration `079_capabilities.sql` + Seed | ✅ | — |
|
||
| `account_lifecycle.py`, `resolve_account_state` | ✅ | — |
|
||
| `capabilities.py`, `check_capability`, `probe_capability` | ✅ | — |
|
||
| `TenantContext.account_state` | ✅ | — |
|
||
| `GET /profiles/me` → `account_state`, `club_roles` | ✅ | — |
|
||
| Account-Gates auf **Schreib-/KI-Endpoints** | ✅ | Lesepfade für Bewerber noch offen |
|
||
| `CAPABILITY_ENFORCE=0` (nur Log) | ✅ | — |
|
||
| Onboarding UX: nur Bewerbung/Gründung | ✅ | Phase A: API-Middleware + `/onboarding` + reduzierte Nav |
|
||
| `club_creation_requests` (M7) | ✅ Basis | Capabilities + Admin-Freigabe |
|
||
| Quota-Bypass via Capability-Grants (083) | ✅ | kein paralleles Exemption-Schema |
|
||
| Custom Roles / Co-Trainer | ❌ | bewusst v2 |
|
||
| Legacy-Helfer entfernt | ❌ | bewusst parallel |
|
||
|
||
### M4 — Anzeige ✅ teilweise
|
||
|
||
| Deliverable | Status |
|
||
|-------------|--------|
|
||
| `GET /api/me/entitlements` | ✅ |
|
||
| `EntitlementsContext`, `hasCapability()` | ✅ (UI nutzt noch kaum) |
|
||
| `FeatureUsageBadge` | ✅ nur KI im Übungsformular |
|
||
| `featureUsageSync` in `request()` | ✅ |
|
||
|
||
### M5 — Hard-Block + vollständiger Verbrauch ⚠️
|
||
|
||
| Deliverable | Status |
|
||
|-------------|--------|
|
||
| `consume_club_feature_with_usage` Standard | ✅ `ai_calls` |
|
||
| `CLUB_FEATURE_ENFORCE=1` produktiv | ❌ Default 0 |
|
||
| Consume `exercises`, `exercise_media`, … | ❌ |
|
||
|
||
### M6 — Admin UI Rollen & Rechte ⚠️
|
||
|
||
| Deliverable | Status |
|
||
|-------------|--------|
|
||
| `/admin/rights` Capability-Matrix (Portal + Verein) | ✅ |
|
||
| Klartext zuerst, Enforcement-Badge | ✅ 2026-06-07 |
|
||
| Kontingent-Bypass + Vereinspläne (Seed) | ✅ |
|
||
| Neue Pläne / Rollen anlegen (CRUD) | ❌ |
|
||
|
||
### Bewusst zurückgestellt
|
||
|
||
| ID | Inhalt |
|
||
|----|--------|
|
||
| M0 | CI-Isolation / Test-DB |
|
||
| M8 | Stripe |
|
||
| v2 | Trainer-Budgets, Custom Roles |
|
||
|
||
---
|
||
|
||
## 3. Architektur-Zielbild (kompakt)
|
||
|
||
```
|
||
Request
|
||
→ require_auth
|
||
→ account_state (Gate)
|
||
→ TenantContext
|
||
→ assert_capability (Rolle / Funktion)
|
||
→ check_club_feature_access (Vereins-Kontingent)
|
||
→ [v2] member_feature_budget (Trainer-Budget)
|
||
→ Governance (Objekt)
|
||
```
|
||
|
||
**Drei Achsen:** Account-Lifecycle · Capabilities · Features (Kontingente). Governance bleibt vierte Prüfung.
|
||
|
||
---
|
||
|
||
## 4. Empfohlene Roadmap (nach Entscheidungen)
|
||
|
||
| Phase | Paket | Warum zuerst |
|
||
|-------|--------|--------------|
|
||
| **A** | **Onboarding-Gates vollständig** | ✅ umgesetzt (API + Frontend `/onboarding`) |
|
||
| **B** | **M7 Vereinsgründung beantragen** | **Als Nächstes** — zweiter Pfad für `verified_pending_club` |
|
||
| **C** | **M5 Hard-Block `ai_calls`** | Free-Plan `0` wird real; Badge (M4) liefert Erklärung |
|
||
| **D** | **M6 voll** | Pläne-CRUD, Rollen-CRUD | ⚠️ Matrix da |
|
||
| **E** | Entitlements im Frontend (`hasCapability`) | Entscheidung 1.2 risikoarm |
|
||
| **F** | `co_trainer` + Member-Budgets (v2) | Entscheidung 1.4 |
|
||
|
||
M0 parallel, nicht blockierend.
|
||
|
||
---
|
||
|
||
## 5. Offene Punkte (vor M6 / v2)
|
||
|
||
1. Fairness Modell A/B/C für Trainer-Budget (Tendenz: A).
|
||
2. Ob `admin` (Portal) langfristig neben `superadmin` bleibt.
|
||
3. Ob offizielle Inhalte für Bewerber **nie** lesbar bleiben (aktuell: ja).
|
||
|
||
---
|
||
|
||
## 6. Referenzen
|
||
|
||
| Pfad | Inhalt |
|
||
|------|--------|
|
||
| `CAPABILITY_CATALOG.v1.md` | Capability-IDs, Account-States |
|
||
| `CLUB_MEMBERSHIP_AND_FEATURES.v1.md` | Feature-Registry, Kontingente |
|
||
| `backend/club_features.py` | Vereins-Features |
|
||
| `backend/capabilities.py` | Capability-Auflösung |
|
||
| `backend/account_lifecycle.py` | Account-Gates |
|
||
|
||
## 7. Superadmin im Verein (FAQ)
|
||
|
||
Siehe **`docs/working/RBAC_ENFORCEMENT_ROADMAP.md` §4**: Plattform-Admin (`admin`, `superadmin`) erhält **Capability-Bypass** für Vereins-Funktionen ohne `club_admin`-Mitgliedschaft. Mandant über aktiven Verein wählen; Kontingente via Bypass. Einzelne Legacy-Pfade (z. B. Löschen `visibility=club`) sind noch nicht vereinheitlicht — Ziel Phase 3.
|
||
|
||
---
|
||
|
||
**Changelog**
|
||
|
||
- 2026-06-06: Initial — Entscheidungen Onboarding, Rollen-Risiko, Kontingente, Trainer-Budget v2; Ist-Stand M1–M3; Roadmap A–F.
|
||
- 2026-06-06: Phase A — `account_onboarding_gate.py`, Frontend `/onboarding`, reduzierte Navigation.
|
||
- 2026-06-07: M4–M6 Ist-Stand, Roadmap-Verweis, Superadmin-FAQ; Admin-Matrix UX + Enforcement-Audit.
|