diff --git a/pkg/registry/apis/ofrep/register.go b/pkg/registry/apis/ofrep/register.go index 49c755ecc92..afecec1b6b3 100644 --- a/pkg/registry/apis/ofrep/register.go +++ b/pkg/registry/apis/ofrep/register.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -252,6 +253,7 @@ func (b *APIBuilder) oneFlagHandler(w http.ResponseWriter, r *http.Request) { if !b.validateNamespace(r) { _ = tracing.Errorf(span, namespaceMismatchMsg) + span.SetAttributes(semconv.HTTPStatusCode(http.StatusUnauthorized)) b.logger.Error(namespaceMismatchMsg) http.Error(w, namespaceMismatchMsg, http.StatusUnauthorized) return @@ -260,6 +262,7 @@ func (b *APIBuilder) oneFlagHandler(w http.ResponseWriter, r *http.Request) { flagKey := mux.Vars(r)["flagKey"] if flagKey == "" { _ = tracing.Errorf(span, "flagKey parameter is required") + span.SetAttributes(semconv.HTTPStatusCode(http.StatusBadRequest)) http.Error(w, "flagKey parameter is required", http.StatusBadRequest) return } @@ -272,6 +275,7 @@ func (b *APIBuilder) oneFlagHandler(w http.ResponseWriter, r *http.Request) { // Unless the request is authenticated, we only allow public flags evaluations if !isAuthedReq && !isPublicFlag(flagKey) { _ = tracing.Errorf(span, "unauthorized to evaluate flag: %s", flagKey) + span.SetAttributes(semconv.HTTPStatusCode(http.StatusUnauthorized)) b.logger.Error("Unauthorized to evaluate flag", "flagKey", flagKey) http.Error(w, "unauthorized to evaluate flag", http.StatusUnauthorized) return @@ -293,6 +297,7 @@ func (b *APIBuilder) allFlagsHandler(w http.ResponseWriter, r *http.Request) { if !b.validateNamespace(r) { _ = tracing.Errorf(span, namespaceMismatchMsg) + span.SetAttributes(semconv.HTTPStatusCode(http.StatusUnauthorized)) b.logger.Error(namespaceMismatchMsg) http.Error(w, namespaceMismatchMsg, http.StatusUnauthorized) return @@ -357,7 +362,9 @@ func (b *APIBuilder) isAuthenticatedRequest(r *http.Request) bool { // validateNamespace checks if the namespace in the evaluation context matches the namespace in the request func (b *APIBuilder) validateNamespace(r *http.Request) bool { - // Extract namespace from request context or URL path + _, span := tracer.Start(r.Context(), "ofrep.validateNamespace") + defer span.End() + var namespace string user, ok := types.AuthInfoFrom(r.Context()) if !ok { @@ -370,19 +377,25 @@ func (b *APIBuilder) validateNamespace(r *http.Request) bool { namespace = mux.Vars(r)["namespace"] } - // Extract namespace from feature flag evaluation context + // Read request body for namespace validation and tracing body, err := io.ReadAll(r.Body) if err != nil { + _ = tracing.Errorf(span, "failed to read request body: %w", err) b.logger.Error("Error reading evaluation request body", "error", err) + span.SetAttributes(attribute.Bool("validation.success", false)) return false } r.Body = io.NopCloser(bytes.NewBuffer(body)) + span.SetAttributes(attribute.String("request.body", string(body))) + evalCtxNamespace := b.namespaceFromEvalCtx(body) // "default" namespace case can only occur in on-prem grafana if (namespace == "default" && evalCtxNamespace == "") || (evalCtxNamespace == namespace) { + span.SetAttributes(attribute.Bool("validation.success", true)) return true } + span.SetAttributes(attribute.Bool("validation.success", false)) return false }