This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/consent_api.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

277 lines
7.7 KiB
Python

"""
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
}