aditya.siregar 4f5950543e init
2025-07-18 20:10:29 +07:00

142 lines
3.8 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())
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)
}