Rework admin console authentication and UI
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -13,47 +14,30 @@ import (
|
||||
"hightube/internal/utils"
|
||||
)
|
||||
|
||||
const adminSessionCookieName = "hightube_admin_session"
|
||||
|
||||
// AuthMiddleware intercepts requests, validates JWT, and injects user_id into context
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header format must be Bearer {token}"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenStr := parts[1]
|
||||
claims, err := utils.ParseToken(tokenStr)
|
||||
user, err := authenticateRequest(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
|
||||
switch {
|
||||
case errors.Is(err, errMissingToken):
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization is required"})
|
||||
case errors.Is(err, errInvalidToken):
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
|
||||
case errors.Is(err, errUserNotFound):
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"})
|
||||
case errors.Is(err, errDisabledAccount):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Account is disabled"})
|
||||
default:
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
}
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := strconv.ParseUint(claims.Subject, 10, 32)
|
||||
|
||||
var user model.User
|
||||
if err := db.DB.First(&user, uint(userID)).Error; err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !user.Enabled {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Account is disabled"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("user_id", uint(userID))
|
||||
c.Set("user_id", user.ID)
|
||||
c.Set("username", user.Username)
|
||||
c.Set("role", user.Role)
|
||||
c.Next()
|
||||
@@ -82,6 +66,58 @@ func RequestMetricsMiddleware() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errMissingToken = errors.New("missing token")
|
||||
errInvalidToken = errors.New("invalid token")
|
||||
errUserNotFound = errors.New("user not found")
|
||||
errDisabledAccount = errors.New("disabled account")
|
||||
)
|
||||
|
||||
func authenticateRequest(c *gin.Context) (*model.User, error) {
|
||||
tokenStr := extractToken(c)
|
||||
if tokenStr == "" {
|
||||
return nil, errMissingToken
|
||||
}
|
||||
|
||||
claims, err := utils.ParseToken(tokenStr)
|
||||
if err != nil {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
|
||||
userID, err := strconv.ParseUint(claims.Subject, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
|
||||
var user model.User
|
||||
if err := db.DB.First(&user, uint(userID)).Error; err != nil {
|
||||
return nil, errUserNotFound
|
||||
}
|
||||
|
||||
if !user.Enabled {
|
||||
return nil, errDisabledAccount
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func extractToken(c *gin.Context) string {
|
||||
authHeader := strings.TrimSpace(c.GetHeader("Authorization"))
|
||||
if authHeader != "" {
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) == 2 && parts[0] == "Bearer" {
|
||||
return strings.TrimSpace(parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
cookieToken, err := c.Cookie(adminSessionCookieName)
|
||||
if err == nil {
|
||||
return strings.TrimSpace(cookieToken)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// CORSMiddleware handles cross-origin requests from web clients
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user