package httpserver import ( "errors" "net/http" "strings" "update_server/internal/apikeys" ) func (h *handler) requireAPIKey(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if h.apiKeys == nil { writeJSON(w, http.StatusServiceUnavailable, map[string]any{"error": "api key auth is unavailable"}) return } token, ok := bearerToken(r.Header.Get("Authorization")) if !ok { writeAPIKeyAuthError(w, http.StatusUnauthorized, "missing bearer api key") return } state, err := h.apiKeys.Authenticate(r.Context(), token) if err != nil { if errors.Is(err, apikeys.ErrUnauthenticated) { writeAPIKeyAuthError(w, http.StatusUnauthorized, "invalid api key") return } writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "api key lookup failed"}) return } next.ServeHTTP(w, r.WithContext(apikeys.NewContext(r.Context(), state))) }) } func (h *handler) requireAPIKeyPermission(permission apikeys.Permission) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { state, ok := apikeys.FromContext(r.Context()) if !ok { writeAPIKeyAuthError(w, http.StatusUnauthorized, "api key authentication required") return } if !apikeys.HasPermission(state.APIKey, permission) { writeJSON(w, http.StatusForbidden, map[string]any{"error": "api key permission denied"}) return } next.ServeHTTP(w, r) }) } } func (h *handler) requireAPIKeyProjectAccess(routeParam string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { state, ok := apikeys.FromContext(r.Context()) if !ok { writeAPIKeyAuthError(w, http.StatusUnauthorized, "api key authentication required") return } projectID, err := routeID(r, routeParam) if err != nil { writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid project id"}) return } allowed, err := h.apiKeys.CanAccessProject(r.Context(), state.APIKey, projectID) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "project access lookup failed"}) return } if !allowed { writeJSON(w, http.StatusForbidden, map[string]any{"error": "api key cannot access this project"}) return } next.ServeHTTP(w, r) }) } } func bearerToken(header string) (string, bool) { header = strings.TrimSpace(header) if header == "" { return "", false } parts := strings.Fields(header) if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { return "", false } token := strings.TrimSpace(parts[1]) if token == "" { return "", false } return token, true } func writeAPIKeyAuthError(w http.ResponseWriter, status int, message string) { w.Header().Set("WWW-Authenticate", `Bearer realm="update-server"`) writeJSON(w, status, map[string]any{"error": message}) }