mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 11:02:54 +00:00
- Add AuthMiddleware with Authorization header parsing, Bearer token extraction, JWT validation, user lookup, and context storage - Add RequirePermission middleware with user authentication check, database context retrieval, and permission validation - Add RequireCasbinPermission middleware with user_id context check, Casbin permission validation, and error handling - Add RequireRole middleware
181 lines
4.5 KiB
Go
181 lines
4.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/nutcas3/telecom-platform/apps/api-server/internal/models"
|
|
"github.com/nutcas3/telecom-platform/apps/api-server/internal/rbac"
|
|
"github.com/nutcas3/telecom-platform/apps/api-server/internal/services"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// AuthMiddleware creates a JWT authentication middleware
|
|
func AuthMiddleware(authService *services.AuthService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Get token from Authorization header
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Extract token from "Bearer <token>" format
|
|
tokenParts := strings.Split(authHeader, " ")
|
|
if len(tokenParts) != 2 || tokenParts[0] != "Bearer" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
token := tokenParts[1]
|
|
|
|
// Validate token
|
|
claims, err := authService.GetJWTService().ValidateToken(token)
|
|
if err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Get user from database
|
|
user, err := authService.GetUserByID(claims.UserID)
|
|
if err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Store user and claims in context
|
|
c.Set("user", user)
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RequirePermission creates a middleware that checks for specific permissions
|
|
func RequirePermission(resource, action string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
user, exists := c.Get("user")
|
|
if !exists {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Get database from context (should be set by the main server)
|
|
db, exists := c.Get("db")
|
|
if !exists {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database not available"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Check permission
|
|
if !user.(*models.User).HasPermission(db.(*gorm.DB), resource, action) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RequireCasbinPermission creates a middleware that checks for specific permissions using Casbin
|
|
func RequireCasbinPermission(casbinService *rbac.CasbinService, resource, action string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
allowed, err := casbinService.CheckPermission(c.Request.Context(), userID.(uint), resource, action)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check permissions"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if !allowed {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RequireRole creates a middleware that checks for specific roles (legacy, use RequireCasbinPermission for new features)
|
|
func RequireRole(allowedRoles ...string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
role, exists := c.Get("role")
|
|
if !exists {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
userRole := role.(string)
|
|
if slices.Contains(allowedRoles, userRole) {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient role permissions"})
|
|
c.Abort()
|
|
}
|
|
}
|
|
|
|
// OptionalAuthMiddleware creates an optional authentication middleware
|
|
func OptionalAuthMiddleware(authService *services.AuthService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Get token from Authorization header
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// Extract token from "Bearer <token>" format
|
|
tokenParts := strings.Split(authHeader, " ")
|
|
if len(tokenParts) != 2 || tokenParts[0] != "Bearer" {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
token := tokenParts[1]
|
|
|
|
// Validate token
|
|
claims, err := authService.GetJWTService().ValidateToken(token)
|
|
if err != nil {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// Get user from database
|
|
user, err := authService.GetUserByID(claims.UserID)
|
|
if err != nil {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// Store user and claims in context
|
|
c.Set("user", user)
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
|
|
c.Next()
|
|
}
|
|
}
|