shinkan-jinkendo/docs/jinkendo-family/design-principles/ACCESS_LAYER_DESIGN_PRINCIPLES.md
Lars 6c7c24e887
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Test Suite / pytest-backend (push) Successful in 45s
Test Suite / lint-backend (push) Successful in 1s
Test Suite / build-frontend (push) Successful in 15s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m35s
Add Jinkendo family design principles and entitlement model docs.
Document cross-app architecture patterns, Mitai alignment, and family entitlement standards. Documentation only; no runtime changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 09:12:48 +02:00

171 lines
7.8 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.

# Access Layer & Tenant Designprinzipien (Extraktion)
**Status:** Analyse / Arbeitspapier
**Stand:** 2026-07-04
**Geltungsbereich:** Modul „Access Layer & Tenant Governance“ — keine Shinkan-Gesamtarchitektur, keine Kampfsport-Domänenlogik
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 1 von 15
**Mitai-Vergleich:** kein direktes Gegenstück (Mitai ist profil-zentriert, kein Vereins-Mandant)
**Kernkomponenten:**
| Bereich | Pfade |
|---------|-------|
| TenantContext | `backend/tenant_context.py` |
| Governance-Helfer | `backend/club_tenancy.py` |
| Listenfilter SQL | `library_content_visibility_sql()` in `tenant_context.py` |
| Endpoint-Audit | `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` |
| Cursor-Regel | `.cursor/rules/access-layer.mdc` |
| Heuristik-Check | `backend/scripts/check_access_layer_hints.py` |
| Normative Spec | `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` |
---
## Modul
**Access Layer & Tenant Governance**
Zentraler Querschnitt für Mandanten-Kontext (`club_id`), Sichtbarkeit (`private`/`club`/`official`) und einheitliche Les-/Schreibregeln für Bibliotheksartefakte (Übungen, Medien, Rahmenprogramme, Vorlagen, Progressionsgraphen).
---
## Fachliche Verantwortung
1. **TenantContext pro HTTP-Request** — Auflösung aus Session + Header `X-Active-Club-Id` + Profilfeld `active_club_id`.
2. **Datenisolierung**`club_id` als Grenze für vereinsgeteilte Inhalte; Cross-Verein ausgeschlossen.
3. **Einheitliche Sichtbarkeits-Semantik** — gleiche Enums und Prüflogik über alle Bibliotheksmodule.
4. **Governance-Transitionen** — Regeln beim Wechsel `private``club``official` und beim Löschen.
5. **Listenfilter** — SQL-Baustein statt „SELECT *“ in jedem Router.
### Administrierte Konfigurationen
| Konfiguration | Speicherort | Inhalt |
|---------------|-------------|--------|
| Aktiver Verein | `profiles.active_club_id` | Persistierter UI-Kontext |
| Request-Override | Header `X-Active-Club-Id` | Client-seitiger Mandantenwechsel |
| Sichtbarkeit | Spalte `visibility` je Objekt | `private`, `club`, `official` |
| Vereinszuordnung | Spalte `club_id` | Pflicht bei `club`-Inhalten |
### Bewusst nicht hardcodiert
- Welche Objekte welchen Verein haben (Daten)
- Individuelle Freigabeentscheidungen (Workflow)
### Hardcodiert (Code)
- Enum-Werte und Leseregeln in `club_tenancy.py` / `library_content_visibility_sql`
- Plattform-Admin-Ausnahmen (`is_platform_admin`, `is_superadmin`)
- Rollencodes für Schreib-/Löschregeln (`club_admin`, `trainer`, …)
---
## Designprinzipien
### 1. Ein Mandant pro Request (`TenantContext`)
| | |
|---|---|
| **Prinzip** | `Depends(get_tenant_context)` liefert `profile_id`, `global_role`, `effective_club_id`, Mitgliedschaften — einmal pro Request. |
| **Begründung** | Kein verteiltes „Rates“ aus Headers; konsistente Filter in allen Routern. |
| **Quelle** | `tenant_context.py`; `ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` §2 |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Nicht alle Endpoints migriert; Audit-Tabelle zeigt Restbestand mit nur `require_auth`. |
### 2. `club_id` als Datenisolierungsgrenze
| | |
|---|---|
| **Prinzip** | Vereinsgeteilte Inhalte sind nur für aktive Mitglieder des Objekt-`club_id` lesbar — nie Cross-Verein. |
| **Begründung** | Mandantenfähigkeit für Vereinsplattform; Compliance bei geteilten Trainingsinhalten. |
| **Quelle** | `library_content_visibility_sql()`; Tests `test_access_layer*.py` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | `division`-Verschärfung noch nicht durchgängig; reserviert im Plan. |
### 3. Einheitliche Visibility-Semantik
| | |
|---|---|
| **Prinzip** | `private` \| `club` \| `official` mit gleicher Bedeutung für Übungen, Medien, Rahmen, Module, Graphen. |
| **Begründung** | Nutzer und Trainer verstehen ein Freigabemodell; UI-Feld „Freigabelevel“ durchgängig. |
| **Quelle** | `club_tenancy.py`; `FACHLICHE_NUTZERFUNKTIONEN.md` §4.7 |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | `community`-Stufe nur dokumentiert, nicht implementiert. |
### 4. Zentraler SQL-Filter für Bibliothekslisten
| | |
|---|---|
| **Prinzip** | Listen nutzen `library_content_visibility_sql(alias, profile_id, role, effective_club_id)` — nicht handgeschriebene WHERE-Kopien. |
| **Begründung** | Drift-Vermeidung; ein Fix gilt für alle Kataloge. |
| **Quelle** | `tenant_context.py`; Router `exercises.py`, `training_framework_programs.py`, … |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Einzelne Legacy-Queries können noch abweichen. |
### 5. Governance-Transitionen explizit prüfen
| | |
|---|---|
| **Prinzip** | Wechsel von `visibility`/`club_id` über `assert_library_content_governance_transition` + `assert_valid_governance_visibility`. |
| **Begründung** | „Privat → Verein teilen“ und „official herabstufen“ sind sicherheitsrelevante Aktionen. |
| **Quelle** | `club_tenancy.py` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Nicht jedes Modul ruft Transition-Helper bei PATCH auf. |
### 6. Löschregeln nach Visibility-Stufe
| | |
|---|---|
| **Prinzip** | `assert_library_content_deletable`: privat → Ersteller/Vereinsadmin-Kontext; club → Vereinsadmin; official → Plattform-Admin. |
| **Begründung** | Schutz vor versehentlichem Löschen fremder oder offizieller Inhalte. |
| **Quelle** | `club_tenancy.py` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Medien-Lifecycle hat zusätzliche Stufen (Papierkorb) — Schnittmenge beachten. |
### 7. Aktiver Verein: Header + Profil synchron
| | |
|---|---|
| **Prinzip** | Frontend sendet `X-Active-Club-Id`; Backend validiert gegen Mitgliedschaft; Profil speichert `active_club_id`. |
| **Begründung** | Einheitlicher Mandanten-Kontext über API und UI. |
| **Quelle** | `frontend/src/api/client.js` (`mergeActiveClubHeader`); `profiles` Router |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Onboarding-Nutzer ohne Verein: eingeschränkter Nav-Modus. |
### 8. Plattform-Admin als Audit-Pfad, nicht als Bypass
| | |
|---|---|
| **Prinzip** | Plattform-Admins sehen fremde `club`-Inhalte nur mit expliziter Regel (Mitgliedschaft oder Audit-Ausnahme in SQL). |
| **Begründung** | Superuser-Zugriff ohne Mandanten-Leak in normalen Trainer-Flows. |
| **Quelle** | `library_content_visibility_sql``club_ok_plat`-Zweig |
| **Tragfähigkeit** | **mittel** |
| **Einschränkung** | Feinheiten zwischen `admin` und `superadmin` (z. B. `official`, Legal Hold) separat geregelt. |
### 9. Endpoint-Audit als lebendes Inventar
| | |
|---|---|
| **Prinzip** | Jeder sicherheitsrelevante Endpoint-Eintrag in `ACCESS_LAYER_ENDPOINT_AUDIT.md`; PR-Checkliste verlangt Update. |
| **Begründung** | Sichtbarkeit des Migrationsstands; kein stilles Ausweichen auf `require_auth` allein. |
| **Quelle** | `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md`; `check_access_layer_hints.py` |
| **Tragfähigkeit** | **hoch** (prozessual) |
| **Einschränkung** | CI-Strict-Modus optional, nicht überall aktiv. |
---
## Nicht übernehmen
1. **Endpoints nur mit `require_auth`** bei tenant-sensitiven Daten — führt zu IDOR und fehlenden Listenfiltern.
2. **Visibility-Logik pro Router duplizieren** — historische Drift zwischen Übungen und Planung.
3. **`division` vor stabiler Vereins-Isolation** — Reihenfolge im Plan: erst Stufe C, dann D.
4. **Community-Freigabe ohne additive Felder** — würde `club`-Isolation brechen.
5. **Client-seitige Mandantenfilter ohne Server-Enforcement** — UI-Hiding reicht nicht.
---
## Verwandte Dokumentation
- [ACCESS_LAYER_AND_GOVERNANCE_PLAN.md](../../../.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md)
- [MULTI_TENANCY_RBAC_ARCHITECTURE.md](../../../.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md)
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)