feat(roadmap): implementar Sprints 1, 2 y 3 (MVC, Stateless JWT API y Spring Security)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
from typing import Optional, List, Dict, Any
|
||||
from flask import request, g, has_request_context
|
||||
from flask_login import current_user
|
||||
import json
|
||||
from app import db
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.user import User
|
||||
|
||||
class AuditService:
|
||||
"""Servicio para la captura y consulta de la bitácora inmutable de auditoría."""
|
||||
|
||||
@staticmethod
|
||||
def log(action: str, module: str, entity_id: Optional[int] = None,
|
||||
details: Optional[Any] = None, user: Optional[User] = None,
|
||||
ip_address: Optional[str] = None) -> AuditLog:
|
||||
"""
|
||||
Registra un evento de auditoría de forma segura, infiriendo el usuario e IP
|
||||
desde el contexto actual de la petición si no se especifican.
|
||||
"""
|
||||
user_id = None
|
||||
user_email = None
|
||||
|
||||
# 1. Determinar usuario actuante
|
||||
if user:
|
||||
user_id = user.id
|
||||
user_email = user.email
|
||||
elif has_request_context():
|
||||
if hasattr(g, 'jwt_user') and g.jwt_user:
|
||||
user_id = g.jwt_user.id
|
||||
user_email = g.jwt_user.email
|
||||
elif current_user and current_user.is_authenticated:
|
||||
user_id = current_user.id
|
||||
user_email = current_user.email
|
||||
|
||||
# 2. Determinar IP de origen
|
||||
if not ip_address and has_request_context():
|
||||
if request.headers.get('X-Forwarded-For'):
|
||||
ip_address = request.headers.get('X-Forwarded-For').split(',')[0].strip()
|
||||
else:
|
||||
ip_address = request.remote_addr
|
||||
|
||||
# 3. Serializar detalles si es estructura de datos
|
||||
details_str = None
|
||||
if details is not None:
|
||||
if isinstance(details, (dict, list)):
|
||||
try:
|
||||
details_str = json.dumps(details, ensure_ascii=False)
|
||||
except Exception:
|
||||
details_str = str(details)
|
||||
else:
|
||||
details_str = str(details)
|
||||
|
||||
log_entry = AuditLog(
|
||||
user_id=user_id,
|
||||
user_email=user_email,
|
||||
action=action.upper(),
|
||||
module=module.lower(),
|
||||
entity_id=entity_id,
|
||||
details=details_str,
|
||||
ip_address=ip_address
|
||||
)
|
||||
try:
|
||||
db.session.add(log_entry)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
db.session.rollback()
|
||||
|
||||
return log_entry
|
||||
|
||||
@staticmethod
|
||||
def get_recent_logs(limit: int = 50, module: Optional[str] = None) -> List[AuditLog]:
|
||||
"""Obtiene las entradas más recientes de auditoría."""
|
||||
query = AuditLog.query
|
||||
if module:
|
||||
query = query.filter_by(module=module.lower())
|
||||
return query.order_by(AuditLog.created_at.desc()).limit(limit).all()
|
||||
Reference in New Issue
Block a user