- Added backend support for Legal Hold with new endpoints to set and release holds on media assets. - Introduced new database columns for managing Legal Hold status and reasons. - Updated frontend to include UI elements for setting and releasing Legal Holds, including a confirmation dialog. - Enhanced Media Library page to display Legal Hold status and actions for superadmins. - Implemented comprehensive backend tests covering all aspects of Legal Hold functionality. - Updated documentation to reflect changes in the upload rights specification and interface models. - Bumped version to 0.8.84 and updated MediaLibraryPage version to 1.6.0.
57 KiB
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 + Umsetzungsnachweis
Erstellt: 2026-05-10
App-Version bei Erstellung: 0.8.74
Letzte Aktualisierung: 2026-05-11 (App-Version 0.8.83, P-06+ Volljournal + Korrektur)
Status P-06: ⚠️ technisch umgesetzt (P-06a–P-06d, P-06+) | KRIT-04 offen bis juristische Validierung
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:
- Jeder Medien-Upload mit einer strukturierten, gespeicherten Rechteerklärung verknüpft ist
- Die Erklärung nach Sichtbarkeitsstufe gestuft wird (private minimal, official vollständig)
- Bestehende Prüfungen aus P-04 (copyright_notice) komplementär erhalten bleiben
- Juristische Textanpassungen ohne Codeänderungen möglich sind (Versionierung)
- Altmedien ohne rückwirkenden Zwang behandelt werden können
- 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 wenncopyright_noticeleer bei Ziel club/officialbulk_media_patch(): Asset infailed-Liste wenn copyright fehltapply_official_exercise_media_rules(): HTTP 422OFFICIAL_MEDIA_CONFIRM_REQUIREDwenn 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=clubodervisibility=officialexistieren 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.
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):
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:
- Nutzer öffnet Upload-Dialog (Exercise oder Archiv)
- Dateiauswahl
- 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]
- Upload-Button aktiv erst wenn Pflicht-Checkbox gesetzt
Backend:
- Empfängt neue Felder:
rights_holder_confirmed, optionalcontains_identifiable_persons,person_consent_confirmed,contains_minors - Prüft:
rights_holder_confirmed == true— sonst HTTP 400RIGHTS_DECLARATION_REQUIRED - INSERT in
media_assetsmitrights_status = 'declared',rights_declared_for_visibility = 'private',rights_declared_at = NOW() - INSERT in
media_asset_rights_declarationsmitaction_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 = clubim 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_declarationsmitaction_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:
- Nutzer ändert Sichtbarkeit eines Mediums zu
club(im Archiv oder via Übungssichtbarkeit) - Frontend erkennt Sichtbarkeitserhöhung
- 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]
- 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_visibilityin media_assets auf 'club' setzen
Backend-Fehler:
CONSENT_REQUIRED_FOR_CLUB: Personenfragen nicht beantwortetLEGACY_REDECLARATION_REQUIRED: Altmedium, muss Nachdeklaration abgebenRIGHTS_DECLARATION_REQUIRED(P-04): copyright_notice fehlt (bestehend)
Flow 5: Promotion private/club → official
Frontend:
- Nur Superadmin kann zu official promoten
- 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]
- 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:
- Nutzer versucht Promotion eines Altmediums
- Backend gibt HTTP 400
LEGACY_REDECLARATION_REQUIREDzurück - Frontend zeigt Hinweis und Deklarations-Dialog (analog zu neuem Upload-Dialog)
- Nutzer füllt Deklaration aus
- PATCH mit
legacy_re_declaration=true+ alle Pflichtfelder +visibility - Backend: INSERT in Declarations-Log mit
action_type='legacy_re_declaration', aktualisiertrights_status,rights_declared_for_visibility
Migrationsstrategie für bestehende Daten:
- Alle bestehenden
media_assets-Zeilen bleiben mitrights_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 P-11 (implementiert) und P-13
P-11 Status: ✅ Implementiert (v0.8.84)
| P-06-Element | P-11-Umsetzung | P-13-Anschluss |
|---|---|---|
rights_status = 'blocked' |
P-11 setzt diesen Status als Spiegel bei legal_hold_active=TRUE; bei Freigabe auf declared/legacy_unreviewed zurückgesetzt |
P-13-Moderator triggert set_legal_hold() |
| Declaration-Log | Zeigt, welche Version bei Sperrung galt; wird bei release_legal_hold zur Wiederherstellung von rights_status genutzt |
Kontext für Moderationsentscheidung |
contains_identifiable_persons |
Relevant für Löschbegründung (KUG), reason_code=consent_withdrawn |
Relevanzmerkmal für Meldekategorie |
contains_minors |
Höchste Priorität: reason_code=youth_protection |
CSAM-Eskalationspfad in P-13 |
Implementiertes Schnittstellenmodell P-06 ↔ P-11:
media_legal_hold.py:set_legal_hold()schreibtrights_status='blocked'als Spiegelmedia_legal_hold.py:release_legal_hold()stelltrights_statusanhand vorhandener Deklarationen wieder her (declaredwennCOUNT(rights_holder_confirmed=TRUE)>0, sonstlegacy_unreviewed)- Audit-Log
media_asset_audit_logprotokolliertlegal_hold_set/legal_hold_releasedals eigene Ereignistypen (orthogonal zucopyright_change,visibility_changeetc.)
P-13-Vorbereitung: set_legal_hold() in media_legal_hold.py ist eine generische Funktion — P-13 ruft sie mit reason_code aus der Moderationsqueue auf, ohne eigene Sperr-Logik implementieren zu müssen.
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
- Migration (P-06a): Neue Spalten in
media_assetsmitDEFAULT 'legacy_unreviewed'→ alle bestehenden Zeilen erhalten automatisch Statuslegacy_unreviewed - Neue Uploads nach P-06: Code setzt
rights_status = 'declared'nach erfolgreicher Deklaration - Altmedien-Promotion: Backend blockiert mit
LEGACY_REDECLARATION_REQUIRED; Nutzer muss Nachdeklaration abgeben - 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
- Rechtsanwalt beauftragt für §7.7 (Texte) und §7.1–7.3 (Anforderungen je Stufe)
- Nach Klärung §7.7: Freigabe P-06a + P-06b parallel (kein Frontend-Merge nötig)
- Nach §7.7 Textfreigabe: Freigabe P-06c (Frontend-Dialoge)
- P-06d: Tests + Doku beim gleichen Merge wie P-06b/P-06c
- 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 (Stand nach vollständiger Umsetzung)
| Prüfung | Ergebnis |
|---|---|
| Codeänderungen vorgenommen? | Ja — P-06a–P-06d vollständig; P-06+ nachgezogen |
| Neue Migrationen erstellt? | Ja — Migration 048 (Deklarations-Log + Schnellfelder) + Migration 049 (Kontext-Felder) + Migration 050 (Audit-Log + correction_note) |
| Neue Endpoints erstellt? | Ja — 5 neue Endpoints (re-declaration, legacy-summary, legacy-assets, journal, correction) |
| 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 — technisch umgesetzt; KRIT-04 (juristische Validierung) bleibt offen |
P-06 technisch umgesetzt (2026-05-11, v0.8.83). KRIT-04 bleibt bis juristische Validierung (§22 KUG, §8 DSGVO, Textfreigabe).
10. Vorläufige Implementierungsannahmen (2026-05-11)
Wichtiger Hinweis: Die folgenden Annahmen gelten als vorläufige Erstfassung. Sie wurden bewusst konservativ gewählt. Die juristische Validierung (insb. §7.1–§7.7 dieser Spec) steht weiterhin aus. Texte, Pflichtfelder und Stufenlogik können nach juristischer Prüfung gelockert oder präzisiert werden. P-06 gilt nach dieser technischen Umsetzung als technisch umgesetzt unter vorläufigen Annahmen — nicht als rechtlich endgültig abgeschlossen. KRIT-04 bleibt bis zur anwaltlichen Prüfung als rechtlicher Blocker offen.
10.1 Abweichung gegenüber ursprünglicher Matrix (§3)
Die Entscheidungsmatrix in §3 sah vor:
- private Upload: Personenfragen optional
- club/official Upload: Personenfragen Pflicht
Geändert für conservative Erstannahme:
- Personenfragen sind bei allen Uploads (auch
private) Pflicht zu beantworten - Begründung: lieber zunächst strenger und nach juristischer Klärung gezielt lockern; ein bereits gesammeltes „Nein, keine erkennbaren Personen" ist besser als gar kein Datenpunkt
- Diese Verschärfung ist als
declaration_version = 'p06-v1-conservative'dokumentiert - Spätere Lockerung auf
'p06-v1.1'o. ä. möglich ohne Datenverlust
10.2 Verbindliche Erstannahmen
| Annahme | Technisch umgesetzt als |
|---|---|
Jeder Upload (auch private) erfordert Rechteerklärung |
rights_holder_confirmed = true Pflicht bei allen Uploads |
| Personenfragen bei allen Uploads Pflicht | contains_identifiable_persons muss beantwortet sein |
| Personeneinwilligung Pflicht wenn Personen vorhanden | person_consent_confirmed = true wenn contains_identifiable_persons = true |
| Minderjährigenfrage bei allen Uploads Pflicht | contains_minors muss beantwortet sein |
| Elterneinwilligung Pflicht wenn Minderjährige vorhanden | parental_consent_confirmed = true wenn contains_minors = true |
| Musikfrage Pflicht | contains_music muss beantwortet sein |
| Musikrechte Pflicht wenn Musik vorhanden | music_rights_confirmed = true wenn contains_music = true |
| Fremdmaterial-Frage Pflicht | contains_third_party_content muss beantwortet sein |
| Fremdmaterialrechte Pflicht wenn Fremdmaterial vorhanden | third_party_rights_confirmed = true wenn contains_third_party_content = true |
| Erklärung gilt für konkrete Zielsichtbarkeit | target_visibility = private / club / official |
| Promotion zu höherer Sichtbarkeit erfordert neue Erklärung | Backend blockiert ohne passende neue Declaration |
| Altmedien bleiben sichtbar | rights_status = 'legacy_unreviewed' per Migration-Default |
| Altmedien-Promotion blockiert | HTTP 400 LEGACY_REDECLARATION_REQUIRED |
| Selbsterklärung genügt (MVP) | Kein Dokumenten-Upload erforderlich |
| Deklarationsversion | declaration_version = 'p06-v1-conservative' |
10.3 Vorläufige Dialogtexte (Arbeitsfassung — juristisch nicht geprüft)
Die folgenden Texte sind Arbeitsfassungen und explizit als vorläufig zu behandeln. Sie werden in der UI mit einem entsprechenden Hinweis angezeigt. Alle Labels sind in der Frontend-Komponente zentral definiert und ohne Code-Änderung austauschbar.
| # | Label | Text (Vorläufig) |
|---|---|---|
| T1 | Rechteinhaber-Bestätigung | „Ich bestätige, dass ich berechtigt bin, dieses Medium hochzuladen und in der gewählten Sichtbarkeitsstufe zu verwenden, und dass ich über die dafür erforderlichen Rechte verfüge." |
| T2 | Personen-Frage | „Sind auf diesem Medium Personen eindeutig erkennbar?" |
| T3 | Personen-Einwilligung | „Ich bestätige, dass mir für alle erkennbaren Personen die für diese Nutzung erforderlichen Einwilligungen vorliegen." |
| T4 | Minderjährigen-Frage | „Sind auf diesem Medium Minderjährige eindeutig erkennbar?" |
| T5 | Minderjährigen-Einwilligung | „Ich bestätige, dass mir die für diese Nutzung erforderlichen Einwilligungen der Sorgeberechtigten vorliegen." |
| T6 | Musik-Frage | „Enthält das Medium Musik?" |
| T7 | Musik-Rechte | „Ich bestätige, dass ich für die enthaltene Musik die für diese Nutzung erforderlichen Rechte habe." |
| T8 | Fremdmaterial-Frage | „Enthält das Medium Logos, Grafiken oder sonstige fremde geschützte Inhalte?" |
| T9 | Fremdmaterial-Rechte | „Ich bestätige, dass ich für alle enthaltenen fremden Inhalte die für diese Nutzung erforderlichen Rechte habe." |
| T10 | Hinweis | „Diese Erklärung wird protokolliert. Die konkrete rechtliche Ausgestaltung wird noch abschließend geprüft." |
10.4 Tatsächlich implementierte Endpunkte und Felder
Migration 048: backend/migrations/048_media_rights_declarations.sql
- Neue Tabelle:
media_asset_rights_declarations(append-only, alle Felder aus §4.1) - Neues Feld
rights_statusinmedia_assets(DEFAULT'legacy_unreviewed') - Neues Feld
rights_declared_for_visibilityinmedia_assets - Neues Feld
rights_declared_atinmedia_assets
Neues Backend-Modul: backend/media_rights.py
VISIBILITY_LEVELS: Hierarchie private(1) < club(2) < official(3)validate_rights_declaration(): Prüft vollständige Deklaration je Sichtbarkeitcheck_rights_coverage(): Prüft ob vorhandene Erklärung Zielsichtbarkeit abdecktwrite_rights_declaration(): Schreibt append-only Declaration-Logupdate_rights_quick_fields(): Aktualisiert Schnellfelder inmedia_assets
Angepasste Endpunkte:
| Endpunkt | Änderung |
|---|---|
POST /api/media-assets/bulk-upload |
P-06-Pflichtfelder als Form-Parameter; Declaration-Log bei Erfolg |
PATCH /api/media-assets/{id} |
Rechte-Prüfung bei Promotion; Declaration-Log wenn neue Erklärung |
POST /api/media-assets/bulk-patch |
Rechte-Prüfung bei Promotion per Asset |
POST /api/exercises/{id}/media |
P-06-Pflichtfelder; Declaration-Log bei Datei-Upload |
POST /api/exercises/{id}/media/from-asset |
rights_status-Prüfung des Assets gegen Übungssichtbarkeit |
Neue Endpunkte (P-06b):
| Endpunkt | Beschreibung |
|---|---|
POST /api/media-assets/{id}/rights-declarations |
Re-/Nachdeklaration für vorhandene Medien |
GET /api/admin/media-rights/legacy-summary |
Auswertung legacy_unreviewed nach Sichtbarkeit |
GET /api/admin/media-rights/legacy-assets |
Liste legacy_unreviewed club/official-Medien |
Frontend-Komponente:
frontend/src/components/RightsDeclarationDialog.jsx— wiederverwendbarer Dialog- Integration in
MediaLibraryPage.jsx(Bulk-Upload) undExerciseFormPage.jsx(Upload)
Migration 049 (Kontext-Felder — ergänzend zu 048):
person_consent_context TEXT,parental_consent_context TEXT,music_rights_context TEXT,third_party_rights_context TEXT— Freitextfelder für Einwilligungskontext (z. B. Einwilligungsreferenz, Datum, Beschreibung)
10.5 Juristische Restoffenheit
Folgende Punkte aus §7 sind technisch mit konservativer Annahme implementiert, aber rechtlich noch zu validieren:
| §7-Punkt | Konservative Annahme | Juristisch offen |
|---|---|---|
| §7.1 private: Reicht rights_holder allein? | Nein — alle Fragen Pflicht | Ja |
| §7.2 club: KUG vereinsintern? | Ja — volle Erklärung Pflicht | Ja |
| §7.4 Minderjährige: Schwelle, Form? | Pflichtfeld ab Upload | Ja |
| §7.5 Selbsterklärung genügt? | Ja (MVP) | Ja |
| §7.6 Batch-Deklaration genügend? | Ja — eine Erklärung gilt für alle Batch-Dateien | Ja |
| §7.7 Textfassung der Erklärungen | Vorläufige Arbeitsfassung (T1–T10) | Ja — anwaltliche Freigabe nötig |
| §7.10 Aufbewahrung Declaration-Log | ON DELETE SET NULL für declared_by; CASCADE auf media_asset_id |
Ja |
11. P-06+ Erweiterungen: Volljournal + Korrektur (2026-05-11, v0.8.82–0.8.83)
Diese Erweiterungen gehen über die ursprüngliche P-06-Spezifikation (§8) hinaus und wurden in der Implementierungsphase ergänzt, da das Volljournal und die Korrekturfähigkeit als notwendige Bestandteile eines nachweisfähigen Einwilligungs-Systems erkannt wurden.
11.1 Motivation
Die ursprüngliche P-06-Spezifikation sah einen append-only Deklarations-Log vor (§4.1, Entscheidung 4). Nicht explizit geplant war:
- Protokollierung von Nicht-Deklarations-Änderungen (Sichtbarkeit, Copyright, Lifecycle)
- Eine einheitliche chronologische Ansicht aller Ereignisse zu einem Asset
- Die Möglichkeit, abgegebene Deklarationen mit Begründung nachträglich zu korrigieren (bei Irrtum oder Widerruf)
11.2 Migration 050: Vollständiger Audit-Log
Datei: backend/migrations/050_media_audit_log.sql
Neue Tabelle media_asset_audit_log:
CREATE TABLE media_asset_audit_log (
id SERIAL PRIMARY KEY,
media_asset_id INT NOT NULL REFERENCES media_assets(id) ON DELETE CASCADE,
acting_profile_id INT REFERENCES profiles(id) ON DELETE SET NULL,
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
event_type VARCHAR(50) NOT NULL
CHECK (event_type IN (
'visibility_change',
'copyright_change',
'metadata_change',
'lifecycle_change'
)),
old_values JSONB,
new_values JSONB
);
Append-only. Wird nie geändert oder gelöscht (außer ON DELETE CASCADE des Assets).
Neue Spalte correction_note in media_asset_rights_declarations:
ALTER TABLE media_asset_rights_declarations
ADD COLUMN IF NOT EXISTS correction_note TEXT;
Erweiterter CHECK in media_asset_rights_declarations:
action_typenimmt jetzt auch'correction'an (zusätzlich zu upload, promote_club, promote_official, re_declaration, legacy_re_declaration)
11.3 Neue Backend-Funktionen (backend/media_rights.py)
| Funktion | Beschreibung |
|---|---|
write_audit_log_entry(cur, asset_id, acting_profile_id, event_type, old_values, new_values) |
Schreibt einen Eintrag in media_asset_audit_log (JSONB-serialisiert) |
write_rights_correction_declaration(cur, asset_id, profile_id, target_visibility, decl, correction_note) |
Schreibt action_type='correction' in media_asset_rights_declarations |
11.4 Neue Endpoints (backend/routers/media_assets.py)
| Endpoint | Auth | Beschreibung |
|---|---|---|
GET /api/admin/media-rights/assets/{id}/journal |
Superadmin / Uploader / Vereins-Admin | Vollständiges Medien-Journal: Deklarationen + Audit-Ereignisse chronologisch gemischt (events[] mit kind='declaration' oder kind='audit'). Gibt auch can_correct zurück. |
POST /api/admin/media-rights/assets/{id}/correction |
Superadmin / Uploader / Vereins-Admin | Korrektur-Deklaration einreichen (append-only, neueste gilt). Felder identisch mit P-06-Dialog + correction_note. |
Journal-Response-Format:
{
"asset": { "id": 57, "original_filename": "...", "visibility": "club", ... },
"events": [
{
"kind": "declaration",
"id": 12,
"at": "2026-05-11T10:00:00Z",
"action_type": "upload",
"target_visibility": "private",
"rights_holder_confirmed": true,
"contains_identifiable_persons": false,
"correction_note": null,
"declared_by_name": "Lars",
"declared_by_email": "stommer@gmail.com"
},
{
"kind": "audit",
"id": 3,
"at": "2026-05-11T11:00:00Z",
"event_type": "visibility_change",
"old_values": {"visibility": "private", "club_id": null},
"new_values": {"visibility": "club", "club_id": 2},
"acting_name": "Lars"
}
],
"can_correct": true
}
11.5 Automatische Audit-Log-Einträge
| Aktion | event_type | Auslöser |
|---|---|---|
| Sichtbarkeitsänderung via PATCH | visibility_change |
PATCH /api/media-assets/{id} wenn visibility oder club_id sich ändert |
| Copyright-Änderung via PATCH | copyright_change |
PATCH /api/media-assets/{id} wenn copyright_notice sich ändert |
| Metadaten-Änderung (Dateiname etc.) | metadata_change |
PATCH /api/media-assets/{id} wenn original_filename etc. sich ändert |
| Lifecycle-Übergang | lifecycle_change |
_apply_lifecycle_action: trash_soft, trash_hidden, recover, reactivate (nicht bei Hard-Delete oder Purge) |
11.6 Frontend-Erweiterungen (MediaLibraryPage.jsx)
- Unified Journal Modal: Journal-Ansicht zeigt
events[]chronologisch. Deklarationseinträge und Audit-Einträge visuell unterschieden (--auditCSS-Modifier). - Korrektur-Formular: Inline-Formular im Journal-Modal. Erscheint wenn
can_correct = trueund Nutzer „Korrektur erfassen" klickt. Enthält alle P-06-Felder +correction_noteFreitextfeld. - Neue
api.js-Funktion:addMediaAssetDeclarationCorrection(assetId, body)— POST auf Korrektur-Endpoint.
11.7 Zugriffsmodell
Journal und Korrektur sind nicht auf Superadmin beschränkt — bewusste Entscheidung:
| Rolle | Zugang Journal | Zugang Korrektur |
|---|---|---|
| Superadmin | ✅ | ✅ |
| Uploader (ursprünglicher Upload) | ✅ | ✅ |
| Vereins-Admin (bei club-Medien) | ✅ | ✅ |
| Andere | ❌ 403 | ❌ 403 |
Club-Admin-Prüfung erfolgt über has_club_role(cur, profile_id, club_id, "club_admin") (Tabelle club_member_roles), nicht über direktes role-Feld in club_members.
11.8 Bugfixes (2026-05-11)
| Bug | Ursache | Fix |
|---|---|---|
| Journal und Korrektur gaben 500 zurück | Club-Admin-Prüfung nutzte AND role = 'admin' auf club_members — diese Spalte existiert dort nicht; Rollen liegen in club_member_roles |
Ersetzt durch has_club_role(cur, profile_id, club_id, "club_admin") |
Frontend-Build-Fehler nach Duplikat lcLabel |
Funktion lcLabel wurde doppelt deklariert (bei Zeile 98 und 264) |
Duplikat bei Zeile 264 entfernt |
11.9 Juristische Erweiterungsfragen
Die Korrekturfähigkeit von Deklarationen wirft zusätzliche juristische Fragen auf:
| Frage | Technisches Verhalten | Juristisch zu prüfen |
|---|---|---|
| Wie lange ist eine Korrektur zulässig? | Kein zeitliches Limit implementiert | Fristenregelung? |
| Wer darf eine Korrektur einreichen? | Superadmin, Uploader, Vereins-Admin | Nur Uploader? |
| Gilt die neueste Korrektur immer als maßgeblich? | Ja — rights_declared_for_visibility wird aktualisiert |
Ja, solange append-only |
| Muss eine Korrektur begründet werden? | correction_note optional, nicht Pflicht |
Pflichtfeld? |
Erstellt: 2026-05-10 | Implementierungsannahmen hinzugefügt: 2026-05-11 | P-06+ Volljournal + Korrektur: 2026-05-11 (v0.8.82–0.8.83) | Autor: Claude Code | Kein Rechtsanwalt; alle rechtlichen Einschätzungen sind juristisch zu prüfen.