Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
276
backend-compliance/consent_api.py
Normal file
276
backend-compliance/consent_api.py
Normal file
@@ -0,0 +1,276 @@
|
||||
"""
|
||||
Consent API Endpoints für BreakPilot
|
||||
Stellt die Consent-Funktionalität über das BreakPilot Backend bereit
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Header, Query
|
||||
from typing import Optional, List
|
||||
from pydantic import BaseModel
|
||||
|
||||
from consent_client import consent_client, DocumentType, generate_demo_token, generate_jwt_token
|
||||
|
||||
router = APIRouter(prefix="/consent", tags=["consent"])
|
||||
|
||||
|
||||
# Request/Response Models
|
||||
class ConsentRequest(BaseModel):
|
||||
document_type: str
|
||||
version_id: str
|
||||
consented: bool = True
|
||||
|
||||
|
||||
class CookieConsentItem(BaseModel):
|
||||
category_id: str
|
||||
consented: bool
|
||||
|
||||
|
||||
class CookieConsentRequest(BaseModel):
|
||||
categories: List[CookieConsentItem]
|
||||
|
||||
|
||||
class DataDeletionRequest(BaseModel):
|
||||
reason: Optional[str] = None
|
||||
|
||||
|
||||
# Helper für JWT Token
|
||||
def get_token(authorization: Optional[str], allow_demo: bool = True) -> str:
|
||||
"""
|
||||
Extrahiert JWT Token aus Authorization Header.
|
||||
Falls kein Token vorhanden und allow_demo=True, wird ein Demo-Token generiert.
|
||||
"""
|
||||
if authorization:
|
||||
parts = authorization.split(" ")
|
||||
if len(parts) == 2 and parts[0] == "Bearer":
|
||||
return parts[1]
|
||||
|
||||
if allow_demo:
|
||||
# Generiere einen Demo-Token für nicht-authentifizierte Benutzer
|
||||
return generate_demo_token()
|
||||
|
||||
raise HTTPException(status_code=401, detail="Authorization header required")
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Token Endpoints
|
||||
# ==========================================
|
||||
|
||||
@router.get("/token/demo")
|
||||
async def get_demo_token():
|
||||
"""
|
||||
Generiert einen Demo-Token für nicht-authentifizierte Benutzer.
|
||||
Dieser Token ermöglicht das Lesen von öffentlichen Dokumenten.
|
||||
"""
|
||||
token = generate_demo_token()
|
||||
return {
|
||||
"token": token,
|
||||
"type": "Bearer",
|
||||
"expires_in": 86400 # 24 Stunden
|
||||
}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Public Endpoints
|
||||
# ==========================================
|
||||
|
||||
@router.get("/check/{document_type}")
|
||||
async def check_consent(
|
||||
document_type: str,
|
||||
language: str = Query("de"),
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""
|
||||
Prüft ob der Benutzer einem Dokument zugestimmt hat.
|
||||
Gibt zurück ob Zustimmung vorliegt und ob sie aktualisiert werden muss.
|
||||
"""
|
||||
token = get_token(authorization)
|
||||
|
||||
try:
|
||||
doc_type = DocumentType(document_type)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid document type: {document_type}")
|
||||
|
||||
status = await consent_client.check_consent(token, doc_type, language)
|
||||
|
||||
return {
|
||||
"has_consent": status.has_consent,
|
||||
"current_version_id": status.current_version_id,
|
||||
"consented_version": status.consented_version,
|
||||
"needs_update": status.needs_update,
|
||||
"consented_at": status.consented_at
|
||||
}
|
||||
|
||||
|
||||
@router.get("/pending")
|
||||
async def get_pending_consents(
|
||||
language: str = Query("de"),
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""
|
||||
Gibt alle Dokumente zurück, die noch Zustimmung benötigen.
|
||||
Nützlich für Anzeige beim Login oder in den Einstellungen.
|
||||
"""
|
||||
token = get_token(authorization)
|
||||
pending = await consent_client.get_pending_consents(token, language)
|
||||
|
||||
return {
|
||||
"pending_consents": pending,
|
||||
"has_pending": len(pending) > 0
|
||||
}
|
||||
|
||||
|
||||
@router.get("/documents/{document_type}/latest")
|
||||
async def get_latest_document(
|
||||
document_type: str,
|
||||
language: str = Query("de"),
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""Holt die aktuellste Version eines rechtlichen Dokuments"""
|
||||
token = get_token(authorization)
|
||||
|
||||
doc = await consent_client.get_latest_document(token, document_type, language)
|
||||
|
||||
if not doc:
|
||||
raise HTTPException(status_code=404, detail="Document not found")
|
||||
|
||||
return {
|
||||
"id": doc.id,
|
||||
"document_id": doc.document_id,
|
||||
"version": doc.version,
|
||||
"language": doc.language,
|
||||
"title": doc.title,
|
||||
"content": doc.content,
|
||||
"summary": doc.summary
|
||||
}
|
||||
|
||||
|
||||
@router.post("/give")
|
||||
async def give_consent(
|
||||
request: ConsentRequest,
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""Speichert die Zustimmung des Benutzers zu einem Dokument"""
|
||||
token = get_token(authorization)
|
||||
|
||||
success = await consent_client.give_consent(
|
||||
token,
|
||||
request.document_type,
|
||||
request.version_id,
|
||||
request.consented
|
||||
)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Failed to save consent")
|
||||
|
||||
return {"success": True, "message": "Consent saved successfully"}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Cookie Consent Endpoints
|
||||
# ==========================================
|
||||
|
||||
@router.get("/cookies/categories")
|
||||
async def get_cookie_categories(
|
||||
language: str = Query("de"),
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""Holt alle Cookie-Kategorien für das Cookie-Banner"""
|
||||
token = get_token(authorization)
|
||||
categories = await consent_client.get_cookie_categories(token, language)
|
||||
|
||||
return {"categories": categories}
|
||||
|
||||
|
||||
@router.post("/cookies")
|
||||
async def set_cookie_consent(
|
||||
request: CookieConsentRequest,
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""Speichert die Cookie-Präferenzen des Benutzers"""
|
||||
token = get_token(authorization)
|
||||
|
||||
categories = [
|
||||
{"category_id": cat.category_id, "consented": cat.consented}
|
||||
for cat in request.categories
|
||||
]
|
||||
|
||||
success = await consent_client.set_cookie_consent(token, categories)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Failed to save cookie preferences")
|
||||
|
||||
return {"success": True, "message": "Cookie preferences saved"}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# GDPR / Privacy Endpoints
|
||||
# ==========================================
|
||||
|
||||
@router.get("/privacy/my-data")
|
||||
async def get_my_data(authorization: Optional[str] = Header(None)):
|
||||
"""
|
||||
GDPR Art. 15: Auskunftsrecht
|
||||
Gibt alle über den Benutzer gespeicherten Daten zurück.
|
||||
"""
|
||||
token = get_token(authorization)
|
||||
data = await consent_client.get_my_data(token)
|
||||
|
||||
if not data:
|
||||
raise HTTPException(status_code=500, detail="Failed to retrieve data")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@router.post("/privacy/export")
|
||||
async def request_data_export(authorization: Optional[str] = Header(None)):
|
||||
"""
|
||||
GDPR Art. 20: Recht auf Datenübertragbarkeit
|
||||
Fordert einen Export aller Benutzerdaten an.
|
||||
"""
|
||||
token = get_token(authorization)
|
||||
request_id = await consent_client.request_data_export(token)
|
||||
|
||||
if not request_id:
|
||||
raise HTTPException(status_code=500, detail="Failed to create export request")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"request_id": request_id,
|
||||
"message": "Export request created. You will be notified when ready."
|
||||
}
|
||||
|
||||
|
||||
@router.post("/privacy/delete")
|
||||
async def request_data_deletion(
|
||||
request: DataDeletionRequest,
|
||||
authorization: Optional[str] = Header(None)
|
||||
):
|
||||
"""
|
||||
GDPR Art. 17: Recht auf Löschung
|
||||
Fordert die Löschung aller Benutzerdaten an.
|
||||
"""
|
||||
token = get_token(authorization)
|
||||
request_id = await consent_client.request_data_deletion(token, request.reason)
|
||||
|
||||
if not request_id:
|
||||
raise HTTPException(status_code=500, detail="Failed to create deletion request")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"request_id": request_id,
|
||||
"message": "Deletion request created. We will process it within 30 days."
|
||||
}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Health Check
|
||||
# ==========================================
|
||||
|
||||
@router.get("/health")
|
||||
async def consent_health():
|
||||
"""Prüft die Verbindung zum Consent Service"""
|
||||
is_healthy = await consent_client.health_check()
|
||||
|
||||
return {
|
||||
"consent_service": "healthy" if is_healthy else "unavailable",
|
||||
"connected": is_healthy
|
||||
}
|
||||
Reference in New Issue
Block a user