package middlewares import ( "os" "strings" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" ) // AdminApiAuth protege /api/v2. Acepta dos tipos de credencial: // 1. ADMIN_API_KEY de entorno — llave maestra, acceso total, la que ya usan // las integraciones existentes (no se retira para no romperlas). // 2. Una ApiKey emitida desde /app/api-keys — token propio, hasheado en DB, // restringida a una IP/CIDR obligatoria y a los scopes que se le hayan // habilitado. RequireScope() más adelante en la cadena decide si esa // llave puede usar el grupo de endpoints puntual. func AdminApiAuth() fiber.Handler { return func(c *fiber.Ctx) error { token := "" auth := c.Get("Authorization") if strings.HasPrefix(auth, "Bearer ") { token = auth[7:] } if token == "" { token = c.Get("X-API-Key") } if token == "" { return c.Status(401).JSON(fiber.Map{"error": "unauthorized"}) } if masterKey := os.Getenv("ADMIN_API_KEY"); masterKey != "" && token == masterKey { c.Locals("api_key_master", true) return c.Next() } key, err := models.FindApiKeyActivaByToken(token) if err != nil { return c.Status(401).JSON(fiber.Map{"error": "unauthorized"}) } if !key.IPValida(c.IP()) { return c.Status(403).JSON(fiber.Map{"error": "IP no autorizada para esta API key"}) } go models.RegistrarUsoApiKey(key.ID, c.IP()) c.Locals("api_key", key) return c.Next() } } // RequireScope gatea un grupo de rutas a un scope puntual. La llave maestra // (ADMIN_API_KEY de entorno) siempre pasa; una ApiKey scoped solo pasa si // tiene ese scope habilitado. func RequireScope(scope string) fiber.Handler { return func(c *fiber.Ctx) error { if master, _ := c.Locals("api_key_master").(bool); master { return c.Next() } key, ok := c.Locals("api_key").(*models.ApiKey) if !ok || key == nil || !key.TieneScope(scope) { return c.Status(403).JSON(fiber.Map{"error": "Esta API key no tiene el scope '" + scope + "'"}) } return c.Next() } }