67 lines
1.6 KiB
Go
67 lines
1.6 KiB
Go
|
|
package middleware
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
"veza-backend-api/internal/models"
|
||
|
|
"veza-backend-api/internal/services"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
)
|
||
|
|
|
||
|
|
// RequireAPIKeyScope returns a Gin middleware that enforces API key scopes.
|
||
|
|
// For API key authenticated requests, it verifies the key has the required scope.
|
||
|
|
// JWT-authenticated requests pass through (they already use RBAC).
|
||
|
|
//
|
||
|
|
// Scope mapping:
|
||
|
|
// - GET/HEAD/OPTIONS → "read"
|
||
|
|
// - POST/PUT/PATCH/DELETE → "write"
|
||
|
|
// - "admin" scope implies both "read" and "write"
|
||
|
|
func RequireAPIKeyScope(apiKeyService *services.APIKeyService) gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
apiKeyVal, exists := c.Get("api_key")
|
||
|
|
if !exists {
|
||
|
|
// Not an API key request (JWT auth) — pass through
|
||
|
|
c.Next()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
key, ok := apiKeyVal.(*models.APIKey)
|
||
|
|
if !ok {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||
|
|
"success": false,
|
||
|
|
"error": gin.H{
|
||
|
|
"code": "INTERNAL_ERROR",
|
||
|
|
"message": "Invalid API key context",
|
||
|
|
},
|
||
|
|
})
|
||
|
|
c.Abort()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var requiredScope string
|
||
|
|
switch c.Request.Method {
|
||
|
|
case http.MethodGet, http.MethodHead, http.MethodOptions:
|
||
|
|
requiredScope = "read"
|
||
|
|
default:
|
||
|
|
requiredScope = "write"
|
||
|
|
}
|
||
|
|
|
||
|
|
if !apiKeyService.HasScope(key, requiredScope) {
|
||
|
|
c.JSON(http.StatusForbidden, gin.H{
|
||
|
|
"success": false,
|
||
|
|
"error": gin.H{
|
||
|
|
"code": "INSUFFICIENT_SCOPE",
|
||
|
|
"message": "API key does not have the required scope: " + requiredScope,
|
||
|
|
"required_scope": requiredScope,
|
||
|
|
"key_scopes": key.Scopes,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
c.Abort()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Next()
|
||
|
|
}
|
||
|
|
}
|