654 lines
18 KiB
Go
654 lines
18 KiB
Go
// ============================================
|
|
// middleware/middleware_CORRIGES.go
|
|
// ============================================
|
|
// Réorganisation complète des middlewares
|
|
// Déplace ClientMiddleware, AdminMiddleware, etc depuis handlers/auth.go
|
|
|
|
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"gestion/db"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// ============================================
|
|
// TYPES JWT CLAIMS
|
|
// ============================================
|
|
|
|
type ClientClaims struct {
|
|
ClientID int `json:"client_id"`
|
|
Username string `json:"username"`
|
|
Role string `json:"role"`
|
|
SessionID string `json:"session_id"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
type AdminClaims struct {
|
|
UserID int `json:"user_id"`
|
|
Username string `json:"username"`
|
|
Role string `json:"role"`
|
|
SessionID string `json:"session_id"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
// ============================================
|
|
// VARIABLES GLOBALES
|
|
// ============================================
|
|
|
|
var (
|
|
userJWTSecret = []byte(os.Getenv("USER_JWT_SECRET")) // ✅ Pour clients
|
|
adminJWTSecret = []byte(os.Getenv("ADMIN_JWT_SECRET")) // ✅ Pour admin/cabine/livreur
|
|
roleHierarchy = map[string][]string{
|
|
"admin": {"admin", "cabine", "livreur", "client"},
|
|
"cabine": {"cabine", "livreur", "client"},
|
|
"livreur": {"livreur", "client"},
|
|
}
|
|
)
|
|
|
|
// ============================================
|
|
// VALIDATION TOKENS
|
|
// ============================================
|
|
|
|
// validateClientToken valide un token client
|
|
func validateClientToken(tokenString string, database *db.Database) (*ClientClaims, error) {
|
|
tokenString = strings.TrimSpace(tokenString)
|
|
if tokenString == "" {
|
|
return nil, fmt.Errorf("token vide")
|
|
}
|
|
|
|
log.Printf("🔍 [VALIDATE-CLIENT] Validating client token...")
|
|
|
|
// Parser JWT EN PREMIER avec userJWTSecret (CLIENT)
|
|
token, err := jwt.ParseWithClaims(tokenString, &ClientClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
// Vérifier explicitement l'algorithme
|
|
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
|
|
return nil, fmt.Errorf("unexpected signing algorithm: %v", token.Method.Alg())
|
|
}
|
|
return userJWTSecret, nil
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("❌ [VALIDATE-CLIENT] JWT parse error: %v", err)
|
|
return nil, fmt.Errorf("jwt parsing failed: %v", err)
|
|
}
|
|
|
|
if !token.Valid {
|
|
log.Printf("❌ [VALIDATE-CLIENT] Token not valid")
|
|
return nil, fmt.Errorf("token not valid")
|
|
}
|
|
|
|
claims, ok := token.Claims.(*ClientClaims)
|
|
if !ok {
|
|
log.Printf("❌ [VALIDATE-CLIENT] Claims type error")
|
|
return nil, fmt.Errorf("invalid claims type")
|
|
}
|
|
if claims.Role == "" {
|
|
return nil, fmt.Errorf("role manquant dans le token")
|
|
}
|
|
if claims.Issuer != "api-client" {
|
|
return nil, fmt.Errorf("issuer invalide")
|
|
}
|
|
|
|
if claims.ExpiresAt.Unix() < time.Now().Unix() {
|
|
return nil, fmt.Errorf("token expiré")
|
|
}
|
|
|
|
log.Printf("✅ [VALIDATE-CLIENT] JWT valid - Username: %s, ClientID: %d", claims.Username, claims.ClientID)
|
|
|
|
return claims, nil
|
|
}
|
|
|
|
// validateAdminToken valide un token admin
|
|
func validateAdminToken(tokenString string, database *db.Database) (*AdminClaims, error) {
|
|
tokenString = strings.TrimSpace(tokenString)
|
|
if tokenString == "" {
|
|
return nil, fmt.Errorf("token vide")
|
|
}
|
|
|
|
log.Printf("🔍 [VALIDATE-ADMIN] Validating admin token...")
|
|
|
|
// Parser JWT EN PREMIER avec adminJWTSecret (ADMIN/CABINE/LIVREUR)
|
|
token, err := jwt.ParseWithClaims(tokenString, &AdminClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
|
|
return nil, fmt.Errorf("unexpected signing algorithm: %v", token.Method.Alg())
|
|
}
|
|
return adminJWTSecret, nil
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("❌ [VALIDATE-ADMIN] JWT parse error: %v", err)
|
|
return nil, fmt.Errorf("jwt parsing failed: %v", err)
|
|
}
|
|
|
|
if !token.Valid {
|
|
log.Printf("❌ [VALIDATE-ADMIN] Token not valid")
|
|
return nil, fmt.Errorf("token not valid")
|
|
}
|
|
|
|
claims, ok := token.Claims.(*AdminClaims)
|
|
if !ok {
|
|
log.Printf("❌ [VALIDATE-ADMIN] Claims type error")
|
|
return nil, fmt.Errorf("invalid claims type")
|
|
}
|
|
if claims.Role == "" {
|
|
return nil, fmt.Errorf("role manquant dans le token")
|
|
}
|
|
if claims.Issuer != "api-admin" {
|
|
return nil, fmt.Errorf("issuer invalide")
|
|
}
|
|
|
|
if claims.ExpiresAt.Unix() < time.Now().Unix() {
|
|
return nil, fmt.Errorf("token expiré")
|
|
}
|
|
log.Printf("✅ [VALIDATE-ADMIN] JWT valid - Username: %s, Role: %s, UserID: %d Issuer: %s ExpiresAt: %d",
|
|
claims.Username, claims.Role, claims.UserID, claims.Issuer, claims.ExpiresAt.Unix())
|
|
|
|
return claims, nil
|
|
}
|
|
|
|
// ============================================
|
|
// MIDDLEWARE AUTHENTIFICATION CLIENT
|
|
// ============================================
|
|
|
|
// ClientMiddleware valide le JWT d'un client
|
|
func ClientMiddleware(c *gin.Context) {
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
|
|
log.Printf("❌ [CLIENT-MWARE] Authorization header manquant")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token d'autorisation requis"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
claims, err := validateClientToken(tokenStr, database)
|
|
if err != nil {
|
|
log.Printf("❌ [CLIENT-MWARE] Token invalide: %v", err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// ✅ NOUVEAU : Vérifier que le token n'a pas été révoqué
|
|
valid, err := database.IsTokenValid(tokenStr)
|
|
if err != nil {
|
|
log.Printf("❌ [CLIENT-MWARE] Erreur vérification token DB: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur serveur"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !valid {
|
|
log.Printf("❌ [CLIENT-MWARE] Token révoqué ou expiré")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token révoqué ou expiré"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Stocker les infos du client dans le contexte
|
|
c.Set("client_id", claims.ClientID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
c.Set("session_id", claims.SessionID)
|
|
|
|
log.Printf("✅ [CLIENT-MWARE] Client %s (ID=%d) authentifié", claims.Username, claims.ClientID)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// MIDDLEWARE AUTHENTIFICATION ADMIN
|
|
// ============================================
|
|
|
|
// AdminMiddleware valide le JWT d'un admin (role == "admin" SEULEMENT)
|
|
// ✅ Remplace le AdminMiddleware de handlers/auth.go
|
|
func AdminMiddleware(c *gin.Context) {
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
|
|
log.Printf("❌ [ADMIN-MWARE] Authorization header manquant")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token d'autorisation requis"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
claims, err := validateAdminToken(tokenStr, database)
|
|
if err != nil {
|
|
log.Printf("❌ [ADMIN-MWARE] Token invalide: %v", err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token admin invalide"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// ✅ Check révocation
|
|
valid, err := database.IsTokenValid(tokenStr)
|
|
if err != nil {
|
|
log.Printf("❌ [ADMIN-MWARE] Erreur vérification token DB: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur serveur"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !valid {
|
|
log.Printf("❌ [ADMIN-MWARE] Token révoqué ou expiré")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token révoqué ou expiré"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier rôle
|
|
validRoles := []string{"admin", "cabine", "livreur"}
|
|
isValid := false
|
|
for _, role := range validRoles {
|
|
if claims.Role == role {
|
|
isValid = true
|
|
break
|
|
}
|
|
}
|
|
if !isValid {
|
|
log.Printf("❌ [ADMIN-MWARE] Role invalide: %s", claims.Role)
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Droits insuffisants - Accès admin requis"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Stocker les infos de l'admin dans le contexte
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
c.Set("session_id", claims.SessionID)
|
|
|
|
log.Printf("✅ [ADMIN-MWARE] Admin %s (Role=%s, ID=%d) authentifié",
|
|
claims.Username, claims.Role, claims.UserID)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// MIDDLEWARE AUTHENTIFICATION CABINE
|
|
// ============================================
|
|
|
|
// CabineMiddleware valide que l'utilisateur a accès à la cabine
|
|
// ✅ Remplace le CabineMiddleware de handlers/auth.go
|
|
func CabineMiddleware(c *gin.Context) {
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
|
|
log.Printf("❌ [CABINE-MWARE] Authorization header manquant")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token d'autorisation requis"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
claims, err := validateAdminToken(tokenStr, database)
|
|
if err != nil {
|
|
log.Printf("❌ [CABINE-MWARE] Token invalide: %v", err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// ✅ Check révocation
|
|
valid, err := database.IsTokenValid(tokenStr)
|
|
if err != nil {
|
|
log.Printf("❌ [CABINE-MWARE] Erreur vérification token DB: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur serveur"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !valid {
|
|
log.Printf("❌ [CABINE-MWARE] Token révoqué ou expiré")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token révoqué ou expiré"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier hiérarchie des rôles
|
|
allowedRoles := roleHierarchy[claims.Role]
|
|
authorized := false
|
|
for _, r := range allowedRoles {
|
|
if r == "cabine" {
|
|
authorized = true
|
|
break
|
|
}
|
|
}
|
|
if !authorized {
|
|
log.Printf("❌ [CABINE-MWARE] Role non autorisé: %s", claims.Role)
|
|
c.JSON(http.StatusForbidden, gin.H{
|
|
"error": "Droits insuffisants - Accès cabine requis",
|
|
"your_role": claims.Role,
|
|
"allowed_roles": "admin, cabine",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Stocker les infos
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
c.Set("session_id", claims.SessionID)
|
|
|
|
log.Printf("✅ [CABINE-MWARE] Cabine %s (%s) authentifiée", claims.Username, claims.Role)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// MIDDLEWARE AUTHENTIFICATION LIVREUR
|
|
// ============================================
|
|
|
|
// LivreurMiddleware valide que l'utilisateur est livreur
|
|
// ✅ Remplace le LivreurMiddleware de handlers/auth.go
|
|
func LivreurMiddleware(c *gin.Context) {
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
|
|
log.Printf("❌ [LIVREUR-MWARE] Authorization header manquant")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token d'autorisation requis"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
claims, err := validateAdminToken(tokenStr, database)
|
|
if err != nil {
|
|
log.Printf("❌ [LIVREUR-MWARE] Token invalide: %v", err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// ✅ Check révocation
|
|
valid, err := database.IsTokenValid(tokenStr)
|
|
if err != nil {
|
|
log.Printf("❌ [LIVREUR-MWARE] Erreur vérification token DB: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur serveur"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !valid {
|
|
log.Printf("❌ [LIVREUR-MWARE] Token révoqué ou expiré")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token révoqué ou expiré"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier hiérarchie des rôles
|
|
allowedRoles := roleHierarchy[claims.Role]
|
|
authorized := false
|
|
for _, r := range allowedRoles {
|
|
if r == "livreur" {
|
|
authorized = true
|
|
break
|
|
}
|
|
}
|
|
if !authorized {
|
|
log.Printf("❌ [LIVREUR-MWARE] Role non autorisé: %s", claims.Role)
|
|
c.JSON(http.StatusForbidden, gin.H{
|
|
"error": "Droits insuffisants - Accès livreur requis",
|
|
"your_role": claims.Role,
|
|
"allowed_roles": "admin, livreur",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Stocker les infos
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
c.Set("session_id", claims.SessionID)
|
|
|
|
log.Printf("✅ [LIVREUR-MWARE] Livreur %s (%s) authentifié", claims.Username, claims.Role)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// SESSION MIDDLEWARE CLIENT (Existant)
|
|
// ============================================
|
|
|
|
// ClientSessionMiddleware valide la session Redis du client
|
|
func ClientSessionMiddleware(c *gin.Context) {
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
// Récupérer le username du JWT (DÉJÀ VALIDÉ)
|
|
username, exists := c.Get("username")
|
|
if !exists {
|
|
log.Printf("❌ [SESSION-MWARE] Username manquant du JWT")
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "JWT invalide - username manquant",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
usernameStr := username.(string)
|
|
|
|
// Récupérer le client_id du JWT
|
|
clientID, ok := c.Get("client_id")
|
|
if !ok {
|
|
log.Printf("❌ [SESSION-MWARE] client_id manquant du JWT")
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "JWT invalide - client_id manquant",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
clientIDInt, ok := clientID.(int)
|
|
if !ok {
|
|
log.Printf("❌ [SESSION-MWARE] client_id malformé: %v", clientID)
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "JWT invalide - client_id malformé",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier la session Redis
|
|
session, err := database.GetClientSession(clientIDInt)
|
|
if err != nil {
|
|
log.Printf("❌ [SESSION-MWARE] Pas de session Redis pour client %d: %v", clientIDInt, err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Session expirée - Veuillez vous reconnecter",
|
|
"action": "Please login again",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier que les données correspondent
|
|
if session.Username != usernameStr {
|
|
log.Printf("❌ [SESSION-MWARE] MISMATCH! JWT=%s, session=%s", usernameStr, session.Username)
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Session invalide - Mismatch détecté",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if session.ClientID != clientIDInt {
|
|
log.Printf("❌ [SESSION-MWARE] MISMATCH! JWT=%d, session=%d", clientIDInt, session.ClientID)
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Session invalide - client_id mismatch",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Prolonger la session
|
|
if err := database.RefreshSessionTimeout(clientIDInt); err != nil {
|
|
log.Printf("⚠️ [SESSION-MWARE] Erreur refresh: %v", err)
|
|
}
|
|
|
|
// Charger les infos dans le contexte
|
|
c.Set("session_id", session.SessionID)
|
|
c.Set("session", session)
|
|
c.Set("last_activity", session.LastActivity)
|
|
|
|
log.Printf("✅ [SESSION-MWARE] Session valide pour %s (client_id=%d)", usernameStr, clientIDInt)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// RATE LIMITING MIDDLEWARE
|
|
// ============================================
|
|
|
|
// RateLimitMiddleware limite le nombre de requêtes par client
|
|
// Config: 100 requêtes par minute par client
|
|
func RateLimitMiddleware(c *gin.Context) {
|
|
// Récupérer le client_id
|
|
clientID, ok := c.Get("client_id")
|
|
if !ok {
|
|
// Pas de client_id (requête publique), pas de rate limit
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
clientIDInt := clientID.(int)
|
|
rateLimitKey := "ratelimit:" + strconv.Itoa(clientIDInt)
|
|
|
|
// Incrémenter le compteur
|
|
count, err := db.Redis.Incr(db.RedisCtx, rateLimitKey).Result()
|
|
if err != nil {
|
|
log.Printf("⚠️ [RATELIMIT] Erreur: %v", err)
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// Initialiser le TTL à la première requête
|
|
if count == 1 {
|
|
db.Redis.Expire(db.RedisCtx, rateLimitKey, 60*time.Second) // 1 minute
|
|
}
|
|
|
|
// Vérifier si dépassement (100 requêtes/min)
|
|
if count > 100 {
|
|
log.Printf("❌ [RATELIMIT] Client %d dépassé le limite: %d requêtes/min", clientIDInt, count)
|
|
c.JSON(http.StatusTooManyRequests, gin.H{
|
|
"error": "Trop de requêtes - Réessayez dans une minute",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Ajouter le header du remaining
|
|
c.Header("X-RateLimit-Remaining", strconv.FormatInt(100-count, 10))
|
|
|
|
log.Printf("📊 [RATELIMIT] Client %d: %d/%d requêtes", clientIDInt, count, 100)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// ============================================
|
|
// HELPER MIDDLEWARE
|
|
// ============================================
|
|
|
|
// VerifyAuthHeader vérifie que le header Authorization est valide
|
|
func VerifyAuthHeader(c *gin.Context) {
|
|
authHeader := c.GetHeader("Authorization")
|
|
|
|
if authHeader == "" {
|
|
log.Printf("❌ [AUTH-HEADER] Authorization header manquant")
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Authorization header manquant",
|
|
"hint": "Utilisez: Authorization: Bearer <token>",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Vérifier le format "Bearer <token>"
|
|
parts := strings.Split(authHeader, " ")
|
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
|
log.Printf("❌ [AUTH-HEADER] Format invalide: %s", authHeader)
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Format Authorization invalide",
|
|
"hint": "Utilisez: Authorization: Bearer <token>",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
log.Printf("✅ [AUTH-HEADER] Format valide")
|
|
c.Next()
|
|
}
|
|
|
|
// SessionErrorRecovery récupère les erreurs de session
|
|
func SessionErrorRecovery(c *gin.Context) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
log.Printf("❌ [SESSION-ERROR] Erreur système: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"error": "Erreur serveur - Session compromise",
|
|
})
|
|
}
|
|
}()
|
|
|
|
c.Next()
|
|
|
|
if len(c.Errors) > 0 {
|
|
log.Printf("⚠️ [SESSION] Erreur handler: %v", c.Errors)
|
|
}
|
|
}
|
|
|
|
// LogSessionMiddleware log toutes les infos de session
|
|
func LogSessionMiddleware(c *gin.Context) {
|
|
username, _ := c.Get("username")
|
|
clientID, _ := c.Get("client_id")
|
|
sessionID, _ := c.Get("session_id")
|
|
|
|
log.Printf("📊 [SESSION-LOG] %s %s | user=%v | client_id=%v | session=%v",
|
|
c.Request.Method, c.Request.URL.Path, username, clientID, sessionID)
|
|
|
|
c.Next()
|
|
|
|
log.Printf("📊 [SESSION-LOG] Response: %d", c.Writer.Status())
|
|
}
|
|
|
|
// LoadClientContext charge les infos du client en contexte
|
|
func LoadClientContext(c *gin.Context, database *db.Database) (*db.SessionData, error) {
|
|
clientID, ok := c.Get("client_id")
|
|
if !ok {
|
|
return nil, fmt.Errorf("client_id manquant du contexte")
|
|
}
|
|
|
|
clientIDInt := clientID.(int)
|
|
|
|
// Récupérer la session
|
|
session, err := database.GetClientSession(clientIDInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return session, nil
|
|
}
|
|
|
|
// ============================================
|
|
// DATABASE MIDDLEWARE
|
|
// ============================================
|
|
|
|
// DatabaseMiddleware injecte la base de données dans le contexte
|
|
func DatabaseMiddleware(db *db.Database) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Set("database", db)
|
|
c.Next()
|
|
}
|
|
}
|