208 lines
7.1 KiB
Python
208 lines
7.1 KiB
Python
"""
|
|
Middleware pour la gestion de la déconnexion automatique des utilisateurs.
|
|
|
|
Ce middleware vérifie si l'utilisateur connecté a une configuration de
|
|
déconnexion automatique (auto_logout_hours) et le déconnecte si le temps
|
|
depuis sa dernière connexion dépasse cette durée.
|
|
|
|
Typiquement utilisé pour les utilisateurs qui partagent un ordinateur
|
|
en équipes de travail (shifts).
|
|
"""
|
|
|
|
from django.contrib.auth import logout
|
|
from django.shortcuts import redirect
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
from django.utils.translation import gettext_lazy as _
|
|
import traceback
|
|
import sys
|
|
|
|
|
|
# Clé de session pour stocker l'heure de connexion
|
|
SESSION_LOGIN_TIME_KEY = 'login_timestamp'
|
|
|
|
|
|
class RequestLoggingMiddleware:
|
|
"""
|
|
Middleware qui capture les erreurs HTTP (4xx, 5xx) et les exceptions
|
|
non gérées pour les enregistrer dans la base de données.
|
|
|
|
Cela permet de visualiser les erreurs dans l'admin Django.
|
|
"""
|
|
|
|
# URLs à ignorer pour éviter de logger des requêtes non pertinentes
|
|
IGNORED_PATHS = [
|
|
'/static/',
|
|
'/media/',
|
|
'/favicon.ico',
|
|
'/__debug__/',
|
|
]
|
|
|
|
# Codes de statut à logger
|
|
LOGGED_STATUS_CODES = [400, 401, 403, 404, 405, 500, 502, 503, 504]
|
|
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
# Vérifier si cette URL doit être ignorée
|
|
if self._should_ignore(request):
|
|
return self.get_response(request)
|
|
|
|
response = None
|
|
try:
|
|
response = self.get_response(request)
|
|
|
|
# Logger les erreurs HTTP (4xx, 5xx)
|
|
if response.status_code in self.LOGGED_STATUS_CODES:
|
|
self._log_request(
|
|
request,
|
|
status_code=response.status_code,
|
|
level=self._get_log_level(response.status_code)
|
|
)
|
|
|
|
return response
|
|
|
|
except Exception as exc:
|
|
# Logger les exceptions non gérées
|
|
self._log_request(
|
|
request,
|
|
exception=exc,
|
|
level='CRITICAL',
|
|
status_code=500,
|
|
traceback_str=traceback.format_exc()
|
|
)
|
|
raise # Re-raise l'exception pour que Django la gère
|
|
|
|
def process_exception(self, request, exception):
|
|
"""
|
|
Appelé quand une vue lève une exception.
|
|
"""
|
|
if self._should_ignore(request):
|
|
return None
|
|
|
|
self._log_request(
|
|
request,
|
|
exception=exception,
|
|
level='ERROR',
|
|
status_code=500,
|
|
traceback_str=traceback.format_exc()
|
|
)
|
|
return None # Laisser Django gérer l'exception
|
|
|
|
def _should_ignore(self, request):
|
|
"""Vérifie si la requête doit être ignorée."""
|
|
for path in self.IGNORED_PATHS:
|
|
if request.path.startswith(path):
|
|
return True
|
|
return False
|
|
|
|
def _get_log_level(self, status_code):
|
|
"""Retourne le niveau de log approprié selon le code de statut."""
|
|
if status_code >= 500:
|
|
return 'ERROR'
|
|
elif status_code == 404:
|
|
return 'WARNING'
|
|
elif status_code >= 400:
|
|
return 'WARNING'
|
|
return 'INFO'
|
|
|
|
def _log_request(self, request, exception=None, level='ERROR', status_code=None, traceback_str=None):
|
|
"""Enregistre le log dans la base de données."""
|
|
try:
|
|
from common.models import RequestLog
|
|
RequestLog.log_request(
|
|
request,
|
|
exception=exception,
|
|
level=level,
|
|
status_code=status_code,
|
|
traceback_str=traceback_str
|
|
)
|
|
except Exception as e:
|
|
# En cas d'erreur lors de l'enregistrement du log, ne pas faire planter la requête
|
|
# On peut quand même logger dans le fichier standard
|
|
import logging
|
|
logger = logging.getLogger('django')
|
|
logger.error(f"Erreur lors de l'enregistrement du log: {e}")
|
|
|
|
|
|
class AutoLogoutMiddleware:
|
|
"""
|
|
Middleware qui déconnecte automatiquement les utilisateurs après un
|
|
certain nombre d'heures configuré dans leur UserConfig.
|
|
|
|
Si l'utilisateur a auto_logout_hours défini dans son UserConfig,
|
|
il sera automatiquement déconnecté après ce nombre d'heures depuis
|
|
sa dernière connexion.
|
|
"""
|
|
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
# Ne vérifier que pour les utilisateurs authentifiés
|
|
if request.user.is_authenticated:
|
|
# Enregistrer l'heure de connexion si pas encore fait
|
|
if SESSION_LOGIN_TIME_KEY not in request.session:
|
|
request.session[SESSION_LOGIN_TIME_KEY] = timezone.now().isoformat()
|
|
|
|
# Vérifier si l'utilisateur doit être déconnecté
|
|
if self._should_logout(request):
|
|
# Stocker un message avant la déconnexion
|
|
messages.info(
|
|
request,
|
|
_("Votre session a expiré après le délai configuré. Veuillez vous reconnecter.")
|
|
)
|
|
logout(request)
|
|
return redirect('login')
|
|
|
|
response = self.get_response(request)
|
|
return response
|
|
|
|
def _should_logout(self, request):
|
|
"""
|
|
Vérifie si l'utilisateur doit être déconnecté en fonction de
|
|
son auto_logout_hours configuré.
|
|
"""
|
|
try:
|
|
# Récupérer la configuration utilisateur
|
|
user_config = getattr(request.user, 'config', None)
|
|
if not user_config:
|
|
return False
|
|
|
|
# Vérifier si auto_logout_hours est défini
|
|
auto_logout_hours = user_config.auto_logout_hours
|
|
if not auto_logout_hours:
|
|
return False
|
|
|
|
# Récupérer l'heure de connexion depuis la session
|
|
login_time_str = request.session.get(SESSION_LOGIN_TIME_KEY)
|
|
if not login_time_str:
|
|
return False
|
|
|
|
# Parser l'heure de connexion
|
|
from datetime import datetime
|
|
login_time = datetime.fromisoformat(login_time_str)
|
|
if timezone.is_naive(login_time):
|
|
login_time = timezone.make_aware(login_time)
|
|
|
|
# Calculer la durée depuis la connexion
|
|
now = timezone.now()
|
|
hours_since_login = (now - login_time).total_seconds() / 3600
|
|
|
|
# Déconnecter si le temps dépasse la limite
|
|
return hours_since_login >= auto_logout_hours
|
|
|
|
except Exception:
|
|
# En cas d'erreur, ne pas déconnecter
|
|
return False
|
|
|
|
|
|
def set_login_time(sender, user, request, **kwargs):
|
|
"""
|
|
Signal handler pour enregistrer l'heure de connexion quand un
|
|
utilisateur se connecte.
|
|
|
|
À connecter au signal user_logged_in de Django.
|
|
"""
|
|
request.session[SESSION_LOGIN_TIME_KEY] = timezone.now().isoformat()
|