162 lines
3.7 KiB
Go
162 lines
3.7 KiB
Go
package router
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/atlasos/calypso/internal/auth"
|
|
"github.com/atlasos/calypso/internal/common/database"
|
|
"github.com/atlasos/calypso/internal/iam"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// authMiddleware validates JWT tokens and sets user context
|
|
func authMiddleware(authHandler *auth.Handler) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var token string
|
|
|
|
// Try to extract token from Authorization header first
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader != "" {
|
|
// Parse Bearer token
|
|
parts := strings.SplitN(authHeader, " ", 2)
|
|
if len(parts) == 2 && parts[0] == "Bearer" {
|
|
token = parts[1]
|
|
}
|
|
}
|
|
|
|
// If no token from header, try query parameter (for WebSocket)
|
|
if token == "" {
|
|
token = c.Query("token")
|
|
}
|
|
|
|
// If still no token, return error
|
|
if token == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization token"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Validate token and get user
|
|
user, err := authHandler.ValidateToken(token)
|
|
if err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Load user roles and permissions from database
|
|
// We need to get the DB from the auth handler's context
|
|
// For now, we'll load them in the permission middleware instead
|
|
|
|
// Set user in context
|
|
c.Set("user", user)
|
|
c.Set("user_id", user.ID)
|
|
c.Set("username", user.Username)
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// requireRole creates middleware that requires a specific role
|
|
func requireRole(roleName string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
user, exists := c.Get("user")
|
|
if !exists {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
authUser, ok := user.(*iam.User)
|
|
if !ok {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Load roles if not already loaded
|
|
if len(authUser.Roles) == 0 {
|
|
// Get DB from context (set by router)
|
|
db, exists := c.Get("db")
|
|
if exists {
|
|
if dbConn, ok := db.(*database.DB); ok {
|
|
roles, err := iam.GetUserRoles(dbConn, authUser.ID)
|
|
if err == nil {
|
|
authUser.Roles = roles
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if user has the required role
|
|
hasRole := false
|
|
for _, role := range authUser.Roles {
|
|
if role == roleName {
|
|
hasRole = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasRole {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// requirePermission creates middleware that requires a specific permission
|
|
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": "authentication required"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
authUser, ok := user.(*iam.User)
|
|
if !ok {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Load permissions if not already loaded
|
|
if len(authUser.Permissions) == 0 {
|
|
// Get DB from context (set by router)
|
|
db, exists := c.Get("db")
|
|
if exists {
|
|
if dbConn, ok := db.(*database.DB); ok {
|
|
permissions, err := iam.GetUserPermissions(dbConn, authUser.ID)
|
|
if err == nil {
|
|
authUser.Permissions = permissions
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if user has the required permission
|
|
permissionName := resource + ":" + action
|
|
hasPermission := false
|
|
for _, perm := range authUser.Permissions {
|
|
if perm == permissionName {
|
|
hasPermission = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasPermission {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|