diff --git a/pkg/services/contexthandler/contexthandler.go b/pkg/services/contexthandler/contexthandler.go index 8dfa2408edd..dc759ffd0d3 100644 --- a/pkg/services/contexthandler/contexthandler.go +++ b/pkg/services/contexthandler/contexthandler.go @@ -6,10 +6,12 @@ import ( "fmt" "net/http" + "github.com/open-feature/go-sdk/openfeature" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" claims "github.com/grafana/authlib/types" + authnClients "github.com/grafana/grafana/pkg/services/authn/clients" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -141,6 +143,16 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler { reqContext.Resp.Before(h.addIDHeaderEndOfRequestFunc(reqContext.SignedInUser)) } + // Set open feature evaluation context with namespace + ns := "default" + if id != nil { + ns = id.Namespace + } + evalCtx := openfeature.NewEvaluationContext(ns, map[string]any{ + "namespace": ns, + }) + ctx = openfeature.MergeTransactionContext(ctx, evalCtx) + // End the span to make next handlers not wrapped within middleware span span.End() next.ServeHTTP(w, r.WithContext(ctx)) diff --git a/pkg/services/contexthandler/contexthandler_test.go b/pkg/services/contexthandler/contexthandler_test.go index 9aace92ed7f..8af7dfaed54 100644 --- a/pkg/services/contexthandler/contexthandler_test.go +++ b/pkg/services/contexthandler/contexthandler_test.go @@ -5,10 +5,12 @@ import ( "net/http" "testing" + "github.com/open-feature/go-sdk/openfeature" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" claims "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/authn" @@ -209,4 +211,50 @@ func TestContextHandler(t *testing.T) { require.NoError(t, res.Body.Close()) }) }) + + t.Run("openfeature evaluation context defaults", func(t *testing.T) { + handler := contexthandler.ProvideService( + setting.NewCfg(), + &authntest.FakeService{ExpectedErr: errors.New("some error")}, + featuremgmt.WithFeatures(), + ) + + server := webtest.NewServer(t, routing.NewRouteRegister()) + server.Mux.Use(handler.Middleware) + server.Mux.Get("/api/handler", func(c *contextmodel.ReqContext) { + evalCtx := openfeature.TransactionContext(c.Req.Context()) + require.NotNil(t, evalCtx) + require.Equal(t, "default", evalCtx.Attribute("namespace")) + }) + + res, err := server.Send(server.NewGetRequest("/api/handler")) + require.NoError(t, err) + require.NoError(t, res.Body.Close()) + }) + + t.Run("openfeature evaluation context with user namespace", func(t *testing.T) { + handler := contexthandler.ProvideService( + setting.NewCfg(), + &authntest.FakeService{ + ExpectedIdentity: &authn.Identity{ + ID: "1", + Type: claims.TypeUser, + Namespace: "org-3", + }, + }, + featuremgmt.WithFeatures(), + ) + + server := webtest.NewServer(t, routing.NewRouteRegister()) + server.Mux.Use(handler.Middleware) + server.Mux.Get("/api/handler", func(c *contextmodel.ReqContext) { + evalCtx := openfeature.TransactionContext(c.Req.Context()) + require.NotNil(t, evalCtx) + require.Equal(t, "org-3", evalCtx.Attribute("namespace")) + }) + + res, err := server.Send(server.NewGetRequest("/api/handler")) + require.NoError(t, err) + require.NoError(t, res.Body.Close()) + }) }