shinkan-jinkendo/.claude/docs/technical/CAPABILITY_CATALOG.v1.md
Lars 4130a63dfe
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 42s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 35s
Test Suite / playwright-tests (push) Successful in 1m51s
Implement Registry-First Approach for Rights and Capabilities Management
- Updated the capability catalog to reflect a registry-first approach, requiring modules to register rights and quotas upon implementation.
- Enhanced the backend to synchronize the rights registry with the database, ensuring only registered capabilities and features are displayed in the admin matrix.
- Modified SQL queries in the admin rights router to filter capabilities and features based on module registration.
- Updated documentation to clarify the new rights and features registry process, replacing the previous catalog-first method.
- Incremented application version to 0.8.201 and updated database schema version to 20260606084 to reflect these changes.
2026-06-07 15:36:31 +02:00

332 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# Capability-Katalog Shinkan v1
**Status:** Konzept (verbindliche Zieldefinition; M3 teilweise umgesetzt)
**Stand:** 2026-06-06
**Bezüge:** `ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` (Stufe E), `MULTI_TENANCY_RBAC_ARCHITECTURE.md`, `CLUB_MEMBERSHIP_AND_FEATURES.v1.md`, **`MEMBERSHIP_RBAC_DECISIONS_2026-06.md`** (Produktentscheidungen)
---
## 1. Zweck
Dieses Dokument definiert **benannte Capabilities** (Wer darf welche **Funktion** ausführen?) — getrennt von:
- **Governance** (Darf ich *dieses Objekt* lesen/ändern? → `visibility`, `club_id`, `created_by`)
- **Feature-Limits** (Wie viel darf der **Verein**? → `CLUB_MEMBERSHIP_AND_FEATURES.v1.md`)
Capabilities beantworten: *„Darf ein Trainer mit Rolle X die Funktion Y im Verein Z überhaupt nutzen?“*
---
## 2. Namenskonvention
```
{domain}.{action}[.{qualifier}]
```
| Segment | Beispiele |
|---------|-----------|
| `domain` | `exercises`, `media`, `planning`, `org`, `platform` |
| `action` | `read`, `create`, `update`, `delete`, `manage`, `execute` |
| `qualifier` | `ai.suggest`, `join_request`, `inbox.review` |
**CRUD-Mapping:**
| Aktion | Capability-Suffix | Bedeutung |
|--------|-------------------|-----------|
| Lesen (Listen/Detail) | `.read` | Navigation + API-Lesen erlaubt |
| Anlegen | `.create` | POST/INSERT |
| Bearbeiten | `.update` | PUT/PATCH (eigenes + berechtigtes Fremdes) |
| Löschen | `.delete` | DELETE (strenger als update) |
| Verwalten | `.manage` | Org-Funktionen, Freigaben, Mitglieder |
| Ausführen (ohne Persistenz) | `.execute` | z. B. KI-Vorschau, Coach-Lauf |
Objektbezogene Feinheiten (nur Ersteller, nur Vereinsadmin des Objekt-Vereins) bleiben in **Governance** — Capabilities sind das **Tür-Schloss** davor.
---
## 3. Account-Lifecycle (Voraussetzung für Capabilities)
| `account_state` | Bedingung | Typische Capabilities |
|-----------------|-----------|------------------------|
| `anonymous` | Keine Session | nur öffentliche Routen (`/login`, Rechtstexte, `clubs/public-directory`) |
| `unverified` | Session, `email_verified=false` | `account.resend_verification`, `account.logout` |
| `verified_pending_club` | Verifiziert, keine aktive `club_members` | `club.join_request`, `club.creation_request` (M7), `account.settings`**kein** Lesezugriff auf Domänen-Inhalte (siehe Entscheidungs-Doc §1.1) |
| `active_member` | Mind. eine aktive Vereinsmitgliedschaft | Domänen-Capabilities gemäß Vereinsrolle |
| `platform_admin` | `role``admin`, `superadmin` | `platform.*` zusätzlich |
**Regel:** Domänen-Capabilities (`exercises.*`, `planning.*`, …) erfordern mindestens `active_member`, sofern nicht `platform_admin`.
---
## 4. Rollen-Scopes
### 4.1 Portal-Rollen (`profiles.role`)
| Rolle | Scope | Kurz |
|-------|-------|------|
| `user` | Portal | Standard nach Registrierung (Zielbild; heute oft `trainer` Legacy) |
| `trainer` | Portal | Legacy — mittelfristig durch `user` + Vereinsrollen ersetzen |
| `admin` | Portal | Plattform-Admin (Vereine anlegen, erweiterte Ops) |
| `superadmin` | Portal | Vollzugriff Plattform + Superadmin-Werkzeuge |
### 4.2 Vereinsrollen (`club_member_roles.role_code`)
| Rolle | Fachlich |
|-------|----------|
| `club_admin` | Vereinsorganisation, Mitglieder, Struktur |
| `trainer` | Planung, Übungen, Durchführung |
| `content_editor` | Inhalte pflegen (Bibliothek) |
| `division_lead` | Spartenleitung (später division-scope) |
Mehrfachrollen pro Mitgliedschaft sind möglich (OR-Verknüpfung der Capabilities).
### 4.3 Mapping heutiger Helfer → Capabilities
| Heutiger Code (`club_tenancy.py`) | Ziel-Capability-Cluster |
|-----------------------------------|-------------------------|
| `can_manage_club_org` | `org.structure.manage`, `org.members.manage`, `org.inbox.review` |
| `can_plan_in_club` | `planning.*`, `exercises.create/update`, `modules.*`, `framework.*` |
| `is_platform_admin` | `platform.*` (Bypass Mandant, Audit-Pflicht) |
| `is_superadmin` | `platform.superadmin.*` |
---
## 5. Capability-Katalog (v1)
Legende Spalten:
- **Min. Account:** `verified_pending_club` | `active_member` | `platform_admin`
- **Vereinsrollen:** leer = alle aktiven Mitglieder; sonst mindestens eine Rolle
- **Feature-ID:** optionales Kontingent (siehe Club-Membership-Doc); leer = kein Limit
- **Governance:** zusätzliche Objektprüfung ja/nein
### 5.1 Account & Onboarding
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Endpoints / UI |
|---------------|--------------|---------------|------------|----------------|
| `account.settings.read` | `unverified` | — | — | `GET /profiles/me`, Einstellungen |
| `account.settings.update` | `unverified` | — | — | `PUT /profiles/{id}` (eigenes Profil) |
| `account.password.change` | `unverified` | — | — | `PUT /api/auth/pin` |
| `account.resend_verification` | `unverified` | — | — | `POST /api/auth/resend-verification` |
| `club.directory.read` | `verified_pending_club` | — | — | `GET /clubs/public-directory` |
| `club.join_request.create` | `verified_pending_club` | — | — | `POST /me/club-join-requests`, Registrierung mit `requested_club_id` |
| `club.join_request.withdraw` | `verified_pending_club` | — | — | `DELETE /me/club-join-requests/{id}` |
| `club.join_request.read_own` | `verified_pending_club` | — | — | `GET /me/club-join-requests` |
### 5.2 Organisation (Verein)
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Endpoints / UI |
|---------------|--------------|---------------|------------|----------------|
| `org.club.read` | `active_member` | * | — | `GET /clubs`, `GET /clubs/{id}` (eigene Vereine) |
| `org.club.create` | `platform_admin` | — | — | `POST /clubs` |
| `org.club.update` | `platform_admin` | `club_admin` | — | `PUT /clubs/{id}` |
| `org.club.delete` | `platform_admin` | — | — | `DELETE /clubs/{id}` |
| `org.structure.manage` | `active_member` | `club_admin` | `training_groups` | Sparten, Gruppen CRUD |
| `org.members.read` | `active_member` | `club_admin` | — | `GET /clubs/{id}/members` |
| `org.members.manage` | `active_member` | `club_admin` | `active_members` | POST/PUT/DELETE Mitglieder |
| `org.members.directory` | `active_member` | * | — | `GET /clubs/{id}/members/directory` (ohne E-Mail für Nicht-Admins) |
| `org.join_request.review` | `active_member` | `club_admin` | — | Join-Request accept/reject, Inbox |
| `org.inbox.read` | `active_member` | `club_admin` | — | Posteingang Join + Content-Reports |
### 5.3 Übungen
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `exercises.read` | `active_member` | * | — | ja (visibility) |
| `exercises.create` | `active_member` | `trainer`, `content_editor`, `club_admin`, `division_lead` | `exercises` | — |
| `exercises.update` | `active_member` | `trainer`, `content_editor`, `club_admin`, `division_lead` | — | ja |
| `exercises.delete` | `active_member` | `club_admin` (+ Ersteller privat) | — | ja |
| `exercises.bulk_metadata` | `active_member` | `content_editor`, `club_admin` | — | ja |
| `exercises.ai.suggest` | `active_member` | `trainer`, `content_editor`, `club_admin` | `ai_calls` | — |
| `exercises.ai.regenerate` | `active_member` | `trainer`, `content_editor`, `club_admin` | `ai_calls` | ja (edit) |
| `exercises.media.read` | `active_member` | * | — | ja |
| `exercises.media.upload` | `active_member` | `trainer`, `content_editor`, `club_admin` | `exercise_media` | ja |
| `exercises.variants.manage` | `active_member` | `trainer`, `content_editor`, `club_admin` | — | ja |
**Representative Endpoints:** `/api/exercises*`, `/api/exercises/ai/*`, Medien-Datei-Download.
### 5.4 Medien-Bibliothek (Archiv)
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `media.library.read` | `active_member` | * | — | ja |
| `media.library.upload` | `active_member` | `trainer`, `content_editor`, `club_admin` | `exercise_media` | ja |
| `media.library.update` | `active_member` | `trainer`, `content_editor`, `club_admin` | — | ja |
| `media.library.lifecycle` | `active_member` | `trainer`, `club_admin` | — | ja |
| `media.rights.declare` | `active_member` | `trainer`, `club_admin` | — | ja |
| `media.admin.rights_review` | `platform_admin` | — | — | Plattform-Admin Legacy-Review |
### 5.5 Trainingsmodule & Rahmenprogramme
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `modules.read` | `active_member` | * | — | ja |
| `modules.create` | `active_member` | `trainer`, `content_editor`, `club_admin` | `training_programs` | — |
| `modules.update` | `active_member` | `trainer`, `content_editor`, `club_admin` | — | ja |
| `modules.delete` | `active_member` | `club_admin` (+ Ersteller) | — | ja |
| `framework.read` | `active_member` | * | — | ja |
| `framework.create` | `active_member` | `trainer`, `club_admin` | `training_programs` | — |
| `framework.update` | `active_member` | `trainer`, `club_admin` | — | ja |
| `framework.delete` | `active_member` | `club_admin` (+ Ersteller) | — | ja |
| `plan_templates.read` | `active_member` | * | — | ja |
| `plan_templates.manage` | `active_member` | `trainer`, `club_admin` | — | ja |
### 5.6 Progressionspfade
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `progression.read` | `active_member` | * | — | ja |
| `progression.manage` | `active_member` | `trainer`, `content_editor`, `club_admin` | — | ja |
### 5.7 Planung & Durchführung
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `planning.calendar.read` | `active_member` | * | — | ja (Gruppe/Verein) |
| `planning.units.create` | `active_member` | `trainer`, `club_admin`, `division_lead` | `training_units` | ja |
| `planning.units.update` | `active_member` | `trainer`, `club_admin`, `division_lead` | — | ja |
| `planning.units.delete` | `active_member` | `club_admin`, `trainer` (eigene) | — | ja |
| `planning.units.run` | `active_member` | `trainer`, `club_admin`, `division_lead` | — | ja |
| `planning.coach.execute` | `active_member` | `trainer`, `club_admin` | — | ja |
| `planning.ai.suggest` | `active_member` | `trainer`, `club_admin` | `ai_calls` | — |
| `planning.ai.progression_path` | `active_member` | `trainer`, `club_admin` | `ai_calls` | — |
### 5.8 Fähigkeiten & Scoring
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID | Governance |
|---------------|--------------|---------------|------------|------------|
| `skills.catalog.read` | `active_member` | * | — | globaler Katalog |
| `skills.discovery.read` | `active_member` | `trainer`, `content_editor` | — | — |
| `skill_profiles.read` | `active_member` | * | — | ja (Artefakt) |
### 5.9 Governance & Meldungen
| Capability-ID | Min. Account | Vereinsrollen | Feature-ID |
|---------------|--------------|---------------|------------|
| `governance.content_report.create` | `active_member` | * | — |
| `governance.content_report.review` | `active_member` | `club_admin` | — |
| `governance.change_request.*` | `active_member` | `content_editor`, `club_admin` | — |
### 5.10 Plattform (nur Portal-Admin / Superadmin)
| Capability-ID | Min. Account | Portal-Rolle | Feature-ID |
|---------------|--------------|--------------|------------|
| `platform.admin.access` | `platform_admin` | `admin`, `superadmin` | — |
| `platform.users.manage` | `platform_admin` | `superadmin` | — |
| `platform.catalogs.manage` | `platform_admin` | `superadmin` | — |
| `platform.maturity_models.manage` | `platform_admin` | `superadmin` | — |
| `platform.wiki_import.execute` | `platform_admin` | `superadmin` | `wiki_import` |
| `platform.ai_prompts.manage` | `platform_admin` | `superadmin` | — |
| `platform.exercise_enrichment.execute` | `platform_admin` | `superadmin` | `ai_calls` |
| `platform.user_content.moderate` | `platform_admin` | `superadmin` | — |
| `platform.legal_documents.manage` | `platform_admin` | `superadmin` | — |
| `platform.media_storage.manage` | `platform_admin` | `superadmin` | — |
| `platform.club_creation.approve` | `platform_admin` | `superadmin` | — |
*Geplant:* `club.creation_request.submit``verified_pending_club`; Freigabe über `platform.club_creation.approve`.
---
## 6. Standard-Zuordnung Vereinsrolle → Capabilities (v1, fest)
Diese Tabelle ist die **initiale** Grant-Matrix (`club_role_capability_grants`). Später durch Custom Roles ersetzbar — gleiche Capability-IDs.
| Capability-Cluster | `club_admin` | `trainer` | `content_editor` | `division_lead` |
|--------------------|:------------:|:---------:|:----------------:|:---------------:|
| `org.structure.manage` | ✓ | — | — | ✓ (eigene Sparte, später) |
| `org.members.manage` | ✓ | — | — | — |
| `org.join_request.review` | ✓ | — | — | — |
| `exercises.read` | ✓ | ✓ | ✓ | ✓ |
| `exercises.create/update` | ✓ | ✓ | ✓ | ✓ |
| `exercises.delete` | ✓ | — | — | — |
| `exercises.ai.*` | ✓ | ✓ | ✓ | ✓ |
| `media.library.*` | ✓ | ✓ | ✓ | ✓ |
| `modules.*` / `framework.*` | ✓ | ✓ | ✓ | ✓ |
| `planning.*` | ✓ | ✓ | — | ✓ |
| `planning.coach.execute` | ✓ | ✓ | — | ✓ |
| `governance.content_report.review` | ✓ | — | — | — |
---
## 7. API-Vertrag (Ziel)
### 7.1 Effektive Rechte für Frontend
```
GET /api/me/entitlements?club_id={optional}
```
Antwort (Ausschnitt):
```json
{
"account_state": "active_member",
"portal_role": "user",
"club_id": 12,
"club_roles": ["trainer"],
"capabilities": {
"exercises.read": true,
"exercises.ai.suggest": true,
"org.members.manage": false
},
"features": {
"ai_calls": { "allowed": true, "used": 4, "limit": 50, "remaining": 46, "reset_at": "2026-07-01T00:00:00Z" }
}
}
```
Frontend: Navigation und Buttons nur aus dieser Antwort — **keine** duplizierten Rollen-Checks in JSX (Ausnahme: rein kosmetische Labels).
### 7.2 Backend-Enforcement
Zentral (Zielmodul `authorization/capabilities.py` oder Erweiterung `club_tenancy.py`):
```python
assert_capability(tenant, "exercises.ai.suggest", club_id=tenant.effective_club_id)
assert_club_feature(tenant, "ai_calls", club_id=tenant.effective_club_id) # siehe Club-Membership-Doc
# + bestehende Governance auf Objekt-Ebene
```
---
## 8. Implementierungsreihenfolge (Capabilities)
| Phase | Inhalt |
|-------|--------|
| C0 | Account-Gates (`unverified`, `verified_pending_club`) — ohne Capability-DB |
| C1 | `capabilities` + `club_role_capability_grants` seed aus §56 |
| C2 | `GET /api/me/entitlements` + Frontend-Nav |
| C3 | Enforcement: KI-Endpoints, `exercises.create`, `planning.*` |
| C4 | Restliche Router schrittweise; Audit in `ACCESS_LAYER_ENDPOINT_AUDIT.md` |
| C5 | Custom Roles (optional) — gleiche IDs |
---
## 9. Abgrenzung & Drift-Schutz
1. **Neue Nutzerfunktion**`register_capability()` in `rights_registrations/<modul>.py`, dann Endpoint mit `probe_capability`. Namenskonvention hier dokumentieren — **kein** Bulk-Seed in Migrationen.
2. **Kontingent**`register_feature()` im selben Modul; Consume über `consume_club_feature_with_usage`.
3. **Kein** paralleles `if (user.role === 'trainer')` für Sicherheit — nur UX-Fallback.
4. Capability ≠ Feature: `exercises.ai.suggest` (darf ich?) vs. `ai_calls` (wie viel übrig?).
5. Plattform-Admin-Bypass dokumentieren und auditieren (`platform_admin` sieht Mandant, nicht automatisch alle Quotas).
Siehe **`docs/working/RIGHTS_AND_FEATURES_REGISTRY.md`** (Registry-first, ersetzt Katalog-first aus 079).
---
## 10. Referenzen
| Dokument | Inhalt |
|----------|--------|
| `CLUB_MEMBERSHIP_AND_FEATURES.v1.md` | Vereinsabo, Feature-Registry, Kontingente |
| `ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` | TenantContext, Governance, Stufe E |
| `MULTI_TENANCY_RBAC_ARCHITECTURE.md` | §4.6 Vereinsabo-Zielbild |
| `ACCESS_LAYER_ENDPOINT_AUDIT.md` | Endpoint-Pflege |
| Mitai `FEATURE_ENFORCEMENT.md` | 4-Phasen-Rollout-Vorbild |
---
**Changelog**
- 2026-06-06: v1 — Initial-Katalog aus Ist-Code (`club_tenancy`, Router-Inventar) + Ziel-Onboarding.