first commit
This commit is contained in:
184
app/core/affidavit.py
Normal file
184
app/core/affidavit.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# core/affidavit.py
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional, List
|
||||
import hashlib
|
||||
import json
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.affidavit_logs.models import Operation, StatusOP, AffidavitRecord
|
||||
|
||||
|
||||
class LegalAffidavit:
|
||||
"""
|
||||
Certificador Legal Digital
|
||||
Cada operación genera un acta notarial inmutable
|
||||
"""
|
||||
|
||||
def __init__(self, db: Session, user, operation: str, client_id: int = None):
|
||||
self.db = db
|
||||
self.user = user
|
||||
self.operation = operation
|
||||
self.client_id = client_id
|
||||
self.timestamp = datetime.utcnow()
|
||||
self.entities_affected: List[Dict] = []
|
||||
self.rules_validated: List[Dict] = []
|
||||
self.evidence: List[Dict] = []
|
||||
self._record = None
|
||||
|
||||
def _get_operation_enum(self) -> Operation:
|
||||
"""Convierte string de operación a Enum Operation"""
|
||||
operation_map = {
|
||||
"create": Operation.CREATED,
|
||||
"CREATE": Operation.CREATED,
|
||||
"update": Operation.UPDATED,
|
||||
"UPDATE": Operation.UPDATED,
|
||||
"delete": Operation.DELETED,
|
||||
"DELETE": Operation.DELETED,
|
||||
"cancel": Operation.CANCELED,
|
||||
"CANCEL": Operation.CANCELED,
|
||||
"tried": Operation.TRIED,
|
||||
"TRIED": Operation.TRIED,
|
||||
}
|
||||
return operation_map.get(self.operation.lower(), Operation.TRIED)
|
||||
|
||||
def _get_status_enum(self, success: bool) -> StatusOP:
|
||||
"""Convierte resultado a StatusOP"""
|
||||
if success:
|
||||
return StatusOP.COMPLETED
|
||||
return StatusOP.INVALID
|
||||
|
||||
def _hash_data(self, data: Any) -> str:
|
||||
"""Genera hash para integridad de datos"""
|
||||
data_str = json.dumps(data, sort_keys=True, default=str)
|
||||
return hashlib.sha256(data_str.encode()).hexdigest()
|
||||
|
||||
def certify_entity(self, entity_type: str, entity_id: int, entity_data: Dict) -> 'LegalAffidavit':
|
||||
"""Certifica que una entidad fue afectada"""
|
||||
self.entities_affected.append({
|
||||
"type": entity_type,
|
||||
"id": entity_id,
|
||||
"data_hash": self._hash_data(entity_data),
|
||||
"timestamp": self.timestamp.isoformat()
|
||||
})
|
||||
return self
|
||||
|
||||
def validate_rule(self, rule_name: str, rule_result: bool, rule_details: str = "") -> 'LegalAffidavit':
|
||||
"""Valida una regla de negocio"""
|
||||
self.rules_validated.append({
|
||||
"rule": rule_name,
|
||||
"passed": rule_result,
|
||||
"details": rule_details,
|
||||
"validated_at": self.timestamp.isoformat()
|
||||
})
|
||||
|
||||
if not rule_result:
|
||||
raise AffidavitValidationError(f"Regla '{rule_name}' no cumplida: {rule_details}")
|
||||
|
||||
return self
|
||||
|
||||
def add_evidence(self, evidence_type: str, evidence_data: Any) -> 'LegalAffidavit':
|
||||
"""Agrega evidencia digital"""
|
||||
self.evidence.append({
|
||||
"type": evidence_type,
|
||||
"data": evidence_data,
|
||||
"hash": self._hash_data(evidence_data),
|
||||
"timestamp": self.timestamp.isoformat()
|
||||
})
|
||||
return self
|
||||
|
||||
def _generate_signature(self) -> str:
|
||||
"""Genera firma digital del acta"""
|
||||
data_to_sign = f"{self.user.id}{self.timestamp.isoformat()}{self.operation}{json.dumps(self.entities_affected, sort_keys=True)}"
|
||||
return hashlib.sha512(data_to_sign.encode()).hexdigest()
|
||||
|
||||
def _build_description(self) -> str:
|
||||
"""Construye la descripción del acta"""
|
||||
entities = ", ".join([f"{e['type']}:{e['id']}" for e in self.entities_affected])
|
||||
rules = ", ".join([r['rule'] for r in self.rules_validated if r['passed']])
|
||||
|
||||
desc = f"Operación {self.operation} por usuario {self.user.email}"
|
||||
if entities:
|
||||
desc += f" | Entidades: {entities}"
|
||||
if rules:
|
||||
desc += f" | Reglas: {rules}"
|
||||
return desc
|
||||
|
||||
def sign(self, success: bool = True) -> Dict:
|
||||
"""
|
||||
Firma y guarda el acta notarial en la base de datos
|
||||
"""
|
||||
description = self._build_description()
|
||||
signature = self._generate_signature()
|
||||
status = self._get_status_enum(success)
|
||||
operation_enum = self._get_operation_enum()
|
||||
|
||||
# Crear registro en BD
|
||||
self._record = AffidavitRecord(
|
||||
operation=operation_enum,
|
||||
user_id=self.user.id,
|
||||
client_id=self.client_id,
|
||||
description=description,
|
||||
adjustment=json.dumps(self.evidence) if self.evidence else None,
|
||||
status=status,
|
||||
sign=signature,
|
||||
created_at=self.timestamp,
|
||||
created_by=self.user.id
|
||||
)
|
||||
|
||||
self.db.add(self._record)
|
||||
self.db.commit()
|
||||
self.db.refresh(self._record)
|
||||
|
||||
return {
|
||||
"affidavit_id": self._record.id,
|
||||
"operation": self.operation,
|
||||
"user": {
|
||||
"id": self.user.id,
|
||||
"email": self.user.email,
|
||||
"user_type": self.user.user_type,
|
||||
"rol_operativo": self.user.rol_operativo
|
||||
},
|
||||
"timestamp": self.timestamp.isoformat(),
|
||||
"description": description,
|
||||
"status": status.value,
|
||||
"signature": signature,
|
||||
"entities_affected": self.entities_affected,
|
||||
"rules_validated": self.rules_validated
|
||||
}
|
||||
|
||||
def get_record_id(self) -> Optional[int]:
|
||||
"""Retorna el ID del registro creado"""
|
||||
return self._record.id if self._record else None
|
||||
|
||||
|
||||
class AffidavitValidationError(Exception):
|
||||
"""Error de validación notarial"""
|
||||
pass
|
||||
|
||||
|
||||
def certify_operation(operation_name: str):
|
||||
"""Decorador para certificar operaciones automáticamente"""
|
||||
def decorator(func):
|
||||
async def wrapper(*args, **kwargs):
|
||||
db = kwargs.get('db')
|
||||
user = kwargs.get('current_user')
|
||||
client_id = kwargs.get('client_id')
|
||||
|
||||
if not db and args and hasattr(args[0], 'db'):
|
||||
db = args[0].db
|
||||
|
||||
if db and user:
|
||||
affidavit = LegalAffidavit(db, user, operation_name, client_id)
|
||||
kwargs['affidavit'] = affidavit
|
||||
|
||||
try:
|
||||
result = await func(*args, **kwargs)
|
||||
affidavit.sign(success=True)
|
||||
return result
|
||||
except Exception as e:
|
||||
affidavit.sign(success=False)
|
||||
raise
|
||||
|
||||
return await func(*args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
Reference in New Issue
Block a user