""" PII-Maskierung für MCP-Ausgaben. Bei readonly- und editor-Rollen werden folgende Felder maskiert: - iban → "****" + letzte 4 Stellen - email → "***@" + Domain - telefon → "****" + letzte 4 Ziffern - geburtsdatum → nur Jahreszahl - jaehrliches_einkommen / monatliche_bezuege / vermoegen → Bereichsangabe Admin-Rolle erhält ungemaskierte Daten. """ import re from decimal import Decimal def mask_iban(value: str | None) -> str | None: if not value: return value clean = value.replace(" ", "") if len(clean) > 4: return "****" + clean[-4:] return "****" def mask_email(value: str | None) -> str | None: if not value: return value parts = value.split("@", 1) if len(parts) == 2: return "***@" + parts[1] return "***" def mask_telefon(value: str | None) -> str | None: if not value: return value digits = re.sub(r"\D", "", value) if len(digits) > 4: return "****" + digits[-4:] return "****" def mask_geburtsdatum(value) -> str | None: """Zeigt nur das Jahr des Geburtsdatums.""" if not value: return None try: return str(value)[:4] # "YYYY-MM-DD" → "YYYY" except Exception: return None def mask_einkommen(value) -> str | None: """Gibt Einkommensbereich statt genauen Wert zurück.""" if value is None: return None try: amount = float(value) if amount < 10000: return "< 10.000 €" elif amount < 20000: return "10.000–20.000 €" elif amount < 30000: return "20.000–30.000 €" elif amount < 50000: return "30.000–50.000 €" elif amount < 75000: return "50.000–75.000 €" else: return "> 75.000 €" except (TypeError, ValueError): return None def mask_monatsbezuege(value) -> str | None: """Gibt Monatsbezüge-Bereich statt genauen Wert zurück.""" if value is None: return None try: amount = float(value) if amount < 500: return "< 500 €/Mon." elif amount < 1000: return "500–1.000 €/Mon." elif amount < 2000: return "1.000–2.000 €/Mon." elif amount < 3000: return "2.000–3.000 €/Mon." else: return "> 3.000 €/Mon." except (TypeError, ValueError): return None # PII-Felder nach Modell PII_FIELDS: dict[str, dict] = { "destinataer": { "iban": mask_iban, "email": mask_email, "telefon": mask_telefon, "geburtsdatum": mask_geburtsdatum, "jaehrliches_einkommen": mask_einkommen, "monatliche_bezuege": mask_monatsbezuege, "vermoegen": mask_einkommen, }, "paechter": { "iban": mask_iban, "email": mask_email, "telefon": mask_telefon, "geburtsdatum": mask_geburtsdatum, }, "rentmeister": { "iban": mask_iban, "email": mask_email, "telefon": mask_telefon, }, } def apply_privacy_filter(data: dict, model_type: str, role: str) -> dict: """ Maskiert PII-Felder in einem Daten-Dictionary basierend auf Rolle und Modelltyp. Args: data: Rohdaten-Dictionary model_type: Modelltyp (z.B. "destinataer", "paechter") role: Aktuelle Rolle ("readonly", "editor", "admin") Returns: Gefiltertes Dictionary (bei admin: unveränderter Input) """ from .auth import can_read_unmasked if can_read_unmasked(role): return data maskers = PII_FIELDS.get(model_type, {}) if not maskers: return data result = dict(data) for field, mask_fn in maskers.items(): if field in result: result[field] = mask_fn(result[field]) return result def apply_privacy_filter_list(items: list[dict], model_type: str, role: str) -> list[dict]: """Wendet apply_privacy_filter auf eine Liste von Dicts an.""" return [apply_privacy_filter(item, model_type, role) for item in items]