Implementiert server-seitige Rechteerklärungspflicht für alle Medien-Uploads
und Sichtbarkeits-Promotions (konservative Erstannahme: alle Uploads).
Backend:
- backend/media_rights.py (NEU): Kernmodul — validate_rights_declaration,
check_rights_coverage, assert_rights_for_promotion, assert_rights_for_exercise_link,
write_rights_declaration, update_rights_quick_fields
- backend/migrations/048_media_rights_declarations.sql (NEU): Tabelle
media_asset_rights_declarations (Append-only Audit-Log), Felder
rights_status/rights_visibility_level in media_assets
- backend/routers/media_assets.py: P-06-Pflichtprüfung in PATCH (single + bulk),
POST /api/media-assets/{id}/rights-declarations (Re-Deklaration),
GET /api/admin/media-rights/legacy-summary|legacy-assets (Admin-Endpoints)
- backend/routers/exercises.py: P-06-Felder in upload_exercise_media,
assert_rights_for_exercise_link in attach_exercise_media_from_asset
- backend/main.py: admin_rights_router registriert
Frontend:
- frontend/src/components/RightsDeclarationDialog.jsx (NEU): 9-Felder-Dialog
(konservativ: immer alle Fragen), Client-Validierung, VORLÄUFIG-Hinweis
- frontend/src/pages/MediaLibraryPage.jsx: Dialog-Intercept vor Upload,
Altbestand-Indikator (legacy_unreviewed)
- frontend/src/utils/api.js: P-06-Felder in bulkUploadMediaAssets weitergeleitet
Tests:
- backend/tests/test_media_rights_declaration.py (NEU): 28 Unit-/Integrationstests
- backend/tests/test_media_assets_archive.py: P-06 fetchone-Slots + Mock ergänzt
- backend/tests/test_media_assets_copyright_promotion.py: check_rights_coverage gemockt
- tests/dev-smoke-test.spec.js: 5 P-06 E2E-Tests ergänzt
Dokumentation:
- docs/compliance-implementation.md: P-06-Abschnitt
- docs/compliance-package-register.md: Status ⚠️ teilweise umgesetzt (KRIT-04 offen)
- docs/compliance-roadmap.md: P-06 im Freigaben-Log
Offen: KRIT-04 (rechtliche Finalisierung Einwilligungsformulierung) — technisch
vollständig, Rechtstext VORLÄUFIG.
version: 0.8.75
module: media_rights 1.0.0, media_assets 1.13.0, exercises 2.20.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 KiB
Compliance-Implementierung – Umsetzungsbericht
Erstellt: 2026-05-09
Zuletzt aktualisiert: 2026-05-11
Audit-Basis: docs/compliance-audit.md
App-Version nach Umsetzung: 0.8.75
Freigegebene Pakete und Umsetzungsstatus
P-01 – Rechtstexte ⚠️ (technischer Teil umgesetzt; juristische Inhalte offen)
Status: Technisch teilweise umgesetzt (2026-05-10, Version 0.8.69)
Betroffene Dateien:
frontend/src/pages/LegalPage.jsx(neu) — Platzhalter-Komponente für alle vier Seitenfrontend/src/App.jsx— 4 neue öffentliche Routenfrontend/src/pages/LoginPage.jsx— Rechtstext-Links im Card-Footerfrontend/src/components/DesktopSidebar.jsx— Rechtstext-Links im Sidebar-Footer
Technische Änderung:
Vier öffentliche Routen angelegt, kein Auth erforderlich, kein Redirect für nicht eingeloggte Nutzer:
/impressum→<LegalPage type="impressum" />/datenschutz→<LegalPage type="datenschutz" />/nutzungsbedingungen→<LegalPage type="nutzungsbedingungen" />/medienrichtlinie→<LegalPage type="medienrichtlinie" />
Jede Seite enthält:
- Deutlich sichtbaren Platzhalterhinweis: „MUSTER / PLATZHALTER – Inhalt wird vor Produktivbetrieb juristisch geprüft und durch den Betreiber ergänzt."
- Strukturierte Pflichtfelder je Rechtstext (Betreiber, Rechtsgrundlagen, Betroffenenrechte etc.)
- Querlinks zu den anderen drei Rechtstextseiten
Links zu allen vier Seiten sind in der Login-/Registrierungsseite (Card-Footer) und im Desktop-Sidebar-Footer sichtbar ohne Anmeldung erreichbar.
Nicht umgesetzt (außerhalb Freigabe):
Juristische Texte, inhaltliche Überprüfung, Admin-konfigurierbare Inhalte, Einwilligungs-Checkboxen.
KRIT-01 Blocking-Status:
Der Blocker KRIT-01 bleibt offen bis juristisch geprüfte Texte durch den Betreiber eingepflegt sind. Die technische Voraussetzung (Routen und Seitenstruktur) ist erfüllt.
Tests: 5 Playwright-Tests, alle grün:
- Impressum ohne Auth erreichbar + Platzhalterhinweis sichtbar + Reload OK
- Datenschutz ohne Auth erreichbar + Platzhalterhinweis sichtbar + Reload OK
- Nutzungsbedingungen ohne Auth erreichbar + Platzhalterhinweis sichtbar + Reload OK
- Medienrichtlinie ohne Auth erreichbar + Platzhalterhinweis sichtbar + Reload OK
- Login-Seite enthält alle vier Rechtstext-Links
P-01b – Mobile/PWA-Erreichbarkeit der Rechtstexte ✅
Status: Umgesetzt (2026-05-10, Version 0.8.70) — Nacharbeit zu P-01
Betroffene Dateien:
frontend/src/pages/SettingsLegalPage.jsx(neu) — Hub-Seite/settings/legalfrontend/src/App.jsx— Route/settings/legalim ProtectedLayoutfrontend/src/pages/AccountSettingsPage.jsx— Link zu/settings/legal
Technische Änderung:
Neue Seite /settings/legal im eingeloggten Einstellungsbereich: Hub-Seite mit Links zu allen vier Rechtstextseiten, erreichbar über Einstellungen → Rechtliches. Entspricht dem bestehenden Pattern von /settings/system. Auf der AccountSettingsPage ist ein Link „Rechtliches" unterhalb der System-Info-Verlinkung ergänzt.
Tests: 3 Playwright-Tests, alle grün (17/17 Gesamt):
- Einstellungen enthält Link zu
/settings/legal /settings/legalenthält Überschrift + alle vier Rechtstext-Links- Jeder Link aus
/settings/legalführt zur korrekten öffentlichen Route
P-01c – Admin-konfigurierbare Rechtstexte ✅
Status: Umgesetzt (2026-05-10, Version 0.8.71) — Nacharbeit zu P-01
Betroffene Dateien:
backend/migrations/047_legal_documents.sql(neu) — Tabellenlegal_documents+legal_document_auditbackend/routers/legal_documents.py(neu) — Öffentliche + Superadmin-Endpointsbackend/main.py— Router-Registrierungfrontend/src/pages/LegalPage.jsx— API-Fetch mit Fallback auf statischen Platzhalterfrontend/src/pages/AdminLegalDocumentsPage.jsx(neu) — Superadmin-UIfrontend/src/App.jsx— Route/admin/legal-documentsfrontend/src/components/AdminPageNav.jsx— Link „Rechtstexte" im Admin-Navfrontend/src/utils/api.js— 8 neue API-Funktionen
Technische Änderung:
Datenbank (Migration 047):
Tabelle legal_documents: versionierte Rechtstexte mit Workflow draft → published → archived. Felder: document_type (impressum | privacy_policy | terms_of_use | media_policy), version (INT, auto-inkrementiert), title, content_sections (JSONB: [{heading, content}]), status, change_note, Timestamps, FK auf Ersteller + Publisher. Partial-Unique-Index: nur ein published-Datensatz pro document_type gleichzeitig. Tabelle legal_document_audit: unveränderlicher Änderungslog je Dokument.
Backend-Endpoints:
| Endpoint | Auth | Beschreibung |
|---|---|---|
GET /api/legal-documents/{type}/published |
Kein | Liefert veröffentlichtes Dokument oder null |
GET /api/admin/legal-documents |
Superadmin | Alle Versionen aller Typen |
POST /api/admin/legal-documents |
Superadmin | Neuen Entwurf anlegen |
GET /api/admin/legal-documents/{id} |
Superadmin | Einzeldokument mit content_sections |
PUT /api/admin/legal-documents/{id} |
Superadmin | Entwurf bearbeiten (nur status=draft) |
POST /api/admin/legal-documents/{id}/publish |
Superadmin | Veröffentlichen; bisherige Version → archived |
POST /api/admin/legal-documents/{id}/archive |
Superadmin | Archivieren |
GET /api/admin/legal-documents/{id}/audit |
Superadmin | Änderungslog |
Frontend:
LegalPage.jsx ruft beim Laden GET /api/legal-documents/{type}/published ab. Gibt die API null zurück (kein veröffentlichtes Dokument vorhanden), zeigt die Seite weiterhin den bisherigen Platzhalter mit MUSTER-Banner. Ist ein Dokument veröffentlicht, wird dessen Inhalt ohne Platzhalter-Banner angezeigt. AdminLegalDocumentsPage.jsx unter /admin/legal-documents (nur Superadmin) ermöglicht Erstellen, Bearbeiten, Veröffentlichen und Archivieren von Entwürfen mit Tabs pro Dokumententyp und Änderungslog.
Kein neues npm-Paket notwendig — JSONB-Struktur mit {heading, content} statt Markdown; keine XSS-Gefahr.
Tests: 3 Playwright-Tests:
- Rechtstextseiten laden ohne Fehler (API-fetch mit Fallback)
/admin/legal-documentserreichbar für Superadmin mit korrekter Überschrift- Admin-Nav enthält Link zu Rechtstexten
P-01c Erweiterung — Als-Entwurf-kopieren ✅
Status: Umgesetzt (2026-05-10, Version 0.8.72) — Ergänzung zu P-01c
Betroffene Dateien:
backend/routers/legal_documents.py— Neuer EndpointPOST /api/admin/legal-documents/{id}/copy-as-draftfrontend/src/utils/api.js—copyLegalDocumentAsDraft(id)frontend/src/pages/AdminLegalDocumentsPage.jsx— „Als Entwurf kopieren"-Button in der Dokumentliste
Technische Änderung:
Neuer Superadmin-Endpoint kopiert title und content_sections eines bestehenden Dokuments in einen neuen Entwurf. Die Versionsnummer wird dabei automatisch inkrementiert (letztes Dokument dieses Typs + 1). Status ist immer draft. Ermöglicht inkrementelle Überarbeitung ohne vollständige Neueingabe.
Motivation: Bei jeder fälligen Textanpassung mussten alle Abschnitte neu erfasst werden. Die Kopierfunktion ermöglicht, den letzten Stand zu übernehmen und nur die geänderten Abschnitte zu bearbeiten.
P-01c Erweiterung — Echter PDF-Download + Abschnitts-Sortierung ✅
Status: Umgesetzt (2026-05-10, Version 0.8.74) — Ergänzung zu P-01c
Betroffene Dateien:
frontend/src/pages/AdminLegalDocumentsPage.jsx—generateLegalPdf()via jsPDF,SectionEditormit Sortierungfrontend/src/pages/LegalPage.jsx—generateLegalPdf()via jsPDF, Button „PDF herunterladen"frontend/package.json— neues npm-Paketjspdf
Technische Änderung — PDF:
Ersetzt die bisherige window.open() + window.print()-Lösung (Browser-Druckdialog) durch jsPDF. Die Funktion generateLegalPdf(doc) erzeugt ein A4-PDF client-seitig mit:
- Titel (bold, 20 pt), Metazeile (Version + Gültigkeitsdatum), Trennlinie
- Abschnitte mit Heading (bold, 11 pt) und Fließtext (10 pt,
splitTextToSizefür automatischen Zeilenumbruch) - Automatischer Seitenumbruch bei
y > 277 mm - Footer auf jeder Seite: „Shinkan Jinkendo | Exportiert am DD.MM.YYYY Seite X von Y"
- Direkter Dateidownload via
pdf.save('{document_type}_v{version}.pdf')
Gilt sowohl für die Admin-Seite (Download aus der Dokumentliste, ruft getLegalDocument(id) für Volldokument ab) als auch für LegalPage.jsx (öffentlich, nur bei veröffentlichten Dokumenten).
Technische Änderung — Abschnitts-Sortierung und Einfügen:
SectionEditor in AdminLegalDocumentsPage.jsx erhält:
- ▲/▼-Buttons pro Abschnitt (deaktiviert an den Rändern) — Reihenfolge per Array-Swap
InsertButtonzwischen jedem Abschnitt (inkl. vor dem ersten) — fügt leeren Abschnitt an beliebiger Stelle perspliceein- Kein Drag-and-Drop-Framework — reine React-State-Manipulation
P-03 – Papierkorb-Retention-Job aktivieren ✅
Status: Umgesetzt
Betroffene Dateien:
docker-compose.yml– neuer Serviceretention-cron
Technische Änderung:
Neuer Docker-Service retention-cron nutzt dasselbe Backend-Image und führt scripts/media_retention_job.py täglich um 03:00 Uhr aus. Der Service startet beim ersten Hochfahren sofort und schläft bis zum nächsten 3 AM (Python-basierter Loop ohne externe Cron-Abhängigkeit). Zugriff auf DB und Media-Volume identisch zur Backend-Konfiguration.
Tests: Keine automatisierten Tests möglich (Runtime-Verhalten); operativ über Container-Logs (docker logs shinkan-retention-cron) überprüfbar.
P-03b – Retention-Zeiten mit Löschkonzept abgleichen ✅
Status: Umgesetzt (2026-05-10)
Betroffene Dateien:
backend/media_lifecycle.py:24– DefaultHIDDEN_TO_PURGE_DAYSdocker-compose.yml– explizite Env-Variable imretention-cron-Service
Befund des Re-Audits:
Das fachliche Löschkonzept (Audit §6.4) sieht 30 + 30 Tage vor:
- Stufe 1 → Stufe 2 (Soft → Hidden): 30 Tage ✓ (war bereits korrekt)
- Stufe 2 → Purge (Hidden → gelöscht): weitere 30 Tage → Code-Default war 90 Tage ❌
Technische Änderung:
# media_lifecycle.py Zeile 24 (vorher "90", jetzt "30"):
HIDDEN_TO_PURGE_DAYS = max(1, int(os.getenv("MEDIA_TRASH_HIDDEN_TO_PURGE_DAYS", "30")))
docker-compose.yml enthält jetzt explizit MEDIA_TRASH_HIDDEN_TO_PURGE_DAYS: "${...:-30}" mit Kommentar, der das 30+30-Konzept dokumentiert. Der Wert kann per Env-Variable in einzelnen Deployments überschrieben werden.
P-04 – Copyright-Pflicht bei Archiv-Promotion vereinheitlichen ✅
Status: Umgesetzt; Tests nachgehärtet (2026-05-10)
Betroffene Dateien:
backend/routers/media_assets.py–patch_media_asset()undbulk_media_patch()backend/tests/test_media_assets_copyright_promotion.py
Technische Änderung:
Beide Endpoints prüfen, ob copyright_notice vorhanden ist, wenn visibility auf club oder official gewechselt wird. Priorität: Wert aus dem Request-Body > bestehender Wert in der DB. Ist beides leer, wird HTTP 400 zurückgegeben.
Fehlermeldung: "Für Vereins- oder offizielle Medien ist eine Urheberrechtsangabe (copyright_notice) Pflicht." (Umlaut korrekt, 2026-05-10 korrigiert)
Tests: 7 Tests, alle grün:
- Promotion zu
clubohne Copyright → 400 (exakt) - Promotion zu
officialohne Copyright → 400 (exakt) - Promotion zu
clubmit Copyright im Body → 200 + Payload-Prüfung (gehärtet) - Promotion zu
club, Copyright bereits auf Asset → 200 + Payload-Prüfung (gehärtet) - Kein Visibility-Wechsel → keine Copyright-Prüfung → 200 (exakt)
- Bulk: ohne Copyright → in
failed,updated_count == 0(exakt) - Bulk: mit Copyright → in
updated,updated_count == 1(exakt)
P-05 – Passwort-Mindestlänge angleichen ⚠️ (Teil 1 von 2 – Re-Audit-Auflage offen)
Status: Teilweise umgesetzt
Betroffene Dateien (initialer Fix 2026-05-09):
backend/routers/auth.py:101–PUT /api/auth/pin:< 4→< 8frontend/src/pages/LoginPage.jsx:175–minLength="6"→minLength="8"frontend/src/pages/AccountSettingsPage.jsx:403–minLength={4}→minLength={8}
Verbleibende Lücke identifiziert im Re-Audit 2026-05-09: POST /api/auth/reset-password hatte kein Mindestlängen-Limit → siehe P-05b.
P-05b – reset-password Mindestlänge 8 Zeichen ✅
Status: Umgesetzt (2026-05-10)
Betroffene Dateien:
backend/models.py:29–PasswordResetConfirm.new_passwordbackend/tests/test_auth_password_reset_minlength.py– 7 neue Tests
Technische Änderung:
# models.py (vorher):
class PasswordResetConfirm(BaseModel):
token: str
new_password: str
# models.py (nachher):
class PasswordResetConfirm(BaseModel):
token: str
new_password: str = Field(min_length=8, max_length=128)
FastAPI lehnt Requests mit new_password < 8 Zeichen nun mit HTTP 422 (Pydantic Validation Error) ab, bevor der Endpoint-Handler ausgeführt wird. Kein DB-Zugriff erfolgt für unvalide Requests.
Tests: 7 Tests, alle grün:
- Leer-String → 422
- 1-Zeichen-Passwort → 422
- 7-Zeichen-Passwort (
"1234567") → 422 - Exakt 8 Zeichen (
"12345678") → 200 ✓ - Langes Passwort → 200 ✓
- Fehlendes
new_password-Feld → 422 - Gültiges Passwort, ungültiger Token → 400
P-05 vollständig geschlossen? Ja — alle passwortverarbeitenden Backend-Endpoints erzwingen jetzt Mindestlänge 8:
| Endpoint | Mindestlänge | Mechanismus |
|---|---|---|
POST /api/auth/register |
8 | if len(password) < 8 im Handler |
PUT /api/auth/pin |
8 | if len(new_pin) < 8 im Handler |
POST /api/auth/reset-password |
8 | Pydantic Field(min_length=8) |
| Management-Reset (profiles.py) | 8 | Pydantic Field(min_length=8) |
| Frontend LoginPage | 8 | minLength="8" |
| Frontend AccountSettingsPage | 8 | minLength={8} |
P-07 – ALLOW_PUBLIC_MEDIA_STATIC Release-Test ✅
Status: Umgesetzt
Betroffene Dateien:
backend/tests/test_security_release.py– 2 neue Tests
Tests: Beide grün.
P-12 – sessionStorage bei Logout bereinigen ✅
Status: Umgesetzt (2026-05-10, Version 0.8.68)
Betroffene Dateien:
frontend/src/context/AuthContext.jsx–logout()tests/dev-smoke-test.spec.js– neuer Playwright-Test
Befund (war offen):
logout() löschte nur localStorage-Einträge. TrainingCoachPage schrieb sessionStorage-Schlüssel mit Präfix sj_coach_ (sj_coach_step_{unitId}, sj_coach_deltas_{unitId}, sj_coach_debrief_{unitId}), die nach Logout im Tab erhalten blieben. Bei Nutzerwechsel im selben Tab (geteilter Rechner) konnte der neue Nutzer Trainingsfortschritt des Vorgängers sehen.
Technische Änderung:
Gezielte Präfix-Löschung aller sj_coach_*-Schlüssel beim Logout (kein sessionStorage.clear()). Fremde sessionStorage-Schlüssel (Browser-Extensions o. ä.) bleiben erhalten.
// AuthContext.jsx logout() — Ergänzung:
for (const key of Object.keys(sessionStorage)) {
if (key.startsWith('sj_coach_')) {
sessionStorage.removeItem(key)
}
}
Begründung gezielte statt vollständiger Löschung:
Alle Shinkan-spezifischen sessionStorage-Schlüssel sind eindeutig über den Präfix sj_coach_ identifizierbar (definiert in TrainingCoachPage.jsx Zeilen 15–25). Ein sessionStorage.clear() würde auch Schlüssel fremder Quellen im selben Tab löschen; die Präfix-Löschung ist spezifischer und sicherer.
Tests: Playwright E2E-Test tests/dev-smoke-test.spec.js (Test „P-12: sessionStorage wird bei Logout bereinigt"):
- Setzt drei
sj_coach_*-Schlüssel und einen fremden Schlüssel - Klickt „Abmelden"
- Prüft:
sj_coach_*→ null (entfernt), fremder Schlüssel → erhalten,authToken→ null (localStorage weiterhin korrekt bereinigt)
Hinweis Tests: Das Projekt verfügt über kein Frontend-Unit-Test-Framework (kein Vitest/Jest in package.json). Der Test ist als Playwright E2E-Test implementiert, der einen laufenden Dev-Server voraussetzt. Automatisierte Ausführung der Playwright-Tests erfordert npx playwright test mit gesetzter PLAYWRIGHT_BASE_URL.
P-23 – LoginPage: minLength + Versionsstring ✅
Status: Umgesetzt
Betroffene Dateien:
frontend/src/pages/LoginPage.jsx
Technische Änderung:
minLength="6"→minLength="8"- Hardcodierter Versionsstring
v0.1.0 • Developmententfernt
P-24 – CORS allow_methods und allow_headers einschränken ✅
Status: Umgesetzt
Betroffene Dateien:
backend/main.py:85-86
Technische Änderung:
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "X-Auth-Token", "X-Active-Club-Id"],
Test-Zusammenfassung (Stand 0.8.74)
tests/test_auth_password_reset_minlength.py 7 passed (neu, P-05b)
tests/test_media_assets_copyright_promotion.py 7 passed (gehärtet, P-04)
tests/test_security_release.py 9 passed (inkl. 2 P-07-Tests)
Weitere bestehende Tests: 81 passed, 6 skipped
Gesamt (Backend): 104 passed, 6 skipped, 1 failed
Fehlgeschlagener Test: test_list_media_assets_invalid_lifecycle_400
→ Pre-existing: benötigt laufenden PostgreSQL-Container (Hostname "postgres")
→ Bestand bereits vor allen Compliance-Änderungen (verifiziert per git stash)
Playwright E2E (dev-smoke-test.spec.js): 17 passed (inkl. P-01c-Tests)
→ P-01: 4× Route ohne Auth + Platzhalterhinweis + Reload, 1× Login-Links
→ P-01b: 3× /settings/legal (Link in Einstellungen, Überschrift, Rechtstext-Links)
→ P-01c: 3× Admin-Rechtstexte (Seitenladung, Route /admin/legal-documents, Admin-Nav)
→ P-12: sessionStorage-Bereinigung (grün)
Anmerkung jsPDF (0.8.74):
→ PDF-Download via pdf.save() ist ein Browser-Download, kein Server-Request.
→ Kein Backend-Test möglich; funktional im Browser verifizierbar.
→ Playwright-E2E-Test erfordert laufenden Dev-Server (npx playwright test).
P-06 – Upload-Einwilligungsdialog ⚠️ (technisch umgesetzt; juristische Validierung offen)
Status: Technisch umgesetzt (2026-05-11, Version 0.8.75) unter vorläufigen Erstannahmen — KRIT-04 bleibt offen.
Deklarationsversion: p06-v1-conservative
Betroffene Dateien:
backend/migrations/048_media_rights_declarations.sql(neu) — Append-only Deklarations-Log + 3 Schnellfelder inmedia_assetsbackend/media_rights.py(neu) — Zentrales Policy-Modul:validate_rights_declaration,check_rights_coverage,assert_rights_for_promotion,assert_rights_for_exercise_link,write_rights_declaration,update_rights_quick_fieldsbackend/routers/media_assets.py— P-06-Enforcement in Bulk-Upload, PATCH, Bulk-PATCH; 3 neue Endpointsbackend/routers/exercises.py— P-06 beiupload_exercise_media(neue Assets) undattach_exercise_media_from_assetfrontend/src/components/RightsDeclarationDialog.jsx(neu) — Einwilligungsdialog (9 Felder)frontend/src/pages/MediaLibraryPage.jsx— Dialog-Integration vor Bulk-Upload; Altbestand-Indikatorfrontend/src/utils/api.js—bulkUploadMediaAssetserweitert um P-06-Felderbackend/tests/test_media_rights_declaration.py(neu) — 17 Unit/HTTP-Teststests/dev-smoke-test.spec.js— 5 P-06 E2E-Tests
Neue Endpoints:
POST /api/media-assets/{id}/rights-declarations— Explizite Re-/NachdeklarationGET /api/admin/media-rights/legacy-summary— Zusammenfassung Altbestand (Plattform-Admin)GET /api/admin/media-rights/legacy-assets— Paginierte Liste Altbestand (Plattform-Admin)
Abweichung von Spec §3 (konservative Erstannahme):
Person-Fragen sind auch bei Sichtbarkeit private Pflicht (§10.1 in docs/p06-upload-rights-spec.md).
Altbestand (Legacy):
Alle vor Migration 048 hochgeladenen Medien erhalten rights_status = 'legacy_unreviewed'.
Promotion blockiert bis Nachdeklaration. In Bibliotheks-UI als „Altbestand ⚠" markiert.
KRIT-04 Status:
Offen. Juristische Validierung der Feldtexte, Einwilligungsformulierungen und Altbestand-Behandlung steht aus.
Referenz: docs/p06-upload-rights-spec.md §10.5.
Nicht umgesetzte Pakete
Paket-IDs und -Titel gemäß kanonischem Register
docs/compliance-package-register.md. Abweichende Beschreibungen in der Ursprungsversion dieses Abschnitts wurden am 2026-05-10 korrigiert (P-06, P-08, P-09, P-10, P-11, P-18 — Details im Konsistenzbericht des Registers).
| Paket | Kanonischer Titel | Status | Begründung |
|---|---|---|---|
| P-01 | Rechtstexte | offen | Scope ausgeschlossen (juristischer Inhalt) |
| P-02 | Self-Service-Kontolöschung + Datenexport | offen | Scope ausgeschlossen |
| P-06 | Upload-Einwilligungsdialog (Recht am eigenen Bild) | teilweise umgesetzt | Technisch umgesetzt (2026-05-11, v0.8.75) unter vorläufigen Erstannahmen p06-v1-conservative — siehe §P-06 unten. KRIT-04 bleibt offen bis juristische Validierung. |
| P-08 | HSTS / externe Proxy-Sicherheit dokumentieren | offen | Scope ausgeschlossen (außerhalb Repo — Reverse-Proxy) |
| P-09 | Admin-Audit-Log | offen | Scope ausgeschlossen |
| P-10 | Mindestalter-Abfrage | offen | Scope ausgeschlossen |
| P-11 | Legal-Hold Lifecycle-Status | offen | Scope ausgeschlossen |
| P-12 | sessionStorage bei Logout bereinigen | ✅ umgesetzt | Version 0.8.68 — siehe §P-12 oben |
| P-13 | Content-Melde-Backend | offen | Scope ausgeschlossen (erst juristisch klären) |
| P-14 | Moderations-UI | offen | Scope ausgeschlossen |
| P-15 | Uploader-Benachrichtigung bei Sperrung | offen | Scope ausgeschlossen |
| P-16 | Beschwerdeverfahren | offen | Scope ausgeschlossen |
| P-17 | MFA für Superadmins (TOTP) | offen | Scope ausgeschlossen |
| P-18 | HttpOnly-Cookie als Auth-Alternative | offen | Scope ausgeschlossen |
| P-19 | Anti-Virus-Scan (ClamAV) | offen | Scope ausgeschlossen |
| P-20 | VVT erstellen | offen | Scope ausgeschlossen (Betreiber-Aufgabe) |
| P-21 | AV-Verträge abschließen | offen | Scope ausgeschlossen (Betreiber-Aufgabe) |
| P-22 | HTML-Sanitizer für Rich-Text-Felder | offen | Scope ausgeschlossen |
Re-Audit-Empfehlung
Operativ nach Deployment 0.8.69 prüfen:
- P-03/P-03b:
docker logs shinkan-retention-cron— Job läuft täglich 03:00 Uhr; Retention-Zeiten: 30 → 30 Tage - P-04: Manuell: PATCH privates Medium auf
officialohnecopyright_notice→ muss 400 liefern - P-05b: Manuell: Reset-Link mit 7-Zeichen-Passwort → muss mit Fehler abgewiesen werden
- P-24: Browser DevTools Preflight →
Access-Control-Allow-Headers: content-type, x-auth-token, x-active-club-id
Nächster vollständiger Re-Audit nach Umsetzung der kritischen Findings (P-01: Impressum/Datenschutz, P-02: DSGVO-Löschanfragen).