diff --git a/pkg/plugins/manager/manager_integration_test.go b/pkg/plugins/manager/manager_integration_test.go index 5b2fe031811..b829f238ba3 100644 --- a/pkg/plugins/manager/manager_integration_test.go +++ b/pkg/plugins/manager/manager_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/stretchr/testify/require" "gopkg.in/ini.v1" @@ -107,7 +108,7 @@ func TestIntegrationPluginManager(t *testing.T) { ms := mssql.ProvideService(cfg) sv2 := searchV2.ProvideService(cfg, db.InitTestDB(t), nil, nil, tracer, features, nil, nil, nil) graf := grafanads.ProvideService(sv2, nil) - phlare := phlare.ProvideService(hcp) + phlare := phlare.ProvideService(hcp, acimpl.ProvideAccessControl(cfg)) parca := parca.ProvideService(hcp) coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf, phlare, parca) diff --git a/pkg/tsdb/phlare/instance.go b/pkg/tsdb/phlare/instance.go index f89295f8df5..0328ee33695 100644 --- a/pkg/tsdb/phlare/instance.go +++ b/pkg/tsdb/phlare/instance.go @@ -13,6 +13,9 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/infra/httpclient" + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/contexthandler" + "github.com/grafana/grafana/pkg/services/datasources" ) var ( @@ -27,6 +30,7 @@ type PhlareDatasource struct { httpClient *http.Client client ProfilingClient settings backend.DataSourceInstanceSettings + ac accesscontrol.AccessControl } type JsonData struct { @@ -34,7 +38,7 @@ type JsonData struct { } // NewPhlareDatasource creates a new datasource instance. -func NewPhlareDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { +func NewPhlareDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings, ac accesscontrol.AccessControl) (instancemgmt.Instance, error) { opt, err := settings.HTTPClientOptions() if err != nil { return nil, err @@ -54,6 +58,7 @@ func NewPhlareDatasource(httpClientProvider httpclient.Provider, settings backen httpClient: httpClient, client: getClient(jsonData.BackendType, httpClient, settings.URL), settings: settings, + ac: ac, }, nil } @@ -165,6 +170,17 @@ type BackendTypeRespBody struct { // backendType is a simplistic test to figure out if we are speaking to phlare or pyroscope backend func (d *PhlareDatasource) backendType(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + // To prevent any user sending arbitrary URL for us to test with we allow this only for users who can edit the datasource + // as config page is where this is meant to be used. + ok, err := d.isUserAllowedToEditDatasource(ctx) + if err != nil { + return err + } + + if !ok { + return sender.Send(&backend.CallResourceResponse{Headers: req.Headers, Status: 401}) + } + u, err := url.Parse(req.URL) if err != nil { return err @@ -194,11 +210,26 @@ func (d *PhlareDatasource) backendType(ctx context.Context, req *backend.CallRes return err } - err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: req.Headers, Status: 200}) - if err != nil { - return err + return sender.Send(&backend.CallResourceResponse{Body: data, Headers: req.Headers, Status: 200}) +} + +func (d *PhlareDatasource) isUserAllowedToEditDatasource(ctx context.Context) (bool, error) { + reqCtx := contexthandler.FromContext(ctx) + uidScope := datasources.ScopeProvider.GetResourceScopeUID(accesscontrol.Parameter(":uid")) + + if reqCtx == nil || reqCtx.SignedInUser == nil { + return false, nil } - return nil + + ok, err := d.ac.Evaluate(ctx, reqCtx.SignedInUser, accesscontrol.EvalPermission(datasources.ActionWrite, uidScope)) + if err != nil { + return false, err + } + if !ok { + return false, nil + } + + return true, nil } // QueryData handles multiple queries and returns multiple responses. diff --git a/pkg/tsdb/phlare/service.go b/pkg/tsdb/phlare/service.go index e604c1bf7c9..96fef267e62 100644 --- a/pkg/tsdb/phlare/service.go +++ b/pkg/tsdb/phlare/service.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/accesscontrol" ) // Make sure PhlareDatasource implements required interfaces. This is important to do @@ -41,15 +42,15 @@ func (s *Service) getInstance(pluginCtx backend.PluginContext) (*PhlareDatasourc return in, nil } -func ProvideService(httpClientProvider httpclient.Provider) *Service { +func ProvideService(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl) *Service { return &Service{ - im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)), + im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, ac)), } } -func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc { +func newInstanceSettings(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl) datasource.InstanceFactoryFunc { return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return NewPhlareDatasource(httpClientProvider, settings) + return NewPhlareDatasource(httpClientProvider, settings, ac) } }