fix gateway loopback validation chains
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -468,6 +469,10 @@ func (s *Server) estimatePricing(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "model is required")
|
||||
return
|
||||
}
|
||||
if !apiKeyScopeAllowed(user, kind) {
|
||||
writeError(w, http.StatusForbidden, "api key scope does not allow this capability")
|
||||
return
|
||||
}
|
||||
estimate, err := s.runner.Estimate(r.Context(), kind, model, body, user)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNoModelCandidate) {
|
||||
@@ -509,6 +514,10 @@ func (s *Server) createTask(kind string, compatible bool) http.Handler {
|
||||
writeError(w, http.StatusBadRequest, "model is required")
|
||||
return
|
||||
}
|
||||
if !apiKeyScopeAllowed(user, kind) {
|
||||
writeError(w, http.StatusForbidden, "api key scope does not allow this capability")
|
||||
return
|
||||
}
|
||||
|
||||
task, err := s.store.CreateTask(r.Context(), store.CreateTaskInput{
|
||||
Kind: kind,
|
||||
@@ -567,6 +576,36 @@ func (s *Server) createTask(kind string, compatible bool) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func apiKeyScopeAllowed(user *auth.User, kind string) bool {
|
||||
if user == nil || strings.TrimSpace(user.APIKeyID) == "" || len(user.APIKeyScopes) == 0 {
|
||||
return true
|
||||
}
|
||||
required := scopeForTaskKind(kind)
|
||||
for _, scope := range user.APIKeyScopes {
|
||||
scope = strings.TrimSpace(strings.ToLower(scope))
|
||||
if scope == "*" || scope == "all" || scope == required {
|
||||
return true
|
||||
}
|
||||
if required == "chat" && (scope == "text" || scope == "text_generate") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func scopeForTaskKind(kind string) string {
|
||||
switch kind {
|
||||
case "chat.completions", "responses":
|
||||
return "chat"
|
||||
case "images.generations", "images.edits":
|
||||
return "image"
|
||||
case "videos.generations":
|
||||
return "video"
|
||||
default:
|
||||
return kind
|
||||
}
|
||||
}
|
||||
|
||||
func statusFromRunError(err error) int {
|
||||
switch {
|
||||
case errors.Is(err, store.ErrNoModelCandidate):
|
||||
@@ -578,6 +617,30 @@ func statusFromRunError(err error) int {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) listTasks(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := auth.UserFromContext(r.Context())
|
||||
if !ok {
|
||||
writeError(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
limit := 50
|
||||
if raw := strings.TrimSpace(r.URL.Query().Get("limit")); raw != "" {
|
||||
parsed, err := strconv.Atoi(raw)
|
||||
if err != nil || parsed <= 0 {
|
||||
writeError(w, http.StatusBadRequest, "invalid limit")
|
||||
return
|
||||
}
|
||||
limit = parsed
|
||||
}
|
||||
tasks, err := s.store.ListTasks(r.Context(), user, limit)
|
||||
if err != nil {
|
||||
s.logger.Error("list tasks failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "list tasks failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": tasks})
|
||||
}
|
||||
|
||||
func boolValue(body map[string]any, key string) bool {
|
||||
value, _ := body[key].(bool)
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user