Datenschutz: Bild-Metadaten, private Vorschläge, fremde Bilder, API-Doku
- Uploads: Server entfernt EXIF/XMP/Text (GPS-Standort, Kamera, Uhrzeit) per Pillow; Drehung von Hochkant-Fotos wird vorher angewendet, Farbprofil bleibt; GIFs unverändert (neue Abhängigkeit pillow) - Produktvorschläge pro Liste (Tabelle list_products): Einträge privater Listen tauchen bei anderen weder in Vorschlägen noch beim Rezeptabgleich auf; "Vorschläge verwalten" löscht nur aus eigenen sichtbaren Listen - Artikel: Bilder nur noch aus den eigenen Uploads (kein Tracking-Pixel fremder Server); Bereinigung in htmlclean.py - /api/docs und /api/openapi.json nur noch im Entwicklungsmodus (RELOAD=1 oder P42_API_DOCS=1) - Migration Schritt 2: bestehende Vorschläge den Listen zuordnen, Artikel neu bereinigen, gespeicherte Bilder mit Metadaten säubern - Tests in test_datenschutz.py (u. a. Rezept-Zuordnung, Umstellung) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
419d200bf9
commit
bb20135876
11 files changed
+367
-61
No files matched your search
@@ -6,8 +6,16 @@ SCHEMA in db.py beschreibt immer den neuesten Stand. Bei einer Änderung am Sche
|
||||
Jeder Schritt läuft genau einmal, in einer Transaktion mit dem Hochzählen der Nummer.
|
||||
Tests: tests/test_migrationen.py (alte Stände nachbauen und umstellen).
|
||||
"""
|
||||
from .common import now_iso
|
||||
import logging
|
||||
import re
|
||||
|
||||
from .bilder import hat_metadaten, ohne_metadaten
|
||||
from .common import ValidationError, now_iso
|
||||
from .config import DATA_DIR
|
||||
from .db import HOUSEHOLD_TABLE, KANBAN_PROJECTS_TABLE, KANBAN_TASKS_TABLE
|
||||
from .htmlclean import sanitize
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start(conn) -> None:
|
||||
@@ -176,7 +184,43 @@ def _kanban_projects(conn) -> None:
|
||||
conn.execute("DROP TABLE kanban_tasks_alt")
|
||||
|
||||
|
||||
# ---------- Schritt 2: Datenschutz ----------
|
||||
|
||||
|
||||
def _v2_datenschutz(conn) -> None:
|
||||
"""Vorschläge pro Liste, Artikel ohne fremde Bilder, hochgeladene Bilder ohne Metadaten (GPS usw.)."""
|
||||
# Vorschläge den Listen zuordnen: was gerade auf einer Liste steht, gehört zu dieser Liste;
|
||||
# alles Übrige stammt aus der Zeit vor den privaten Listen und gehört zu den Listen, die alle sehen
|
||||
conn.execute(
|
||||
"""INSERT OR IGNORE INTO list_products (list_id, key, uses, last_used)
|
||||
SELECT DISTINCT i.list_id, p.key, p.uses, p.last_used FROM household_items i JOIN products p ON p.key = i.key"""
|
||||
)
|
||||
conn.execute(
|
||||
"""INSERT OR IGNORE INTO list_products (list_id, key, uses, last_used)
|
||||
SELECT l.id, p.key, p.uses, p.last_used FROM products p JOIN shopping_lists l ON l.everyone = 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM household_items i WHERE i.key = p.key)"""
|
||||
)
|
||||
# Artikel mit den neuen Regeln bereinigen (Bilder nur noch aus den eigenen Uploads)
|
||||
for row in conn.execute("SELECT id, content FROM projects").fetchall():
|
||||
cleaned = sanitize(row["content"] or "")
|
||||
if cleaned != (row["content"] or ""):
|
||||
conn.execute("UPDATE projects SET content = ? WHERE id = ?", (cleaned, row["id"]))
|
||||
# Hochgeladene Bilder: nur solche mit Metadaten neu schreiben (die anderen bleiben unverändert)
|
||||
uploads = DATA_DIR / "uploads"
|
||||
if uploads.is_dir():
|
||||
for f in uploads.iterdir():
|
||||
if not re.fullmatch(r"[a-f0-9]{24}\.(jpg|png|webp)", f.name):
|
||||
continue
|
||||
data = f.read_bytes()
|
||||
if hat_metadaten(data):
|
||||
try:
|
||||
f.write_bytes(ohne_metadaten(data, f.suffix[1:]))
|
||||
except ValidationError:
|
||||
log.warning("Bild %s konnte nicht bereinigt werden", f.name)
|
||||
|
||||
|
||||
# Reihenfolge = Versionsnummer (Index + 1). Nur anhängen, nie umsortieren oder entfernen.
|
||||
STEPS = [
|
||||
_v1_altbestand,
|
||||
_v2_datenschutz,
|
||||
]
|
||||
Reference in new issue
Block a user