qudoco-be/app/middleware/client.middleware.go

129 lines
3.3 KiB
Go
Raw Permalink Normal View History

2026-02-24 09:37:19 +00:00
package middleware
import (
"strings"
"web-qudo-be/app/database/entity"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
)
const (
ClientKeyHeader = "X-Client-Key"
ClientContextKey = "client_id"
)
// excludedPaths contains paths that don't require client key validation
var excludedPaths = []string{
"/swagger/*",
"/docs/*",
"/users/login",
"/health/*",
"/clients",
"/clients/*",
"*/viewer/*",
"/bookmarks/test-table",
}
// isPathExcluded checks if the given path should be excluded from client key validation
func isPathExcluded(path string) bool {
for _, excludedPath := range excludedPaths {
if strings.HasPrefix(excludedPath, "*") && strings.HasSuffix(excludedPath, "*") {
// Handle wildcard at both beginning and end (e.g., "*/viewer/*")
pattern := excludedPath[1 : len(excludedPath)-1] // Remove * from both ends
if strings.Contains(path, pattern) {
return true
}
} else if strings.HasPrefix(excludedPath, "*") {
// Handle wildcard at the beginning
if strings.HasSuffix(path, excludedPath[1:]) {
return true
}
} else if strings.HasSuffix(excludedPath, "*") {
// Handle wildcard at the end
prefix := excludedPath[:len(excludedPath)-1]
if strings.HasPrefix(path, prefix) {
return true
}
} else {
// Exact match
if path == excludedPath {
return true
}
}
}
return false
}
// ClientMiddleware extracts and validates the Client Key from request headers
func ClientMiddleware(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
// Check if path should be excluded from client key validation
if isPathExcluded(c.Path()) {
return c.Next()
}
// Extract Client Key from header
clientKey := c.Get(ClientKeyHeader)
if clientKey == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"code": 400,
"messages": []string{"Client Key is required in header: " + ClientKeyHeader},
})
}
// Parse UUID
clientUUID, err := uuid.Parse(clientKey)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"code": 400,
"messages": []string{"Invalid Client Key format"},
})
}
// Validate client exists and is active
var client entity.Clients
if err := db.Where("id = ? AND is_active = ?", clientUUID, true).First(&client).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"Invalid or inactive Client Key"},
})
}
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error validating Client Key"},
})
}
// Store client ID in context for use in handlers
c.Locals(ClientContextKey, clientUUID)
return c.Next()
}
}
// GetClientID retrieves the client ID from the context
func GetClientID(c *fiber.Ctx) *uuid.UUID {
if clientID, ok := c.Locals(ClientContextKey).(uuid.UUID); ok {
return &clientID
}
return nil
}
// AddExcludedPath adds a new path to the excluded paths list
func AddExcludedPath(path string) {
excludedPaths = append(excludedPaths, path)
}
// GetExcludedPaths returns the current list of excluded paths
func GetExcludedPaths() []string {
return excludedPaths
}