package api import ( "net/http" "strings" "browser.local/platform/domain" "browser.local/platform/service" ) func (h *coreHandlers) requireAuthorizedAPI(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.URL.Path, "/api/v1/") || publicAPIRequest(r) || runServiceRequest(r) { next.ServeHTTP(w, r) return } user, err := h.core.GetCurrentUser(bearerToken(r)) if err != nil { writeServiceError(w, err) return } if platformAdminRequest(r) && !apiPlatformAdmin(user) { writeServiceError(w, service.ErrForbidden) return } next.ServeHTTP(w, r) }) } func publicAPIRequest(r *http.Request) bool { path := r.URL.Path if path == "/api/v1/auth/login" || path == "/api/v1/auth/register" { return true } if r.Method != http.MethodGet { return false } return path == "/api/v1/game-plugins" || strings.HasPrefix(path, "/api/v1/game-plugins/") || path == "/api/v1/plugin-marketplace/plugins" || strings.HasPrefix(path, "/api/v1/plugin-marketplace/plugins/") } func runServiceRequest(r *http.Request) bool { // Run owns the authenticated machine channels under /run/. Keep the // browser's endpoint-management API on the normal bearer/admin path, but do // not maintain a list of individual Run channel prefixes here. New typed // plugin channels (for example /run/scum/facts) must reach their own // signature/session middleware instead of being rejected by this router. path := strings.TrimSuffix(r.URL.Path, "/") return strings.HasPrefix(path, "/api/v1/run/") && path != "/api/v1/run/endpoints" && !strings.HasPrefix(path, "/api/v1/run/endpoints/") } func platformAdminRequest(r *http.Request) bool { path := r.URL.Path if path == "/api/v1/users/current" || strings.HasPrefix(path, "/api/v1/users/current/") { return false } if path == "/api/v1/users" || strings.HasPrefix(path, "/api/v1/users/") || path == "/api/v1/ai-providers" || strings.HasPrefix(path, "/api/v1/ai-providers/") || path == "/api/v1/metrics/platform" || path == "/api/v1/run/endpoints" || strings.HasPrefix(path, "/api/v1/run/endpoints/") { return true } if r.Method != http.MethodGet && (path == "/api/v1/game-plugins" || strings.HasPrefix(path, "/api/v1/game-plugins/") || strings.Contains(path, "/plugin-marketplace/plugins/")) { return true } return r.Method == http.MethodPost && (path == "/api/v1/jobs" || path == "/api/v1/artifacts" || path == "/api/v1/log-streams") } func apiPlatformAdmin(user domain.User) bool { for _, role := range user.Roles { if role == "platform-admin" || role == "admin" || role == "platformadmin" { return true } } return false }