chore: refacto

This commit is contained in:
2026-04-20 19:41:52 +02:00
parent 5fc52c72ee
commit 2e9827af98
46 changed files with 497 additions and 648 deletions
@@ -47,11 +47,6 @@ type AdminClaims struct {
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"},
}
)
// ============================================
@@ -68,7 +63,7 @@ func validateClientToken(tokenString string, database *db.Database) (*ClientClai
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) {
token, err := jwt.ParseWithClaims(tokenString, &ClientClaims{}, func(token *jwt.Token) (any, error) {
// Vérifier explicitement l'algorithme
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
return nil, fmt.Errorf("unexpected signing algorithm: %v", token.Method.Alg())
@@ -116,8 +111,7 @@ func validateAdminToken(tokenString string, database *db.Database) (*AdminClaims
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) {
token, err := jwt.ParseWithClaims(tokenString, &AdminClaims{}, func(token *jwt.Token) (any, error) {
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
return nil, fmt.Errorf("unexpected signing algorithm: %v", token.Method.Alg())
}
@@ -155,11 +149,6 @@ func validateAdminToken(tokenString string, database *db.Database) (*AdminClaims
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 ") {
@@ -180,7 +169,6 @@ func ClientMiddleware(c *gin.Context) {
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)
@@ -195,7 +183,6 @@ func ClientMiddleware(c *gin.Context) {
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)
@@ -206,12 +193,6 @@ func ClientMiddleware(c *gin.Context) {
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 ") {
@@ -232,7 +213,6 @@ func AdminMiddleware(c *gin.Context) {
return
}
// ✅ Check révocation
valid, err := database.IsTokenValid(tokenStr)
if err != nil {
log.Printf("❌ [ADMIN-MWARE] Erreur vérification token DB: %v", err)
@@ -247,23 +227,14 @@ func AdminMiddleware(c *gin.Context) {
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)
// Vérifier rôle — admin uniquement
if claims.Role != "admin" {
log.Printf("❌ [ADMIN-MWARE] Role invalide: %s (admin requis)", 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)
@@ -275,12 +246,6 @@ func AdminMiddleware(c *gin.Context) {
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 ") {
@@ -316,22 +281,10 @@ func CabineMiddleware(c *gin.Context) {
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 {
// Vérifier rôle — admin ou cabine uniquement
if claims.Role != "admin" && claims.Role != "cabine" {
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.JSON(http.StatusForbidden, gin.H{"error": "Droits insuffisants - Accès cabine requis"})
c.Abort()
return
}
@@ -347,12 +300,6 @@ func CabineMiddleware(c *gin.Context) {
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 ") {
@@ -388,22 +335,10 @@ func LivreurMiddleware(c *gin.Context) {
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 {
// Vérifier rôle — admin ou livreur uniquement
if claims.Role != "admin" && claims.Role != "livreur" {
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.JSON(http.StatusForbidden, gin.H{"error": "Droits insuffisants - Accès livreur requis"})
c.Abort()
return
}
@@ -419,11 +354,6 @@ func LivreurMiddleware(c *gin.Context) {
c.Next()
}
// ============================================
// SESSION MIDDLEWARE CLIENT (Existant)
// ============================================
// ClientSessionMiddleware valide la session Redis du client
func ClientSessionMiddleware(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -507,17 +437,10 @@ func ClientSessionMiddleware(c *gin.Context) {
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
}
@@ -533,12 +456,10 @@ func RateLimitMiddleware(c *gin.Context) {
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{
@@ -548,7 +469,6 @@ func RateLimitMiddleware(c *gin.Context) {
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)
@@ -556,6 +476,40 @@ func RateLimitMiddleware(c *gin.Context) {
c.Next()
}
// LoginRateLimitMiddleware limite les tentatives de connexion par IP.
// Config: 10 tentatives par 15 minutes.
func LoginRateLimitMiddleware(c *gin.Context) {
ip := c.GetHeader("X-Real-IP")
if ip == "" {
ip = c.ClientIP()
}
rateLimitKey := "ratelimit:login:" + ip
count, err := db.Redis.Incr(db.RedisCtx, rateLimitKey).Result()
if err != nil {
log.Printf("⚠️ [LOGIN-RATELIMIT] Erreur Redis: %v", err)
c.Next()
return
}
if count == 1 {
db.Redis.Expire(db.RedisCtx, rateLimitKey, 15*time.Minute)
}
if count > 10 {
log.Printf("❌ [LOGIN-RATELIMIT] IP %s bloquée: %d tentatives/15min", ip, count)
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "Trop de tentatives de connexion - Réessayez dans 15 minutes",
})
c.Abort()
return
}
c.Header("X-RateLimit-Remaining", strconv.FormatInt(10-count, 10))
c.Next()
}
// ============================================
// HELPER MIDDLEWARE
// ============================================
@@ -640,11 +594,6 @@ func LoadClientContext(c *gin.Context, database *db.Database) (*db.SessionData,
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)