# P-06 – Fachlich-technische Spezifikation: Upload-Einwilligungsdialog **Paket-ID:** P-06 (kanonisch, unveränderlich) **Kanonischer Titel:** Upload-Einwilligungsdialog (Recht am eigenen Bild, Personen, Minderjährige) **Dokument-Typ:** Spezifikation — keine Umsetzungsfreigabe **Erstellt:** 2026-05-10 **App-Version bei Erstellung:** 0.8.74 **Status P-06:** ❌ offen — Umsetzung erst nach Entscheidung zu offenen juristischen Punkten **Rechtlicher Hinweis:** Alle als „juristisch zu prüfen" markierten Einschätzungen sind keine Rechtsberatung. Keine konkrete Textfassung von Erklärungen in diesem Dokument — Formulierungen obliegen dem Rechtsanwalt. --- ## 1. Kontext und Ziel ### 1.1 Ausgangslage (Initial-Audit-Befund) Das Initial-Audit (`docs/compliance-audit.md`, §8.2–8.3) identifiziert folgende Lücken: - Keine verbindliche Rechteerklärung beim Medienupload - Keine Abfrage, ob erkennbare Personen enthalten sind - Keine Abfrage, ob Minderjährige abgebildet sind - Keine Abfrage, ob erforderliche Einwilligungen abgebildeter Personen vorliegen - Keine Dokumentation, wer wann welche Erklärung abgegeben hat ### 1.2 Regulatorische Prüfanker (technische Modellierung) | Norm | Relevanz für Datenmodell | Juristisch zu prüfen | |------|--------------------------|----------------------| | § 22 KUG | Bildnisse erkennbarer Personen erfordern Einwilligung vor Verbreitung/Zurschaustellung | Gilt auch für vereinsinterne Sichtbarkeit? | | Art. 8 DSGVO | Bei Minderjährigen ggf. Einwilligung Erziehungsberechtigter | Ab welchem Alter? Welche Mechanismen? | | § 19a UrhG | Öffentliche Zugänglichmachung = eigener Nutzungstatbestand | Ab wann gilt `official` als „öffentlich zugänglich"? | | DSGVO Art. 5(1)(e) | Datenminimierung — welche Nachweisdaten dürfen gespeichert werden? | Was ist verhältnismäßig? | ### 1.3 Ziel dieser Spezifikation P-06 soll sicherstellen, dass: 1. Jeder Medien-Upload mit einer strukturierten, gespeicherten Rechteerklärung verknüpft ist 2. Die Erklärung nach Sichtbarkeitsstufe gestuft wird (private minimal, official vollständig) 3. Bestehende Prüfungen aus P-04 (copyright_notice) komplementär erhalten bleiben 4. Juristische Textanpassungen ohne Codeänderungen möglich sind (Versionierung) 5. Altmedien ohne rückwirkenden Zwang behandelt werden können 6. Backend-Erzwingung (keine reine Frontend-Kosmetik) besteht --- ## 2. IST-Analyse ### 2.1 Upload-Pfade (vollständige Bestandsaufnahme) | # | Endpoint | Datei | Upload-Typ | Sichtbarkeit wählbar | Copyright bei Upload | |---|----------|-------|-----------|---------------------|----------------------| | U1 | `POST /api/exercises/{id}/media` | `routers/exercises.py:2518` | Einzeldatei oder Embed-URL | Folgt Übungssichtbarkeit (implizit) | Nicht abgefragt | | U2 | `POST /api/media-assets/bulk-upload` | `routers/media_assets.py:787` | 1–n Dateien | `visibility` + `club_id` als Form-Parameter | Optional (`copyright_notice` fehlt im POST-Body, nur via PATCH nachträglich) | **Kein weiterer Upload-Endpoint vorhanden** (geprüft über Codebase-Suche nach `UploadFile`). **Weitere indirekte Upload-Pfade:** - `POST /api/exercises/{id}/media/from-asset` (`exercises.py`) — verknüpft ein bestehendes Archiv-Asset mit einer Übung; kein Upload, kein neues Medium - Embed-URL (`embed_url`, `embed_platform`) — keine Datei, keine Rechteerklärung für Dateiinhalte; externe Plattform verantwortlich ### 2.2 Promotionspfade (Sichtbarkeitsänderung) | # | Endpoint | Datei | Mögliche Promotion | |---|----------|-------|--------------------| | PR1 | `PATCH /api/media-assets/{id}` | `routers/media_assets.py:1223` | private→club, private→official, club→official, auch Rückstufung | | PR2 | `POST /api/media-assets/bulk-patch` | `routers/media_assets.py:1094` | Massenpromotion, gleiche Richtungen | | PR3 | Implizit bei `PUT /api/exercises/{id}` | `routers/exercises.py` | Übungssichtbarkeit→club/official löst `apply_official_exercise_media_rules()` oder `apply_club_exercise_media_copyright_rules()` aus | **Rückstufungen** (official→club, club→private) werden nicht gesperrt — juristisch zu prüfen, ob Rückstufung Rechte-Konsequenzen hat. ### 2.3 Datenmodell `media_assets` (Migration 045 + 046) ``` media_assets ├── id SERIAL PK ├── mime_type VARCHAR(100) ├── byte_size INT ├── sha256 CHAR(64) NOT NULL ├── original_filename VARCHAR(300) ├── visibility VARCHAR(32) NOT NULL DEFAULT 'private' ← Sichtbarkeitsstufe ├── club_id INT REFERENCES clubs(id) ← Vereinszuordnung ├── uploaded_by_profile_id INT REFERENCES profiles(id) ← Uploader ├── copyright_notice TEXT ← Urheberrechtshinweis (P-04) ├── storage_backend VARCHAR(32) ├── storage_key TEXT NOT NULL UNIQUE ├── lifecycle_state VARCHAR(32) DEFAULT 'active' ├── trash_soft_at, trash_hidden_at, purge_after_at TIMESTAMP ├── created_at, updated_at TIMESTAMP └── tags TEXT[] DEFAULT '{}' ``` **Fehlende P-06-Felder:** Keine Einwilligungsfelder vorhanden. Keine Personenabbildungsmarkierung. Keine Deklarations-Versionierung. ### 2.4 Rollen und Berechtigungen (Zusammenfassung `_item_permissions()`) | Aktion | private | club | official | |--------|---------|------|----------| | Upload (neu) | Jeder authentifizierte Nutzer | Vereinsmitglied + club_id | Nur Superadmin | | `edit_metadata` | Superadmin, Plattform-Admin, Uploader | Superadmin, Plattform-Admin, Club-Admin | Nur Superadmin | | Promotion zu club | Eigener Upload: Uploader; Club-Asset: Club-Admin | — | — | | Promotion zu official | Nur Superadmin | Nur Superadmin | — | | Lifecycle (trash) | Uploader, Plattform-Admin, Superadmin | Club-Admin, Plattform-Admin, Superadmin | Nur Superadmin | ### 2.5 Bestehende Frontend-Dialoge | Stelle | Datei | Aktuelles Verhalten | |--------|-------|---------------------| | Übungsformular Medienupload | `ExerciseFormPage.jsx` | Datei-Picker, kein Einwilligungsdialog | | Archiv Bulk-Upload | `MediaLibraryPage.jsx` | Datei-Picker + optional `copyright_notice` im Editmodal, kein Pflichtdialog | | Übung → official (API-Error) | `ExerciseFormPage.jsx:624` | `window.prompt()` für copyright_notice nach 422-Fehler — reaktiv, kein proaktiver Dialog | | Archiv PATCH Sichtbarkeit | `MediaLibraryPage.jsx` | Backend wirft 400 wenn copyright fehlt; Frontend-Feedback unklar | **Ergebnis:** Kein proaktiver strukturierter Einwilligungsdialog vorhanden. Alle Copyright-Abfragen sind reaktiv (nach API-Fehler) oder optional. ### 2.6 Bestehende Copyright-Prüfungen P-04 (Referenz) P-04 erzwingt `copyright_notice` bei Promotion zu `club` oder `official`: - `patch_media_asset()`: HTTP 400 wenn `copyright_notice` leer bei Ziel club/official - `bulk_media_patch()`: Asset in `failed`-Liste wenn copyright fehlt - `apply_official_exercise_media_rules()`: HTTP 422 `OFFICIAL_MEDIA_CONFIRM_REQUIRED` wenn Assets kein copyright - Mindestlänge: 3 Zeichen (`_MIN_OFFICIAL_MEDIA_COPYRIGHT_LEN = 3`) **Verhältnis zu P-06:** P-04 (copyright_notice) = Attribution für Dritte (wer besitzt das Recht). P-06 = Selbsterklärung des Uploaders (habe ich das Recht, dieses Medium hochzuladen). Ergänzend, nicht konkurrierend. ### 2.7 IST-Lücken (P-06-spezifisch) | Lücke | Schwere | |-------|---------| | Kein Pflichtfeld „Ich bin Rechteinhaber" beim Upload | Kritisch (Grundlage aller Uploads) | | Keine Abfrage „erkennbare Personen enthalten?" | Hoch (§ 22 KUG-Risiko bei club/official) | | Keine Abfrage „Minderjährige enthalten?" | Hoch (Art. 8 DSGVO, besonderer Schutz) | | Keine Speicherung von Einwilligungszeitpunkt und -version | Hoch (Nachweisbarkeit) | | Keine Erzwingung im Backend (nur Frontend-Checkbox wäre zu wenig) | Kritisch | | Kein Einwilligungslog (wer hat wann welche Version bestätigt?) | Hoch | | copyright_notice beim Erstupload nicht abgefragt | Mittel (P-04 fragt erst bei Promotion) | ### 2.8 Altbestandsrisiken - Alle bestehenden `media_assets`-Zeilen haben keine Einwilligungsmetadaten - Unbekannt, ob bei bestehenden Medien erkennbare Personen/Minderjährige abgebildet sind - Medien mit `visibility=club` oder `visibility=official` existieren ohne dokumentierte Einwilligung - Superadmin kann bestehende official-Medien weiter sehen/nutzen ohne Nachdeklaration - Bei Neuzuweisung zu einer anderen Übung werden bestehende Medien ohne Prüfung verknüpft --- ## 3. Entscheidungsmatrix Anforderungen nach **Sichtbarkeitsstufe** (Zeilen) × **Aktion** (Spalten). „Pflicht" = Backend-Sperre wenn fehlend. „Optional" = abfragen aber nicht erzwingen. | Aktion → | Erstupload | Promotion club | Promotion official | Metadaten-Edit | Reuse in neuer Übung | |-----------|-----------|---------------|-------------------|---------------|---------------------| | **private** | `rights_holder`: Pflicht; Personen-Fragen: optional | — | — | Keine P-06-Pflicht | Keine P-06-Pflicht | | **club** | `rights_holder`: Pflicht; `contains_persons`: Pflicht; `person_consent`: Pflicht wenn Personen | `contains_persons` + `person_consent`: Pflicht; `contains_minors`: Pflicht | — | Keine P-06-Pflicht | Keine P-06-Pflicht | | **official** | Alles Pflicht | Alles Pflicht | Alle Felder Pflicht + copyright_notice (P-04) | Keine P-06-Pflicht | Keine P-06-Pflicht | **B: Medieninhalt — Konsequenzen:** | Inhalt | private | club | official | |--------|---------|------|----------| | Keine erkennbaren Personen | `person_consent` entfällt | `person_consent` entfällt | `person_consent` entfällt | | Erkennbare Erwachsene | Optional zu deklarieren | Einwilligung Pflicht (juristisch zu prüfen) | Einwilligung Pflicht | | Erkennbare Minderjährige | Optional zu deklarieren | Einwilligung Erziehungsberechtigter Pflicht (juristisch zu prüfen) | Einwilligung Pflicht | | Unklar / nicht beantwortet | Zulässig bei private | Blockiert bei club/official | Blockiert | **C: Rechteinhalt — Konsequenzen:** | Inhalt | Konsequenz | |--------|-----------| | Nutzer ist selbst Urheber | `rights_holder_confirmed = true` genügt | | Nutzer hat Nutzungsrechte | `rights_holder_confirmed = true` genügt; copyright_notice sollte Rechteinhaber nennen | | Drittmaterial enthalten | `rights_holder_confirmed = true` nur wenn Lizenz vorhanden; copyright_notice Pflicht bei club/official | | Musik enthalten | `contains_music = true`; `music_rights_confirmed` Pflicht (juristisch zu prüfen) | | Logos/Marken/sonstige Fremdinhalte | Fällt unter `rights_holder_confirmed`; juristisch zu prüfen | **D: Aktion — Blockierungslogik:** | Aktion | Fehlendes Pflichtfeld | Verhalten Backend | |--------|----------------------|-------------------| | Upload | rights_holder_confirmed fehlt | HTTP 400 `RIGHTS_DECLARATION_REQUIRED` | | Promotion club | contains_persons / person_consent fehlt | HTTP 400 `CONSENT_REQUIRED_FOR_CLUB` | | Promotion official | Beliebiges Pflichtfeld fehlt | HTTP 400 `CONSENT_REQUIRED_FOR_OFFICIAL` | | Promotion mit Altmedium (legacy) | rights_status = legacy_unreviewed | HTTP 400 `LEGACY_REDECLARATION_REQUIRED` | --- ## 4. Empfohlenes Zielmodell ### 4.1 Neue Tabelle: `media_asset_rights_declarations` Append-only Audit-Log. Jede Erklärung (Upload, Promotion, Nachdeklaration) erzeugt einen neuen Eintrag. Bestehende Einträge werden nie geändert. ```sql CREATE TABLE media_asset_rights_declarations ( id SERIAL PRIMARY KEY, media_asset_id INT NOT NULL REFERENCES media_assets(id) ON DELETE CASCADE, declared_by_profile_id INT REFERENCES profiles(id) ON DELETE SET NULL, declared_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), -- Kontext action_type VARCHAR(50) NOT NULL CHECK (action_type IN ( 'upload', -- Erstupload 'promote_club', -- Promotion zu club 'promote_official', -- Promotion zu official 're_declaration', -- Nachdeklaration (auf Aufforderung) 'legacy_re_declaration' -- Altmedium: erste Deklaration nachgereicht )), target_visibility VARCHAR(32) NOT NULL, -- Sichtbarkeit für die erklärt wird declaration_version VARCHAR(20) NOT NULL DEFAULT 'p06-v1.0', -- Textversion der Erklärung -- Pflichtfeld (alle Sichtbarkeiten) rights_holder_confirmed BOOLEAN NOT NULL, -- Personenrechte (Pflicht ab club) contains_identifiable_persons BOOLEAN, person_consent_confirmed BOOLEAN, -- Pflicht wenn contains_identifiable_persons = true contains_minors BOOLEAN, parental_consent_confirmed BOOLEAN, -- Pflicht wenn contains_minors = true (juristisch zu prüfen) -- Drittmaterial (optional MVP, Pflicht später) contains_music BOOLEAN, music_rights_confirmed BOOLEAN, third_party_content_confirmed BOOLEAN, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); CREATE INDEX idx_mar_declarations_asset ON media_asset_rights_declarations (media_asset_id); CREATE INDEX idx_mar_declarations_profile ON media_asset_rights_declarations (declared_by_profile_id); ``` ### 4.2 Neue Felder in `media_assets` Drei neue Schnellzugriffs-Felder (kein Ersatz für den Log, nur für performante Abfragen): ```sql ALTER TABLE media_assets ADD COLUMN rights_status VARCHAR(32) NOT NULL DEFAULT 'legacy_unreviewed' CHECK (rights_status IN ('pending', 'declared', 'legacy_unreviewed', 'blocked')), ADD COLUMN rights_declared_for_visibility VARCHAR(32), -- Höchste Stufe, für die erklärt wurde ADD COLUMN rights_declared_at TIMESTAMP WITH TIME ZONE; -- Zeitpunkt der aktuellsten Erklärung ``` **Status-Semantik:** | rights_status | Bedeutung | |---------------|-----------| | `legacy_unreviewed` | Bestehendes Medium ohne P-06-Daten — Altbestand | | `pending` | Neues Medium, Upload noch ohne Deklaration (Zwischenzustand, darf nie persistent sein) | | `declared` | Gültige Deklaration vorhanden für `rights_declared_for_visibility` | | `blocked` | Deklaration widerrufen oder durch Admin gesperrt (→ P-11/P-13 Schnittstelle) | **Visibility-Hierarchie für Prüfung:** ``` private (1) < club (2) < official (3) ``` Promotion ist erlaubt wenn: `rights_declared_for_visibility` ≥ Ziel-Visibility. Sonst ist neue Deklaration erforderlich. ### 4.3 Feld-Bewertung: MVP vs. später | Feld | MVP-Pflicht | Sinnvoll später | Unnötig / zu früh | Juristisch abhängig | |------|-------------|-----------------|-------------------|---------------------| | `rights_holder_confirmed` | ✅ | — | — | Nein | | `contains_identifiable_persons` | ✅ ab club | — | — | Nein | | `person_consent_confirmed` | ✅ wenn Personen + ≥ club | — | — | Teilweise | | `contains_minors` | ✅ ab official | ✅ auch club | — | Ja (Schwelle) | | `parental_consent_confirmed` | — | ✅ | — | Ja (juristisch zu klären) | | `contains_music` | — | ✅ | — | Ja | | `music_rights_confirmed` | — | ✅ | — | Ja | | `third_party_content_confirmed` | — | ✅ | — | Teilweise | | `declaration_version` | ✅ | — | — | Nein | | `action_type` | ✅ | — | — | Nein | | `rights_status` in media_assets | ✅ | — | — | Nein | | `rights_declared_for_visibility` | ✅ | — | — | Nein | | Separates Upload von Einwilligungsdokumenten | — | — | Zu früh | Ja | | `may_be_embedded_in_exercises` | — | — | Unnötig (exercise_media-Tabelle reicht) | — | | `declaration_scope_*` als einzelne Booleans | — | — | Unnötig (visibility-String reicht) | — | ### 4.4 Designentscheidungen (mit Begründung) **Entscheidung 1: Gestufte Erklärungen je Sichtbarkeitsstufe** Nicht eine universale Erklärung für alle Stufen, sondern Promotion auf höhere Stufe erfordert neue Erklärung. *Begründung:* Rechtliche Anforderungen steigen mit Sichtbarkeit. Private Medien: nur Eigentümer sieht sie. Official-Medien: plattformweit sichtbar (ggf. öffentlich). Stufenmodell ist rechtsanschlussfähig — spätere juristische Textanpassungen pro Stufe ohne Systemwechsel möglich. **Entscheidung 2: Minimalset beim Erstupload** Pflicht beim privaten Upload: nur `rights_holder_confirmed`. Personen-Fragen: optional beim Upload, Pflicht erst bei Promotion zu club/official. *Begründung:* Die meisten Uploads bleiben privat und werden nie promoted. Volle Fragebogenbelastung beim Upload würde Nutzung hemmen ohne rechtlichen Mehrwert. Personen-Fragen werden erzwungen sobald der Upload für andere sichtbar wird. **Entscheidung 3: Keine globale Nutzer-Einwilligung (pro Upload)** Erklärung wird pro Upload abgegeben, nicht einmalig pro Nutzer. *Begründung:* Jedes Medium ist inhaltlich unterschiedlich. Eine pauschale Einwilligung beim Registrieren oder einmalig je Nutzer deckt nicht ab, ob bei diesem konkreten Video Minderjährige abgebildet sind. **Entscheidung 4: Append-only Audit-Log** Einmal abgegebene Erklärungen werden nie geändert oder gelöscht. Neue Erklärung = neuer Eintrag. *Begründung:* Nachweisbarkeit. Bei späterem Streit muss belegbar sein, was wann erklärt wurde. **Entscheidung 5: Versionierung via `declaration_version`-String** Wenn Rechtsanwalt Erklärungstexte ändert, wird die neue Version als "p06-v1.1" deklariert. Keine Datenbank-FK auf eine Textversion-Tabelle (zu komplex für MVP). *Begründung:* Einfach nachvollziehbar. Ob bei Versionswechsel alle Altdeklarationen erneuert werden müssen, ist juristisch zu entscheiden — technisch ermöglicht das System beides. **Entscheidung 6: copyright_notice bleibt separat (P-04 unberührt)** P-06 führt keine copyright_notice-Redundanz ein. copyright_notice (P-04) = Attribution. rights_holder_confirmed (P-06) = Berechtigung. Beide sind Pflicht bei official. --- ## 5. Ziel-Flows ### Flow 1: Erstupload eines privaten Mediums (Übung oder Archiv) **Frontend:** 1. Nutzer öffnet Upload-Dialog (Exercise oder Archiv) 2. Dateiauswahl 3. **P-06-Dialog erscheint vor Upload-Button-Aktivierung:** - ☑ „Ich bin der Rechteinhaber oder besitze die erforderlichen Nutzungsrechte an diesem Medium." [Pflicht] - ☐ „Das Medium enthält erkennbare Personen." [optional] - → wenn ja: ☑ „Alle abgebildeten Personen haben der Nutzung zugestimmt." [optional bei private, Pflicht bei Promotion] - ☐ „Das Medium enthält Minderjährige." [optional] 4. Upload-Button aktiv erst wenn Pflicht-Checkbox gesetzt **Backend:** - Empfängt neue Felder: `rights_holder_confirmed`, optional `contains_identifiable_persons`, `person_consent_confirmed`, `contains_minors` - Prüft: `rights_holder_confirmed == true` — sonst HTTP 400 `RIGHTS_DECLARATION_REQUIRED` - INSERT in `media_assets` mit `rights_status = 'declared'`, `rights_declared_for_visibility = 'private'`, `rights_declared_at = NOW()` - INSERT in `media_asset_rights_declarations` mit `action_type = 'upload'`, `declaration_version = 'p06-v1.0'`, alle Felder **Fehlermeldungen:** - `RIGHTS_DECLARATION_REQUIRED`: "Bitte bestätige, dass du die erforderlichen Rechte an diesem Medium besitzt." **Tests:** - Upload ohne `rights_holder_confirmed` → 400 - Upload mit `rights_holder_confirmed=true` → 201, `rights_status='declared'` in DB - Declaration-Log-Eintrag vorhanden mit korrektem `action_type='upload'` **Offene juristische Fragen:** Reicht Selbsterklärung oder muss der Betreiber den Upload ansonsten ablehnen? (→ §7.1) --- ### Flow 2: Erstupload direkt als club-Medium (Archiv-Bulk-Upload) **Frontend:** - Nutzer wählt `visibility = club` im Bulk-Upload-Dialog - P-06-Dialog erweitert (zusätzlich zur privaten Pflicht-Checkbox): - ☑ „Das Medium enthält erkennbare Personen." [Antwort Pflicht bei club] - → wenn ja: ☑ „Alle abgebildeten Personen haben der vereinsinternen Nutzung zugestimmt." [Pflicht] - ☑ „Das Medium enthält Minderjährige." [Antwort Pflicht] - → wenn ja: ☑ „Einwilligung der Erziehungsberechtigten liegt vor." [Pflicht, juristisch zu prüfen] **Backend:** - Prüft alle Pflichtfelder für club: `rights_holder_confirmed`, `contains_identifiable_persons` (must be answered), `person_consent_confirmed` (wenn contains_persons=true) - `rights_declared_for_visibility = 'club'` - Eintrag in `media_asset_rights_declarations` mit `action_type = 'upload'`, `target_visibility = 'club'` --- ### Flow 3: Bulk-Upload durch Admin (Plattform-Admin / Superadmin) **Besonderheit:** Admin lädt oft viele Dateien auf einmal. Dialog-Design muss batch-fähig sein. **Empfehlung:** Eine Einwilligungserklärung für den gesamten Batch (nicht je Datei). Alle Dateien des Batches erhalten den gleichen Declarations-Record, jedoch referenziert je `media_asset_id`. **Frontend:** - Vor Upload: Batch-Deklaration (gilt für alle Dateien des Batches) - Alternativ: Je Datei separate Deklaration (aufwändiger aber präziser) — juristisch zu prüfen welche Variante ausreichend ist **Backend:** - `action_type = 'upload'` für alle Dateien des Batches - Wenn Batch-Deklaration: eine Deklarations-Transaktion, n Einträge in Log (je asset_id) **Offene juristische Fragen:** Genügt eine Sammelbestätigung für Batches? (→ §7.6) --- ### Flow 4: Promotion private → club **Frontend:** 1. Nutzer ändert Sichtbarkeit eines Mediums zu `club` (im Archiv oder via Übungssichtbarkeit) 2. Frontend erkennt Sichtbarkeitserhöhung 3. **P-06-Promotions-Dialog:** - ☑ „Das Medium enthält erkennbare Personen." [Pflicht — Antwort erforderlich] - → wenn ja: ☑ „Alle abgebildeten Personen haben der vereinsinternen Nutzung zugestimmt." [Pflicht] - ☑ „Das Medium enthält Minderjährige." [Pflicht — Antwort erforderlich] - → wenn ja: ☑ „Einwilligung der Erziehungsberechtigten liegt vor." [Pflicht, juristisch zu prüfen] 4. Bestehende copyright_notice-Abfrage (P-04) bleibt erhalten — läuft parallel oder im gleichen Dialog **Backend:** - PATCH `/api/media-assets/{id}` empfängt: `visibility='club'`, P-06-Felder, `copyright_notice` - Prüft: rights_status='declared' AND rights_declared_for_visibility ≥ 'club' — wenn ja, keine neue Deklaration nötig (z. B. bereits als club hochgeladen) - Wenn nicht: neue Pflichtfelder prüfen, INSERT in Declarations-Log mit `action_type='promote_club'` - Bestehende P-04-Prüfung: copyright_notice weiterhin Pflicht - `rights_declared_for_visibility` in media_assets auf 'club' setzen **Backend-Fehler:** - `CONSENT_REQUIRED_FOR_CLUB`: Personenfragen nicht beantwortet - `LEGACY_REDECLARATION_REQUIRED`: Altmedium, muss Nachdeklaration abgeben - `RIGHTS_DECLARATION_REQUIRED` (P-04): copyright_notice fehlt (bestehend) --- ### Flow 5: Promotion private/club → official **Frontend:** 1. Nur Superadmin kann zu official promoten 2. Dialog zeigt vollständige Erklärung: - Alle Felder aus Flow 4 (Pflicht) - ☑ zusätzlich: „Das Medium enthält Musik oder sonstige Fremdinhalte." [Pflicht — Antwort erforderlich] - → wenn ja: ☑ „Ich besitze die erforderlichen Lizenzen für die öffentliche Nutzung." [Pflicht] 3. copyright_notice (P-04) weiterhin Pflicht (bestehende Logik) **Backend:** - Prüft alle Felder + copyright_notice - `action_type = 'promote_official'`, `target_visibility = 'official'` - `rights_declared_for_visibility = 'official'` **Offene juristische Fragen:** Reicht Selbstdeklaration oder muss Superadmin bei official höhere Sorgfalt nachweisen? (→ §7.3) --- ### Flow 6: Nachbearbeitung von Metadaten (ohne Sichtbarkeitsänderung) **Szenario:** Nutzer ändert nur `copyright_notice`, `original_filename`, `tags` — keine Sichtbarkeitsänderung. **P-06-Anforderung:** Keine neue Deklaration erforderlich (Metadaten-Edit ist kein Promotions-Schritt). **Ausnahme:** Wenn `rights_status = 'blocked'` (durch P-11/P-13 gesetzt) → Metadaten-Edit erlaubt, aber Sichtbarkeitsänderungen bleiben gesperrt. --- ### Flow 7: Verwendung bestehender Medien in neuer Übung (`from-asset`) **Szenario:** Trainer verknüpft ein bereits im Archiv liegendes Medium mit einer neuen Übung über `POST /api/exercises/{id}/media/from-asset`. **P-06-Anforderung:** Keine neue Deklaration erforderlich. Die Deklaration gilt für das Medium selbst, nicht für die Verknüpfung. Voraussetzung: `rights_status` des Assets ist 'declared' für die Sichtbarkeit der Übung. **Backend-Prüfung (neu):** - Asset-Sichtbarkeit ≥ Übungs-Sichtbarkeit? Bestehende Prüfung. - `rights_declared_for_visibility` ≥ Übungs-Sichtbarkeit? → Neue Prüfung durch P-06 - Wenn nicht: HTTP 400 `RIGHTS_DECLARATION_REQUIRED_FOR_TARGET_VISIBILITY` --- ### Flow 8: Behandlung von Altmedien ohne P-06-Metadaten **Strategie: Soft-Block bei Promotion, kein Rückwirkungs-Zwang für private Altmedien** | Situation | Verhalten | |-----------|-----------| | Altmedium private → weiterhin privat | Unverändert zugänglich; keine Zwangs-Nachdeklaration | | Altmedium privat → Anzeige in Übung (private) | Unverändert; keine neue Deklaration | | Altmedium privat → Promotion zu club | Blocked: `LEGACY_REDECLARATION_REQUIRED`; Nutzer muss Nachdeklaration abgeben | | Altmedium club → Promotion zu official | Blocked; Superadmin muss Nachdeklaration abgeben | | Altmedium mit `rights_status = 'legacy_unreviewed'` | Einsehbar, nicht promotable | **Nachdeklarations-Flow:** 1. Nutzer versucht Promotion eines Altmediums 2. Backend gibt HTTP 400 `LEGACY_REDECLARATION_REQUIRED` zurück 3. Frontend zeigt Hinweis und Deklarations-Dialog (analog zu neuem Upload-Dialog) 4. Nutzer füllt Deklaration aus 5. PATCH mit `legacy_re_declaration=true` + alle Pflichtfelder + `visibility` 6. Backend: INSERT in Declarations-Log mit `action_type='legacy_re_declaration'`, aktualisiert `rights_status`, `rights_declared_for_visibility` **Migrationsstrategie für bestehende Daten:** - Alle bestehenden `media_assets`-Zeilen bleiben mit `rights_status = 'legacy_unreviewed'` (Default in Migration) - Keine rückwirkende Sperrung - Bestehende club/official-Medien: `rights_status = 'legacy_unreviewed'` — die historische Sichtbarkeit bleibt erhalten, spätere Änderungen erfordern aber Nachdeklaration **Juristisch zu prüfen:** Muss der Betreiber aktiv alle Altmedien auf club/official sperren bis zur Nachdeklaration? (→ §7.8) --- ### Flow 9: Widerruf / nachträglich fehlende Rechte erkannt **Szenario:** Abgebildete Person widerruft Einwilligung oder Uploader erkennt, dass er keine Rechte hatte. **P-06 implementiert keinen Widerrufsmechanismus direkt.** Anschluss an spätere Pakete: - **P-11 (Legal-Hold):** Superadmin kann `rights_status = 'blocked'` setzen → Medium sofort aus allen öffentlichen Abfragen entfernt - **P-13 (Content-Melde-Backend):** Dritte können Rechtsverletzung melden → Moderationsqueue → Superadmin entscheidet über Sperrung **P-06-Schnittstelle:** Das Feld `rights_status = 'blocked'` in `media_assets` ist die technische Brücke zu P-11 und P-13. **Empfehlung:** Declaration-Log-Tabelle hat `ON DELETE CASCADE` auf `media_assets.id` — wenn ein Medium purged wird, werden auch alle Deklarationseinträge entfernt. Juristisch zu prüfen, ob Deklarationslog nach Löschung aufbewahrt werden muss (Rechenschaftspflicht Art. 5 Abs. 2 DSGVO). --- ### Flow 10: Zusammenspiel mit späterem P-11 und P-13 | P-06-Element | P-11-Anschluss | P-13-Anschluss | |-------------|---------------|---------------| | `rights_status = 'blocked'` | P-11 setzt diesen Status (Legal-Hold) | P-13-Moderator triggert P-11-Sperre | | Declaration-Log | Zeigt, welche Version bei Sperrung galt | Kontext für Moderationsentscheidung | | `contains_identifiable_persons` | Relevant für Löschbegründung (KUG) | Relevanzmerkmal für Meldekategorie | | `contains_minors` | Höchste Priorität bei P-11-Sofortsperrung | CSAM-Eskalationspfad in P-13 | P-06 darf nicht im Widerspruch zu den späteren Paketen implementiert werden. Der `rights_status`-Enum muss `'blocked'` bereits in P-06a (Migration) enthalten. --- ## 6. Legacy- und Migrationskonzept ### 6.1 Fragen und Antworten | Frage | Empfehlung | Juristisch zu prüfen | |-------|-----------|----------------------| | Dürfen Altmedien sichtbar bleiben? | Ja — keine rückwirkende Sperrung | Ja — insb. club/official ohne Einwilligung | | Dürfen Altmedien in neuen Übungen verwendet werden? | Ja, wenn Sichtbarkeit kompatibel | Ja — falls keine Rechte vorhanden | | Dürfen Altmedien zu club promoted werden? | Nein — Nachdeklaration erforderlich | Ja — ob Selbstdeklaration genügt | | Dürfen Altmedien zu official promoted werden? | Nein — vollständige Nachdeklaration | Ja — höhere Anforderungen | | Muss bei erster Bearbeitung Nachdeklaration erzwungen werden? | Nein (nur bei Promotion) | Ja — Betreiber-Ermessen | ### 6.2 Migration-Schritte 1. **Migration (P-06a):** Neue Spalten in `media_assets` mit `DEFAULT 'legacy_unreviewed'` → alle bestehenden Zeilen erhalten automatisch Status `legacy_unreviewed` 2. **Neue Uploads nach P-06:** Code setzt `rights_status = 'declared'` nach erfolgreicher Deklaration 3. **Altmedien-Promotion:** Backend blockiert mit `LEGACY_REDECLARATION_REQUIRED`; Nutzer muss Nachdeklaration abgeben 4. **Keine automatische Batch-Re-Deklaration** durch Betreiber (wäre fachlich falsch — Betreiber kann nicht für Uploader erklären) ### 6.3 Empfohlene Übergangsstrategie (Minimales Risiko) ``` private Altmedien: weiter sichtbar und nutzbar | Promotion blockiert club Altmedien: weiter sichtbar | Sichtbarkeitsänderung blockiert official Altmedien: weiter sichtbar | Sichtbarkeitsänderung blockiert ``` *Begründung:* Vollständige Sperrung aller Altmedien würde Betrieb lahmlegen. Die Blockierung bei Promotion schützt vor Eskalation ohne existierende Nutzung zu zerstören. Juristisch bleibt das Risiko bei bereits exponierten Medien — das ist ein organisatorisches Problem, kein technisches. --- ## 7. Juristische Klärungsliste Alle folgenden Punkte müssen durch einen Rechtsanwalt oder Datenschutzbeauftragten bewertet werden. Keine der folgenden Einschätzungen ist rechtlich bindend. | # | Frage | Technische Relevanz | Dringlichkeit | |---|-------|---------------------|---------------| | 7.1 | Welche Erklärung ist für **private** Medien (nur Uploader sieht sie) erforderlich? Reicht `rights_holder_confirmed` oder ist auch ohne Sichtbarkeit Dritter eine Personeneinwilligung nötig? | Ob Personen-Fragen bei private Pflicht werden | Vor MVP | | 7.2 | Welche Erklärung ist für **club**-interne Sichtbarkeit (Vereinsmitglieder) erforderlich? Gilt § 22 KUG für vereinsintern? Reicht Selbsterklärung des Uploaders? | Pflichtfelder bei Promotion zu club | Vor MVP | | 7.3 | Welche Erklärung ist für **official** (plattformweit, ggf. öffentlich) erforderlich? Reicht Selbsterklärung oder muss Einwilligung nachgewiesen werden? | Pflichtfelder bei Promotion zu official | Vor MVP | | 7.4 | Wie ist mit erkennbaren **Minderjährigen** zu verfahren? Ab welchem Alter benötigen Minderjährige elterliche Einwilligung? Welche Einwilligungsform ist rechtssicher? | `contains_minors` + `parental_consent_confirmed` — Pflicht ab welcher Stufe? | Vor MVP | | 7.5 | Reicht eine **Selbsterklärung** des Uploaders oder müssen Einwilligungs-Dokumente (Scan, Upload) archiviert werden? | Dateiupload für Einwilligungs-Scan nötig? (MVP oder später?) | Vor MVP | | 7.6 | Ist eine **Batch-Deklaration** (eine Erklärung für mehrere Dateien des Uploads) rechtlich ausreichend? | Flow 3: Bulk-Upload-Design | Vor MVP | | 7.7 | Muss die **Textfassung** der Erklärungen (Checkbox-Labels) durch Rechtsanwalt freigegeben werden? Welche konkreten Formulierungen sind erforderlich? | `declaration_version` Textbasis | Vor MVP | | 7.8 | Wie sind **Altmedien** zu behandeln, die bereits mit club/official-Sichtbarkeit in der DB stehen? Muss der Betreiber diese sperren bis zur Nachdeklaration? | Migrationsstrategie §6.3 | Zeitnah | | 7.9 | Wie ist bei **Widerruf** einer Einwilligung vorzugehen? Welche Fristen gelten? Muss das Medium sofort gelöscht oder nur gesperrt werden? | rights_status='blocked', Schnittstelle P-11 | Vor P-11 | | 7.10 | Welche **Nachweisdaten** dürfen/sollen gespeichert werden (Datensparsamkeit vs. Rechenschaftspflicht)? Wie lange muss der Declaration-Log aufbewahrt werden? | Retention-Policy für `media_asset_rights_declarations` | Vor MVP | | 7.11 | Welche Anforderungen gelten für **Trainingsvideos mit mehreren Personen**? Muss jede Person einzeln einwilligen oder reicht eine kollektive Erklärung? | `person_consent_confirmed` als Boolean vs. Mehrfach-Einwilligung | Vor MVP | | 7.12 | Welche Anforderungen gelten für **Musik, Logos, Marken, Grafiken** von Dritten im Video/Bild? Ab wann ist eine separate Lizenz nötig? Reicht `music_rights_confirmed` als Selbsterklärung? | `contains_music`, `music_rights_confirmed` — MVP oder Pflicht? | Mittel | --- ## 8. Umsetzungsplan (für spätere Codephase — keine Umsetzungsfreigabe) ### P-06a – Datenmodell / Migration | Feld | Wert | |------|------| | **Ziel** | Neue Tabelle `media_asset_rights_declarations` anlegen; drei neue Spalten in `media_assets` | | **Betroffene Dateien** | `backend/migrations/048_media_rights_declarations.sql` (neu) | | **Migrationsbedarf** | Neue Migration; `DEFAULT 'legacy_unreviewed'` automatisch für bestehende Zeilen | | **Abhängigkeiten** | Keine (neue Tabelle) | | **Testbedarf** | Migration idempotent; bestehende `media_assets`-Zeilen haben `rights_status='legacy_unreviewed'` nach Migration; neue Zeilen haben `rights_status='declared'` nach P-06b | | **Akzeptanzkriterien** | Tabelle angelegt, alle Spalten korrekt typisiert, Indizes vorhanden, ALTER-Migration ohne Downtime anwendbar | | **Risiko** | Niedrig (neue Tabelle + ALTER mit DEFAULT ohne Table-Lock in PostgreSQL 16) | | **Rollback** | DROP TABLE media_asset_rights_declarations; ALTER TABLE media_assets DROP COLUMN rights_status, rights_declared_for_visibility, rights_declared_at | --- ### P-06b – Backend-Erzwingung | Feld | Wert | |------|------| | **Ziel** | Backend blockiert Upload und Promotion wenn Pflichtfelder fehlen; schreibt Declarations-Log | | **Betroffene Dateien** | `backend/routers/media_assets.py` (bulk-upload + patch + bulk-patch), `backend/routers/exercises.py` (upload_exercise_media, from-asset), neues Modul `backend/media_rights.py` (Hilfslogik) | | **Neue Pydantic-Models** | `RightsDeclaration` (Pflichtfelder + optionale Felder); Integration in `MediaAssetPatch` + Upload-Endpoints | | **Neue Fehlercodes** | `RIGHTS_DECLARATION_REQUIRED`, `CONSENT_REQUIRED_FOR_CLUB`, `CONSENT_REQUIRED_FOR_OFFICIAL`, `LEGACY_REDECLARATION_REQUIRED`, `RIGHTS_DECLARATION_REQUIRED_FOR_TARGET_VISIBILITY` | | **Abhängigkeiten** | P-06a (Migration) muss abgeschlossen sein | | **Testbedarf** | Unit-Tests für jeden neuen Fehlercode; Integrationstests Upload → Deklarationslog; Promotion ohne/mit Deklaration; Legacy-Block | | **Akzeptanzkriterien** | Upload ohne `rights_holder_confirmed` → 400; promotion ohne Personen-Deklaration → 400; gültige Deklaration → Eintrag in DB vorhanden; bestehender P-04-Test weiterhin grün | | **Risiko** | Mittel (Breaking Change für alle Upload-Clients); Feature-Flag zum schrittweisen Rollout empfohlen | | **Rollback** | Feature-Flag deaktivieren; kein DB-Rollback nötig | --- ### P-06c – Frontend-Dialoge | Feld | Wert | |------|------| | **Ziel** | Einwilligungsdialog in allen Upload-Pfaden; Promotions-Dialog bei Sichtbarkeitsänderung | | **Betroffene Dateien** | `frontend/src/components/RightsDeclarationDialog.jsx` (neu, wiederverwendbar), `frontend/src/pages/ExerciseFormPage.jsx`, `frontend/src/pages/MediaLibraryPage.jsx`, `frontend/src/utils/api.js` | | **Dialog-Typen** | Upload-Dialog (privat/club/official je nach Kontext), Promotions-Dialog (erkennt Sichtbarkeitserhöhung), Nachdeklarations-Dialog (für Legacy) | | **Abhängigkeiten** | P-06b (Backend-Codes müssen definiert sein); juristische Textfreigabe für Checkbox-Labels | | **Testbedarf** | Playwright E2E: Dialog erscheint; Upload ohne Checkbox → deaktivierter Button; Dialog mit allem ausgefüllt → erfolgreicher Upload; Backend-Fehler → korrekte Fehlermeldung | | **Akzeptanzkriterien** | Dialog erscheint bei allen Upload-Pfaden; Checkbox-State korrekt; Labels entsprechen juristisch geprüften Formulierungen; Promotions-Dialog erkennt Sichtbarkeitserhöhung | | **Risiko** | Mittel (UX-Änderung an häufig genutzten Pfaden) | | **Rollback** | Feature-Flag; Dialoge können ohne Backend-Änderung deaktiviert werden | --- ### P-06d – Tests / Dokumentation | Feld | Wert | |------|------| | **Ziel** | Vollständige Testabdeckung für P-06 + Doku-Update | | **Betroffene Dateien** | `backend/tests/test_media_rights_declaration.py` (neu), `tests/dev-smoke-test.spec.js` (neue P-06-Tests), `docs/compliance-implementation.md`, `docs/compliance-package-register.md`, Access-Layer-Audit aktualisieren | | **Backend-Tests** | Upload-Flows, Promotions-Flows, Legacy-Block, Declaration-Log, alle Fehlercodes | | **Playwright-Tests** | Dialog erscheint, Checkbox-Validierung, vollständiger Upload-Flow, Promotions-Dialog | | **Abhängigkeiten** | P-06a, P-06b, P-06c abgeschlossen | | **Akzeptanzkriterien** | Alle neuen Tests grün; bestehende Tests unverändert grün; ACCESS_LAYER_STRICT=1 grün | | **Risiko** | Niedrig | --- ### P-06e – Legacy-Nacharbeit (optional, nach juristischer Klärung) | Feld | Wert | |------|------| | **Ziel** | Massenprüfung und ggf. Sperrung bestehender club/official-Medien ohne Deklaration, falls juristisch notwendig | | **Betroffene Dateien** | `backend/scripts/media_rights_audit.py` (neu), evtl. Admin-UI | | **Inhalt** | Script listet alle `visibility IN ('club', 'official')` mit `rights_status='legacy_unreviewed'`; Superadmin kann Massenmarkierung oder individuelle Überprüfung anstoßen | | **Abhängigkeiten** | P-06a–d; juristische Klärung §7.8 | | **Akzeptanzkriterien** | Script läuft ohne Fehler; erzeugt CSV-Report mit betroffenen Assets | | **Risiko** | Mittel (wenn Sperrung aktiv: Betriebsunterbrechung für Vereinsmedien) | | **Hinweis** | Erst nach juristischer Entscheidung über Altbestand-Behandlung umsetzen | --- ## 9. Abschlussbewertung ### 9.1 Neue / geänderte Dokumente | Dokument | Änderung | |----------|---------| | `docs/p06-upload-rights-spec.md` | Neu erstellt (dieses Dokument) | | `docs/compliance-roadmap.md` | P-06: Spezifikation erstellt vermerkt; nächste Freigabe angepasst | | `docs/compliance-implementation.md` | Kurzer P-06-Eintrag: Spezifikation erstellt, keine Umsetzung | | `docs/compliance-package-register.md` | P-06 Letzter Stand: Spezifikation vorhanden | ### 9.2 Empfohlene P-06-Zielarchitektur (Kurzfassung) - **Append-only Log-Tabelle** `media_asset_rights_declarations` (Wer, Wann, Was, Welche Version) - **Drei Status-Felder** in `media_assets`: `rights_status` / `rights_declared_for_visibility` / `rights_declared_at` - **Gestufte Erklärungen**: Upload = Minimalset; Promotion club = Personen-Pflicht; Promotion official = vollständig - **Backend-Erzwingung** über neue Fehlercodes; keine reine Frontend-Checkbox - **Versionierung** über `declaration_version`-String; kein FK auf Texttabelle - **Legacy**: `rights_status = 'legacy_unreviewed'`; Promotion blockiert; Nachdeklarations-Flow ### 9.3 Kritische Designentscheidungen | Entscheidung | Begründung | |-------------|-----------| | Gestufte Erklärungen je Sichtbarkeitsstufe | Rechtsrisiko steigt mit Sichtbarkeit; Rechtsanschlussfähigkeit | | Minimalset beim Erstupload | Nutzbarkeit vs. Compliance; Pflicht-Erweiterung bei Promotion | | Append-only Log (nie löschen/ändern) | Nachweisbarkeit; Audit-Integrität | | rights_status = 'legacy_unreviewed' per Default | Keine rückwirkende Sperrung; kontrollierbarer Übergang | | Kein Einwilligungs-Datei-Upload (MVP) | Aufwand vs. Mehrwert; spätere Nachrüstung möglich | | `rights_status = 'blocked'` als P-11-Schnittstelle | Zukunftssicherheit; P-11/P-13 können sofort anschließen | ### 9.4 Offene juristische Fragen (Priorisiert) | Priorität | Frage | Paket-Impact | |-----------|-------|-------------| | Vor MVP | §7.7 Textfassung der Erklärungen | P-06c (Dialog-Labels) | | Vor MVP | §7.1 Private: Reicht rights_holder allein? | Pflichtfeld-Definition | | Vor MVP | §7.2 Club: § 22 KUG vereinsintern? | contains_persons bei club Pflicht? | | Vor MVP | §7.4 Minderjährige: Schwelle und Form | parental_consent MVP oder später | | Vor MVP | §7.5 Selbsterklärung oder Dokumenten-Upload? | Scope P-06c | | Zeitnah | §7.8 Altmedien club/official: Sperrpflicht? | P-06e Scope | | Vor P-11 | §7.9 Widerruf: Fristen und Mechanismus | P-11-Design | | Mittel | §7.10 Aufbewahrung Declaration-Log | Retention-Policy | ### 9.5 Empfohlene spätere Umsetzungsschritte 1. Rechtsanwalt beauftragt für §7.7 (Texte) und §7.1–7.3 (Anforderungen je Stufe) 2. Nach Klärung §7.7: Freigabe P-06a + P-06b parallel (kein Frontend-Merge nötig) 3. Nach §7.7 Textfreigabe: Freigabe P-06c (Frontend-Dialoge) 4. P-06d: Tests + Doku beim gleichen Merge wie P-06b/P-06c 5. Nach §7.8-Klärung: ggf. P-06e (Legacy-Massenprüfung) ### 9.6 Implementierungsbereitschaft | Kriterium | Einschätzung | |-----------|-------------| | P-06 implementierungsreif | Teilweise — Datenmodell und Backend-Logik spezifizierbar; Dialog-Labels nicht ohne juristische Textfreigabe | | Juristische Vorabklärung erforderlich | Ja — §7.7 (Textfassung) blockiert P-06c; §7.1–7.3 bestimmen Pflichtfelder | | P-06a (Migration) vorab umsetzbar | Ja — unabhängig von juristischen Texten | | P-06b (Backend) vorab umsetzbar | Ja — mit Platzhalter-Fehlercodes, Texte nachrüstbar | | P-06c (Frontend-Dialog) vorab umsetzbar | Nein — Dialog-Labels müssen juristisch freigegeben sein | ### 9.7 Scope-Drift-Prüfung | Prüfung | Ergebnis | |---------|---------| | Codeänderungen vorgenommen? | Nein | | Neue Migrationen erstellt? | Nein | | Neue Endpoints erstellt? | Nein | | Paket-ID geändert? | Nein | | Andere Paket-IDs berührt? | Nein (P-11, P-13 nur als Referenz) | | P-06 bleibt nach dieser Arbeit offen? | Ja | --- **P-06 fachlich-technisch spezifiziert. Keine Codeänderungen vorgenommen.** --- *Erstellt: 2026-05-10 | Autor: Claude Code | Kein Rechtsanwalt; alle rechtlichen Einschätzungen sind juristisch zu prüfen.*