|
|
""" |
|
|
M贸dulo de integraci贸n con AWS QLDB para registro regulatorio (AI Act y GDPR) |
|
|
|
|
|
NOTA: Esta implementaci贸n est谩 comentada provisionalmente para despliegue futuro. |
|
|
Cuando se active, requerir谩: |
|
|
- AWS Credentials configuradas |
|
|
- Acceso a QLDB Ledger |
|
|
- Permisos adecuados |
|
|
""" |
|
|
|
|
|
import os |
|
|
import json |
|
|
import hashlib |
|
|
import time |
|
|
from datetime import datetime, timezone |
|
|
from typing import Dict, Any, Optional |
|
|
from dataclasses import dataclass, asdict |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
class ComplianceRecord: |
|
|
"""Registro de cumplimiento para AWS QLDB""" |
|
|
user_email: str |
|
|
user_name: str |
|
|
video_title: str |
|
|
video_hash: str |
|
|
video_size: int |
|
|
timestamp: str |
|
|
consent_accepted: bool |
|
|
consent_version: str |
|
|
ip_address: str |
|
|
user_agent: str |
|
|
|
|
|
|
|
|
validators_notified: bool = False |
|
|
validation_status: str = "pending" |
|
|
validator_emails: list = None |
|
|
validation_timestamp: str = None |
|
|
|
|
|
|
|
|
session_id: str = None |
|
|
space_id: str = None |
|
|
|
|
|
class QLDBManager: |
|
|
"""Gestor de registros en AWS QLDB (comentado hasta activaci贸n)""" |
|
|
|
|
|
def __init__(self, ledger_name: str = "veureu-compliance"): |
|
|
self.ledger_name = ledger_name |
|
|
|
|
|
|
|
|
|
|
|
def _compute_video_hash(self, video_bytes: bytes) -> str: |
|
|
"""Calcula hash SHA-256 del v铆deo para integridad""" |
|
|
return hashlib.sha256(video_bytes).hexdigest() |
|
|
|
|
|
def _create_compliance_record(self, user_info: Dict[str, Any], |
|
|
video_info: Dict[str, Any], |
|
|
consent_data: Dict[str, Any]) -> ComplianceRecord: |
|
|
"""Crea registro de cumplimiento""" |
|
|
|
|
|
|
|
|
user_email = user_info.get('email', '[email protected]') |
|
|
user_name = user_info.get('name', 'Unknown User') |
|
|
|
|
|
|
|
|
video_bytes = video_info.get('bytes', b'') |
|
|
video_hash = self._compute_video_hash(video_bytes) |
|
|
|
|
|
|
|
|
timestamp = datetime.now(timezone.utc).isoformat() |
|
|
|
|
|
|
|
|
session_id = os.urandom(16).hex() |
|
|
space_id = os.getenv('SPACE_ID', 'local-dev') |
|
|
|
|
|
return ComplianceRecord( |
|
|
user_email=user_email, |
|
|
user_name=user_name, |
|
|
video_title=video_info.get('name', 'unknown_video.mp4'), |
|
|
video_hash=video_hash, |
|
|
video_size=len(video_bytes), |
|
|
timestamp=timestamp, |
|
|
consent_accepted=consent_data.get('all_accepted', False), |
|
|
consent_version="1.0", |
|
|
ip_address=self._get_client_ip(), |
|
|
user_agent=self._get_user_agent(), |
|
|
session_id=session_id, |
|
|
space_id=space_id, |
|
|
validators_notified=False, |
|
|
validation_status="pending", |
|
|
validator_emails=[] |
|
|
) |
|
|
|
|
|
def _get_client_ip(self) -> str: |
|
|
"""Obtiene IP del cliente (simulado)""" |
|
|
|
|
|
return "127.0.0.1" |
|
|
|
|
|
def _get_user_agent(self) -> str: |
|
|
"""Obtiene User-Agent del cliente""" |
|
|
return "Streamlit/1.0" |
|
|
|
|
|
def record_user_consent(self, user_info: Dict[str, Any], |
|
|
video_info: Dict[str, Any], |
|
|
consent_data: Dict[str, Any]) -> Optional[str]: |
|
|
""" |
|
|
Registra consentimiento del usuario en QLDB |
|
|
|
|
|
Returns: |
|
|
Document ID del registro creado o None si hay error |
|
|
""" |
|
|
try: |
|
|
record = self._create_compliance_record(user_info, video_info, consent_data) |
|
|
|
|
|
|
|
|
""" |
|
|
# Insertar en QLDB |
|
|
result = self.client.execute_statement( |
|
|
LedgerName=self.ledger_name, |
|
|
Statement='INSERT INTO compliance_records ?', |
|
|
Parameters=[asdict(record)] |
|
|
) |
|
|
|
|
|
document_id = result.get('Documents', [{}])[0].get('Id') |
|
|
return document_id |
|
|
""" |
|
|
|
|
|
|
|
|
simulated_id = f"qldb_doc_{int(time.time())}_{hash(record.user_email) % 10000}" |
|
|
print(f"[QLDB - SIMULATED] Registrado consentimiento: {simulated_id}") |
|
|
print(f"[QLDB - SIMULATED] Usuario: {record.user_email}") |
|
|
print(f"[QLDB - SIMULATED] V铆deo: {record.video_title} ({record.video_hash[:16]}...)") |
|
|
|
|
|
return simulated_id |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[QLDB ERROR] Error registrando consentimiento: {e}") |
|
|
return None |
|
|
|
|
|
def record_validator_decision(self, document_id: str, |
|
|
validator_email: str, |
|
|
decision: str, |
|
|
comments: str = "") -> bool: |
|
|
""" |
|
|
Registra decisi贸n del validador en QLDB |
|
|
|
|
|
Args: |
|
|
document_id: ID del documento original |
|
|
validator_email: Email del validador |
|
|
decision: "approved" o "rejected" |
|
|
comments: Comentarios opcionales |
|
|
|
|
|
Returns: |
|
|
True si 茅xito, False si error |
|
|
""" |
|
|
try: |
|
|
timestamp = datetime.now(timezone.utc).isoformat() |
|
|
|
|
|
|
|
|
""" |
|
|
# Actualizar documento en QLDB |
|
|
statement = f''' |
|
|
UPDATE compliance_records AS r |
|
|
SET r.validation_status = ?, |
|
|
r.validation_timestamp = ?, |
|
|
r.validator_emails = LIST_APPEND(r.validator_emails, ?) |
|
|
WHERE r.id = ? |
|
|
''' |
|
|
|
|
|
self.client.execute_statement( |
|
|
LedgerName=self.ledger_name, |
|
|
Statement=statement, |
|
|
Parameters=[decision, timestamp, validator_email, document_id] |
|
|
) |
|
|
""" |
|
|
|
|
|
|
|
|
print(f"[QLDB - SIMULATED] Registrada validaci贸n: {decision}") |
|
|
print(f"[QLDB - SIMULATED] Documento: {document_id}") |
|
|
print(f"[QLDB - SIMULATED] Validador: {validator_email}") |
|
|
print(f"[QLDB - SIMULATED] Timestamp: {timestamp}") |
|
|
|
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[QLDB ERROR] Error registrando validaci贸n: {e}") |
|
|
return False |
|
|
|
|
|
def get_compliance_record(self, document_id: str) -> Optional[Dict[str, Any]]: |
|
|
""" |
|
|
Obtiene registro de cumplimiento desde QLDB |
|
|
|
|
|
Returns: |
|
|
Diccionario con el registro o None si no existe |
|
|
""" |
|
|
try: |
|
|
|
|
|
""" |
|
|
result = self.client.execute_statement( |
|
|
LedgerName=self.ledger_name, |
|
|
Statement='SELECT * FROM compliance_records WHERE id = ?', |
|
|
Parameters=[document_id] |
|
|
) |
|
|
|
|
|
documents = result.get('Documents', []) |
|
|
return documents[0] if documents else None |
|
|
""" |
|
|
|
|
|
|
|
|
return { |
|
|
"id": document_id, |
|
|
"status": "simulated", |
|
|
"message": "QLDB integration pending activation" |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[QLDB ERROR] Error obteniendo registro: {e}") |
|
|
return None |
|
|
|
|
|
def publish_monthly_digest_to_polygon(self, period: str) -> Optional[str]: |
|
|
""" |
|
|
Publica digest mensual de autorizaciones en Polygon blockchain |
|
|
|
|
|
Args: |
|
|
period: Per铆odo en formato YYYY-MM (ej: "2025-11") |
|
|
|
|
|
Returns: |
|
|
Hash de transacci贸n o None si error |
|
|
""" |
|
|
try: |
|
|
|
|
|
""" |
|
|
statement = ''' |
|
|
SELECT user_email, video_hash, timestamp, consent_accepted, |
|
|
validation_status, document_id |
|
|
FROM compliance_records |
|
|
WHERE SUBSTRING(timestamp, 1, 7) = ? |
|
|
ORDER BY timestamp |
|
|
''' |
|
|
|
|
|
result = self.client.execute_statement( |
|
|
LedgerName=self.ledger_name, |
|
|
Statement=statement, |
|
|
Parameters=[period] |
|
|
) |
|
|
|
|
|
authorizations = result.get('Documents', []) |
|
|
""" |
|
|
|
|
|
|
|
|
authorizations = [ |
|
|
{ |
|
|
"user_email": "[email protected]", |
|
|
"video_hash": "abc123...", |
|
|
"timestamp": f"{period}-15T10:00:00Z", |
|
|
"consent_accepted": True, |
|
|
"validation_status": "approved", |
|
|
"document_id": f"doc_{period}_001" |
|
|
}, |
|
|
{ |
|
|
"user_email": "[email protected]", |
|
|
"video_hash": "def456...", |
|
|
"timestamp": f"{period}-20T14:30:00Z", |
|
|
"consent_accepted": True, |
|
|
"validation_status": "approved", |
|
|
"document_id": f"doc_{period}_002" |
|
|
} |
|
|
] |
|
|
|
|
|
if not authorizations: |
|
|
print(f"[QLDB] No hay autorizaciones para el per铆odo {period}") |
|
|
return None |
|
|
|
|
|
|
|
|
""" |
|
|
from polygon_digest import digest_publisher |
|
|
digest_record = digest_publisher.publish_monthly_digest(authorizations) |
|
|
|
|
|
if digest_record: |
|
|
return digest_record.transaction_hash |
|
|
""" |
|
|
|
|
|
|
|
|
print(f"[QLDB - SIMULATED] Publicando digest de {len(authorizations)} autorizaciones") |
|
|
simulated_tx_hash = f"0x{'0123456789abcdef' * 4}" |
|
|
print(f"[QLDB - SIMULATED] Digest publicado en Polygon: {simulated_tx_hash}") |
|
|
|
|
|
return simulated_tx_hash |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[QLDB ERROR] Error publicando digest mensual: {e}") |
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qldb_manager = QLDBManager() |
|
|
|