Files
breakpilot-compliance/backend-compliance/compliance/schemas/banner.py
T
Benjamin Admin 44acd68c96 feat: Cookie-Banner ↔ Backend Integration (DSR, Retention, Consent Proof)
Phase 1: Vendor sync from service registry (82+ services → banner vendors)
Phase 2: Category-based retention (marketing=90d, statistics=790d, not hardcoded 365d)
Phase 3: DSR ↔ Banner email linking (link-email, by-email, Art.17 erasure, Art.15/20 export)
Phase 4: Consent sync (Banner → Einwilligungen bridge)
Phase 6: Consent proof (SHA256 config hash + config_version in audit log, Art. 7(1) DSGVO)

New files:
- banner_dsr_service.py — email linking + DSR integration
- vendor_banner_sync.py — service registry → vendor configs
- migration 106 — linked_email, banner_config_hash, consent_version columns

Tests: 20+ new backend tests + 2 Playwright E2E test suites (API + UI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 19:55:13 +02:00

102 lines
2.8 KiB
Python

"""
Banner consent schemas — cookie consent SDK + admin configuration.
Phase 1 Step 4: extracted from ``compliance.api.banner_routes`` so the
route layer becomes thin delegation to ``compliance.services.banner_*``.
"""
from typing import Any, List, Optional
from pydantic import BaseModel, ConfigDict
class ConsentCreate(BaseModel):
"""Request body for recording a device consent."""
site_id: str
device_fingerprint: str
categories: List[str] = []
vendors: List[str] = []
ip_address: Optional[str] = None
user_agent: Optional[str] = None
consent_string: Optional[str] = None
class SiteConfigCreate(BaseModel):
"""Request body for creating a banner site configuration."""
site_id: str
site_name: Optional[str] = None
site_url: Optional[str] = None
banner_title: Optional[str] = None
banner_description: Optional[str] = None
privacy_url: Optional[str] = None
imprint_url: Optional[str] = None
dsb_name: Optional[str] = None
dsb_email: Optional[str] = None
theme: Optional[dict[str, Any]] = None
tcf_enabled: bool = False
class SiteConfigUpdate(BaseModel):
"""Partial update for a banner site configuration."""
model_config = ConfigDict(extra="ignore")
site_name: Optional[str] = None
site_url: Optional[str] = None
banner_title: Optional[str] = None
banner_description: Optional[str] = None
privacy_url: Optional[str] = None
imprint_url: Optional[str] = None
dsb_name: Optional[str] = None
dsb_email: Optional[str] = None
theme: Optional[dict[str, Any]] = None
tcf_enabled: Optional[bool] = None
is_active: Optional[bool] = None
class CategoryConfigCreate(BaseModel):
"""Request body for adding a cookie category to a site."""
category_key: str
name_de: str
name_en: Optional[str] = None
description_de: Optional[str] = None
description_en: Optional[str] = None
is_required: bool = False
sort_order: int = 0
class VendorConfigCreate(BaseModel):
"""Request body for adding a vendor under a site's category."""
vendor_name: str
vendor_url: Optional[str] = None
category_key: str
description_de: Optional[str] = None
description_en: Optional[str] = None
cookie_names: List[str] = []
retention_days: int = 365
class LinkEmailRequest(BaseModel):
"""Request body for linking an email to a device fingerprint."""
site_id: str
device_fingerprint: str
email: str
class ConsentSyncRequest(BaseModel):
"""Request body for syncing banner consent to Einwilligungen."""
site_id: str
device_fingerprint: str
email: str
__all__ = [
"ConsentCreate",
"SiteConfigCreate",
"SiteConfigUpdate",
"CategoryConfigCreate",
"VendorConfigCreate",
"LinkEmailRequest",
"ConsentSyncRequest",
]