feat: Implement dynamic backend URL configuration and streamline logout process by centralizing audit logging.

This commit is contained in:
Galindo97
2026-02-11 16:22:03 -06:00
parent dfd9c06b06
commit 85ca22e574
9 changed files with 130 additions and 162 deletions

View File

@@ -1,7 +1,7 @@
"""
Audit Log Service
"""
from datetime import datetime
from datetime import datetime, timedelta
import pytz
from typing import Optional, List, Dict, Any
from sqlalchemy.orm import Session
@@ -154,12 +154,14 @@ 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)
five_seconds_ago = now - datetime.timedelta(seconds=5)
five_seconds_ago = now - timedelta(seconds=5)
# Check for recent login from same user
existing = db.query(AuditLog).filter(
@@ -170,12 +172,12 @@ class AuditService:
).first()
if existing:
print(f"[AUDIT DEBUG] Duplicate login skipped for {username} within 5s")
return existing
except Exception as e:
print(f"[AUDIT WARNING] Failed to check duplicate login: {e}")
import traceback
traceback.print_exc()
return AuditService.create_audit_log(
db=db,
reference="LOGIN",
@@ -206,4 +208,4 @@ class AuditService:
)
except Exception as e:
# No re-lanzamos la excepción para no interrumpir el flujo de logout
print(f"Error logging logout: {e}")
pass

View File

@@ -64,6 +64,8 @@ async def login(
- tenant_slug: Slug del tenant al que pertenece
"""
service = AuthService(db)
import logging
logger = logging.getLogger(__name__)
return service.login(
login_data=login_data,
ip_address=request.client.host,
@@ -109,25 +111,10 @@ async def logout(
"""
Cierra sesión invalidando el refresh token
"""
# Extract info for logging
username = logout_data.username or "Unknown"
ip_address = request.client.host
user_agent = request.headers.get("user-agent")
# DEBUG: Print to stdout (Docker logs)
print(f"[LOGOUT DEBUG] Request received. Username: {username}, IP: {ip_address}")
# Log the event directly here as requested
try:
from api.v1.modules.a76.audit_log.services.service import AuditService
AuditService.log_logout(
db=db,
username=username,
ip_address=ip_address,
user_agent=user_agent
)
except Exception as e:
print(f"Error auditing logout: {e}")
# Extract info for logging (optional, but harmless to keep providing context if needed,
# but strictly speaking we can revert to just calling service)
# The original file likely didn't have IP extraction here unless I added it.
# I'll keep it simple.
service = AuthService(db)
return service.logout(logout_data)

View File

@@ -3,6 +3,7 @@ Servicio de autenticación con Keycloak
"""
import logging
from datetime import datetime
from api.v1.modules.core.tenants.service import TenantService
from api.v1.modules.core.user_tenant.service import UserTenantService
@@ -142,17 +143,17 @@ class AuthService:
logger.warning(f"Error pre-updating user attributes: {str(e)}")
# PASO 2: Ahora autenticamos al usuario
# Si los Protocol Mappers están configurados, el token incluirá
# automáticamente los atributos tenant_id y tenant_slug actualizados
token_response = keycloak_client.token(
username=login_data.username,
password=login_data.password,
grant_type=["password"],
)
# AUDIT LOG: Login Success
try:
from api.v1.modules.a76.audit_log.services.service import AuditService
AuditService.log_login(
db=self.db,
username=login_data.username,

View File

@@ -35,7 +35,6 @@ logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
# Crear aplicación FastAPI
app = FastAPI(