diff --git a/pkg/services/live/runstream/manager.go b/pkg/services/live/runstream/manager.go index e2a0033f1f8..f609ef24c40 100644 --- a/pkg/services/live/runstream/manager.go +++ b/pkg/services/live/runstream/manager.go @@ -244,6 +244,8 @@ func getDelay(numErrors int) time.Duration { // run stream until context canceled or stream finished without an error. func (s *Manager) runStream(ctx context.Context, cancelFn func(), sr streamRequest) { + ctx = identity.WithRequester(ctx, sr.user) + defer func() { s.stopStream(sr, cancelFn) }() var numFastErrors int var delay time.Duration diff --git a/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware.go b/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware.go index b8cd387f0f6..c79e04e3a07 100644 --- a/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware.go +++ b/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware.go @@ -5,6 +5,8 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/contexthandler" ) @@ -15,19 +17,36 @@ const forwardIDHeaderName = "X-Grafana-Id" func NewForwardIDMiddleware() backend.HandlerMiddleware { return backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler { return &ForwardIDMiddleware{ + log: log.New("forward_id_middleware"), BaseHandler: backend.NewBaseHandler(next), } }) } type ForwardIDMiddleware struct { + log log.Logger + backend.BaseHandler } -func (m *ForwardIDMiddleware) applyToken(ctx context.Context, pCtx backend.PluginContext, req backend.ForwardHTTPHeaders) error { +func (m *ForwardIDMiddleware) applyToken(ctx context.Context, _ backend.PluginContext, req backend.ForwardHTTPHeaders) error { + if req == nil { + return nil + } + reqCtx := contexthandler.FromContext(ctx) - // no HTTP request context => skip middleware - if req == nil || reqCtx == nil || reqCtx.SignedInUser == nil { + // no HTTP request context => check requester + if reqCtx == nil || reqCtx.SignedInUser == nil { + requester, err := identity.GetRequester(ctx) + if err != nil { + m.log.Debug("Failed to get requester from context", "error", err) + return nil + } + + if requester.GetIDToken() != "" { + req.SetHTTPHeader(forwardIDHeaderName, requester.GetIDToken()) + return nil + } return nil } diff --git a/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware_test.go b/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware_test.go index b8acf0f60b3..f75fee0b700 100644 --- a/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware_test.go +++ b/pkg/services/pluginsintegration/clientmiddleware/forward_id_middleware_test.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/handlertest" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" "github.com/grafana/grafana/pkg/services/user" @@ -235,4 +236,94 @@ func TestForwardIDMiddleware(t *testing.T) { }) }) }) + + t.Run("When signed in with Requester in context", func(t *testing.T) { + cdt := handlertest.NewHandlerMiddlewareTest(t, handlertest.WithMiddlewares(NewForwardIDMiddleware())) + + ctx := context.Background() + requester := &identity.StaticRequester{ + IDToken: "requester-token", + } + ctx = identity.WithRequester(ctx, requester) + + t.Run("And requests are for a datasource", func(t *testing.T) { + pluginContext := backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, + } + + t.Run("Should set forwarded id header from Requester for QueryData", func(t *testing.T) { + _, err := cdt.MiddlewareHandler.QueryData(ctx, &backend.QueryDataRequest{ + PluginContext: pluginContext, + }) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.QueryDataReq.GetHTTPHeader(forwardIDHeaderName)) + }) + + t.Run("Should set forwarded id header from Requester for CallResource", func(t *testing.T) { + err := cdt.MiddlewareHandler.CallResource(ctx, &backend.CallResourceRequest{ + PluginContext: pluginContext, + }, nopCallResourceSender) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.CallResourceReq.GetHTTPHeader(forwardIDHeaderName)) + }) + + t.Run("Should set forwarded id header from Requester for CheckHealth", func(t *testing.T) { + _, err := cdt.MiddlewareHandler.CheckHealth(ctx, &backend.CheckHealthRequest{ + PluginContext: pluginContext, + }) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.CheckHealthReq.GetHTTPHeader(forwardIDHeaderName)) + }) + + t.Run("Should set forwarded id header from Requester for SubscribeStream", func(t *testing.T) { + _, err := cdt.MiddlewareHandler.SubscribeStream(ctx, &backend.SubscribeStreamRequest{ + PluginContext: pluginContext, + }) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.SubscribeStreamReq.GetHTTPHeader(forwardIDHeaderName)) + }) + + t.Run("Should set forwarded id header from Requester for PublishStream", func(t *testing.T) { + _, err := cdt.MiddlewareHandler.PublishStream(ctx, &backend.PublishStreamRequest{ + PluginContext: pluginContext, + }) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.PublishStreamReq.GetHTTPHeader(forwardIDHeaderName)) + }) + + t.Run("Should set forwarded id header from Requester for RunStream", func(t *testing.T) { + err := cdt.MiddlewareHandler.RunStream(ctx, &backend.RunStreamRequest{ + PluginContext: pluginContext, + }, &backend.StreamSender{}) + require.NoError(t, err) + require.Equal(t, "requester-token", cdt.RunStreamReq.GetHTTPHeader(forwardIDHeaderName)) + }) + }) + }) + + t.Run("When signed in with both Requester and SignedInUser", func(t *testing.T) { + cdt := handlertest.NewHandlerMiddlewareTest(t, handlertest.WithMiddlewares(NewForwardIDMiddleware())) + + ctx := context.Background() + requester := &identity.StaticRequester{ + IDToken: "requester-token", + } + ctx = identity.WithRequester(ctx, requester) + ctx = context.WithValue(ctx, ctxkey.Key{}, &contextmodel.ReqContext{ + Context: &web.Context{Req: &http.Request{}}, + SignedInUser: &user.SignedInUser{IDToken: "signed-in-token"}, + }) + + t.Run("Should prefer SignedInUser token over Requester token", func(t *testing.T) { + pluginContext := backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, + } + + _, err := cdt.MiddlewareHandler.QueryData(ctx, &backend.QueryDataRequest{ + PluginContext: pluginContext, + }) + require.NoError(t, err) + require.Equal(t, "signed-in-token", cdt.QueryDataReq.GetHTTPHeader(forwardIDHeaderName)) + }) + }) }