146 lines
3.9 KiB
Go
146 lines
3.9 KiB
Go
package middleware
|
|
|
|
import (
|
|
"apskel-pos-be/internal/appcontext"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/contract"
|
|
"apskel-pos-be/internal/logger"
|
|
"apskel-pos-be/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type AuthMiddleware struct {
|
|
authService service.AuthService
|
|
}
|
|
|
|
func NewAuthMiddleware(authService service.AuthService) *AuthMiddleware {
|
|
return &AuthMiddleware{
|
|
authService: authService,
|
|
}
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
token := m.extractTokenFromHeader(c)
|
|
if token == "" {
|
|
logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireAuth -> Missing authorization token")
|
|
m.sendErrorResponse(c, "Authorization token is required", http.StatusUnauthorized)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
userResponse, err := m.authService.ValidateToken(token)
|
|
if err != nil {
|
|
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthMiddleware::RequireAuth -> Invalid token")
|
|
m.sendErrorResponse(c, "Invalid or expired token", http.StatusUnauthorized)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
setKeyInContext(c, appcontext.UserRoleKey, userResponse.Role)
|
|
setKeyInContext(c, appcontext.OrganizationIDKey, userResponse.OrganizationID.String())
|
|
setKeyInContext(c, appcontext.UserIDKey, userResponse.ID.String())
|
|
|
|
if (userResponse.Role != "superadmin") {
|
|
setKeyInContext(c, appcontext.OutletIDKey, userResponse.OutletID.String())
|
|
}
|
|
|
|
logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireAuth -> User authenticated: %s", userResponse.Email)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireRole(allowedRoles ...string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
appCtx := appcontext.FromGinContext(c.Request.Context())
|
|
|
|
hasRequiredRole := false
|
|
for _, role := range allowedRoles {
|
|
if appCtx.UserRole == role {
|
|
hasRequiredRole = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasRequiredRole {
|
|
m.sendErrorResponse(c, "Insufficient permissions", http.StatusForbidden)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireAdminOrManager() gin.HandlerFunc {
|
|
return m.RequireRole("admin", "manager")
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireAdmin() gin.HandlerFunc {
|
|
return m.RequireRole("admin")
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireSuperAdmin() gin.HandlerFunc {
|
|
return m.RequireRole("superadmin")
|
|
}
|
|
|
|
func (m *AuthMiddleware) RequireActiveUser() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
userResponse, exists := c.Get("user")
|
|
if !exists {
|
|
logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> User not authenticated")
|
|
m.sendErrorResponse(c, "Authentication required", http.StatusUnauthorized)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
user, ok := userResponse.(*contract.UserResponse)
|
|
if !ok {
|
|
logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> Invalid user context")
|
|
m.sendErrorResponse(c, "Invalid user context", http.StatusInternalServerError)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if !user.IsActive {
|
|
logger.FromContext(c.Request.Context()).Errorf("AuthMiddleware::RequireActiveUser -> User account is deactivated: %s", user.Email)
|
|
m.sendErrorResponse(c, "User account is deactivated", http.StatusForbidden)
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireActiveUser -> Active user check passed: %s", user.Email)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func (m *AuthMiddleware) extractTokenFromHeader(c *gin.Context) string {
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
return ""
|
|
}
|
|
|
|
parts := strings.Split(authHeader, " ")
|
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
|
return ""
|
|
}
|
|
|
|
return parts[1]
|
|
}
|
|
|
|
func (m *AuthMiddleware) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
|
errorResponse := &contract.ErrorResponse{
|
|
Error: "auth_error",
|
|
Message: message,
|
|
Code: statusCode,
|
|
Details: map[string]interface{}{
|
|
"entity": constants.AuthHandlerEntity,
|
|
},
|
|
}
|
|
c.JSON(statusCode, errorResponse)
|
|
}
|