Files

112 lines
2.6 KiB
Go

package handlers
import (
"net/http"
"smoop-api/internal/crypto"
"smoop-api/internal/models"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
// UserContext holds structured user information from JWT
type UserContext struct {
ID uint `json:"id"`
Username string `json:"username"`
Role models.Role `json:"role"`
}
func Auth(jwtMgr *crypto.JWTManager) gin.HandlerFunc {
return func(c *gin.Context) {
h := c.GetHeader("Authorization")
if !strings.HasPrefix(h, "Bearer ") {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing bearer token"})
return
}
tok := strings.TrimPrefix(h, "Bearer ")
token, err := jwtMgr.Parse(tok)
if err != nil || !token.Valid {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
return
}
claims, _ := token.Claims.(jwt.MapClaims)
userContext := UserContext{
ID: uint(claims["sub"].(float64)),
Username: claims["name"].(string),
Role: models.Role(claims["role"].(string)),
}
c.Set("user", userContext)
c.Set("claims", claims)
c.Next()
}
}
func RequireRole(role string) gin.HandlerFunc {
return func(c *gin.Context) {
userContext, exists := c.Get("user")
if !exists {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
}
user, ok := userContext.(UserContext)
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid user data"})
return
}
if string(user.Role) != role {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
c.Next()
}
}
// helpers used by handlers
func MustClaims(c *gin.Context) map[string]interface{} {
val, ok := c.Get("claims")
if !ok {
return jwt.MapClaims{}
}
switch t := val.(type) {
case jwt.MapClaims:
return t
case map[string]interface{}:
return jwt.MapClaims(t)
default:
return jwt.MapClaims{}
}
}
func ClaimUserID(claims map[string]interface{}) uint {
if claims == nil {
return 0
}
if v, ok := claims["sub"]; ok {
switch n := v.(type) {
case float64:
return uint(n)
case int:
return uint(n)
case int64:
return uint(n)
}
}
return 0
}
func ClaimRole(claims map[string]interface{}) string {
if r, ok := claims["role"].(string); ok {
return r
}
return ""
}
// New helper to get UserContext from context
func GetUserContext(c *gin.Context) (UserContext, bool) {
userContext, exists := c.Get("user")
if !exists {
return UserContext{}, false
}
user, ok := userContext.(UserContext)
return user, ok
}