feat: PKI API Go avec persistance MongoDB et abstraction storage
- API REST complète pour gestion Infrastructure à Clé Publique - Authentification JWT sur tous les endpoints (sauf login) - Hiérarchie de certificats (Root CA, Sub-CA, End Certificates) - Abstraction de stockage avec MemoryStore et MongoStore - Configuration centralisée via variables d'environnement - Support déploiement Docker Compose avec MongoDB - Tests unitaires pour sérialisation des clés RSA - Documentation complète avec exemples API
This commit is contained in:
77
internal/auth/jwt.go
Normal file
77
internal/auth/jwt.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidToken = errors.New("token invalide")
|
||||
ErrExpiredToken = errors.New("token expiré")
|
||||
)
|
||||
|
||||
// Claims représente les données du token JWT
|
||||
type Claims struct {
|
||||
UserID string `json:"user_id"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// JWTManager gère la génération et validation des tokens
|
||||
type JWTManager struct {
|
||||
secretKey string
|
||||
}
|
||||
|
||||
// NewJWTManager crée un nouveau gestionnaire JWT
|
||||
func NewJWTManager(secretKey string) *JWTManager {
|
||||
return &JWTManager{
|
||||
secretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateToken génère un token JWT
|
||||
func (m *JWTManager) GenerateToken(userID, role string, expiresIn time.Duration) (string, error) {
|
||||
claims := Claims{
|
||||
UserID: userID,
|
||||
Role: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiresIn)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "pkiapi",
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(m.secretKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// ValidateToken valide et parse un token JWT
|
||||
func (m *JWTManager) ValidateToken(tokenString string) (*Claims, error) {
|
||||
claims := &Claims{}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(m.secretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
// Vérifier l'expiration
|
||||
if claims.ExpiresAt != nil && claims.ExpiresAt.Before(time.Now()) {
|
||||
return nil, ErrExpiredToken
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
73
internal/auth/middleware.go
Normal file
73
internal/auth/middleware.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AuthMiddleware vérifie le token JWT dans les headers
|
||||
func AuthMiddleware(jwtManager *JWTManager) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Extraire le token du header Authorization
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "token manquant"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Format: "Bearer <token>"
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "format token invalide"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := parts[1]
|
||||
|
||||
// Valider le token
|
||||
claims, err := jwtManager.ValidateToken(tokenString)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Stocker les claims dans le contexte
|
||||
c.Set("user_id", claims.UserID)
|
||||
c.Set("role", claims.Role)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// OptionalAuthMiddleware vérifie le token s'il est présent, mais ne bloque pas sans
|
||||
func OptionalAuthMiddleware(jwtManager *JWTManager) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := parts[1]
|
||||
claims, err := jwtManager.ValidateToken(tokenString)
|
||||
if err != nil {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("user_id", claims.UserID)
|
||||
c.Set("role", claims.Role)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user