feature/bitacora-correccion-filtro-tenant
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Audit Log Service
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta, date, time
|
||||
from decimal import Decimal
|
||||
import uuid
|
||||
@@ -9,8 +10,8 @@ from typing import Optional, List, Dict, Any
|
||||
from sqlalchemy.orm import Session
|
||||
from ..models import AuditLog
|
||||
from .core import AuditMapper, ReferenceGenerator
|
||||
from core.security import verify_token # keep if needed or simpler just remove if unused
|
||||
# We don't need security import here anymore as context is passed explicitly or handled by events
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _make_json_safe(obj: Any) -> Any:
|
||||
@@ -188,49 +189,62 @@ class AuditService:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def log_login(db: Session, username: str, ip_address: str = None, user_agent: str = None):
|
||||
|
||||
# Prevent duplicate login logs (debounce 5 seconds)
|
||||
# This handles cases where frontend might submit twice or redirects trigger re-auth
|
||||
try:
|
||||
# Timezone handling: Use UTC for consistency
|
||||
now = datetime.now(pytz.UTC)
|
||||
def log_login(
|
||||
db: Session,
|
||||
username: str,
|
||||
ip_address: str = None,
|
||||
user_agent: str = None,
|
||||
company_id: Optional[int] = None,
|
||||
tenant_id: Optional[int] = None,
|
||||
):
|
||||
"""
|
||||
``audit_logs`` exige ``tenant_id`` y ``company_id``. El login vía Hub no
|
||||
define compañía activa; sin ambos argumentos no se inserta fila (antes fallaba NOT NULL).
|
||||
"""
|
||||
if company_id is None or tenant_id is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
now = datetime.now(pytz.UTC)
|
||||
five_seconds_ago = now - timedelta(seconds=5)
|
||||
|
||||
# Check for recent login from same user
|
||||
existing = db.query(AuditLog).filter(
|
||||
AuditLog.username == username,
|
||||
AuditLog.operation_type == "LOGIN",
|
||||
# Compare against timestamp (timezone aware)
|
||||
AuditLog.timestamp >= five_seconds_ago
|
||||
AuditLog.company_id == company_id,
|
||||
AuditLog.tenant_id == tenant_id,
|
||||
AuditLog.timestamp >= five_seconds_ago,
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
return existing
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
logger.warning("Login audit debounce query failed: %s", e)
|
||||
|
||||
return AuditService.create_audit_log(
|
||||
db=db,
|
||||
reference="LOGIN",
|
||||
procedure="SYSTEM SCAF",
|
||||
movement="SYSTEM LOGIN",
|
||||
username=username,
|
||||
operation_type="LOGIN",
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent
|
||||
db=db,
|
||||
reference="LOGIN",
|
||||
procedure="SYSTEM SCAF",
|
||||
movement="SYSTEM LOGIN",
|
||||
username=username,
|
||||
operation_type="LOGIN",
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent,
|
||||
company_id=company_id,
|
||||
tenant_id=tenant_id,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def log_logout(db: Session, username: str, ip_address: str = None, user_agent: str = None):
|
||||
"""
|
||||
Registra un evento de cierre de sesión
|
||||
"""
|
||||
def log_logout(
|
||||
db: Session,
|
||||
username: str,
|
||||
ip_address: str = None,
|
||||
user_agent: str = None,
|
||||
company_id: Optional[int] = None,
|
||||
tenant_id: Optional[int] = None,
|
||||
):
|
||||
"""Misma condición que ``log_login``: sin alcance compañía/tenant no se escribe."""
|
||||
if company_id is None or tenant_id is None:
|
||||
return None
|
||||
try:
|
||||
# Reutilizamos create_audit_log para mantener consistencia
|
||||
AuditService.create_audit_log(
|
||||
db=db,
|
||||
reference="LOGOUT",
|
||||
@@ -239,8 +253,9 @@ class AuditService:
|
||||
username=username,
|
||||
operation_type="LOGOUT",
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent
|
||||
user_agent=user_agent,
|
||||
company_id=company_id,
|
||||
tenant_id=tenant_id,
|
||||
)
|
||||
except Exception as e:
|
||||
# No re-lanzamos la excepción para no interrumpir el flujo de logout
|
||||
pass
|
||||
logger.warning("Logout audit insert failed: %s", e)
|
||||
|
||||
Reference in New Issue
Block a user