diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 2f2acf54175..cf9f0b00c0f 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -524,19 +524,19 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo ds, err := hs.DataSourceCache.GetDatasource(c.Req.Context(), datasourceID, c.SignedInUser, c.SkipCache) if err != nil { if errors.Is(err, models.ErrDataSourceAccessDenied) { - return response.Error(403, "Access denied to datasource", err) + return response.Error(http.StatusForbidden, "Access denied to datasource", err) } - return response.Error(500, "Unable to load datasource metadata", err) + return response.Error(http.StatusInternalServerError, "Unable to load datasource metadata", err) } plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), ds.Type) if !exists { - return response.Error(500, "Unable to find datasource plugin", err) + return response.Error(http.StatusInternalServerError, "Unable to find datasource plugin", err) } dsInstanceSettings, err := adapters.ModelToInstanceSettings(ds, hs.decryptSecureJsonDataFn()) if err != nil { - return response.Error(500, "Unable to get datasource model", err) + return response.Error(http.StatusInternalServerError, "Unable to get datasource model", err) } req := &backend.CheckHealthRequest{ PluginContext: backend.PluginContext{ @@ -547,6 +547,16 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo }, } + var dsURL string + if req.PluginContext.DataSourceInstanceSettings != nil { + dsURL = req.PluginContext.DataSourceInstanceSettings.URL + } + + err = hs.PluginRequestValidator.Validate(dsURL, c.Req) + if err != nil { + return response.Error(http.StatusForbidden, "Access denied", err) + } + resp, err := hs.pluginClient.CheckHealth(c.Req.Context(), req) if err != nil { return translatePluginRequestErrorToAPIError(err) @@ -562,17 +572,17 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo var jsonDetails map[string]interface{} err = json.Unmarshal(resp.JSONDetails, &jsonDetails) if err != nil { - return response.Error(500, "Failed to unmarshal detailed response from backend plugin", err) + return response.Error(http.StatusInternalServerError, "Failed to unmarshal detailed response from backend plugin", err) } payload["details"] = jsonDetails } if resp.Status != backend.HealthStatusOk { - return response.JSON(400, payload) + return response.JSON(http.StatusBadRequest, payload) } - return response.JSON(200, payload) + return response.JSON(http.StatusOK, payload) } func (hs *HTTPServer) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string { diff --git a/pkg/plugins/backendplugin/coreplugin/registry.go b/pkg/plugins/backendplugin/coreplugin/registry.go index 8b4fe4e802b..1b27c5cf3a6 100644 --- a/pkg/plugins/backendplugin/coreplugin/registry.go +++ b/pkg/plugins/backendplugin/coreplugin/registry.go @@ -1,8 +1,10 @@ package coreplugin import ( - "github.com/grafana/grafana-plugin-sdk-go/backend" + "context" + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/tsdb/azuremonitor" "github.com/grafana/grafana/pkg/tsdb/cloudmonitoring" @@ -76,6 +78,16 @@ func (cr *Registry) Get(pluginID string) backendplugin.PluginFactoryFunc { return cr.store[pluginID] } +func (cr *Registry) BackendFactoryProvider() func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc { + return func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc { + if !p.IsCorePlugin() { + return nil + } + + return cr.Get(p.ID) + } +} + func asBackendPlugin(svc interface{}) backendplugin.PluginFactoryFunc { opts := backend.ServeOpts{} if queryHandler, ok := svc.(backend.QueryDataHandler); ok { diff --git a/pkg/plugins/backendplugin/provider/provider.go b/pkg/plugins/backendplugin/provider/provider.go index 53047c91374..4a8a18c4c9a 100644 --- a/pkg/plugins/backendplugin/provider/provider.go +++ b/pkg/plugins/backendplugin/provider/provider.go @@ -12,18 +12,28 @@ import ( "github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2" ) +// PluginBackendProvider is a function type for initializing a Plugin backend. +type PluginBackendProvider func(_ context.Context, _ *plugins.Plugin) backendplugin.PluginFactoryFunc + type Service struct { - coreRegistry *coreplugin.Registry + providerChain []PluginBackendProvider } -func ProvideService(coreRegistry *coreplugin.Registry) *Service { +func New(providers ...PluginBackendProvider) *Service { + if len(providers) == 0 { + return New(RendererProvider, DefaultProvider) + } return &Service{ - coreRegistry: coreRegistry, + providerChain: providers, } } +func ProvideService(coreRegistry *coreplugin.Registry) *Service { + return New(coreRegistry.BackendFactoryProvider(), RendererProvider, DefaultProvider) +} + func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc { - for _, provider := range []PluginBackendProvider{CorePluginProvider(ctx, s.coreRegistry), RendererProvider, DefaultProvider} { + for _, provider := range s.providerChain { if factory := provider(ctx, p); factory != nil { return factory } @@ -31,9 +41,6 @@ func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backend return nil } -// PluginBackendProvider is a function type for initializing a Plugin backend. -type PluginBackendProvider func(_ context.Context, _ *plugins.Plugin) backendplugin.PluginFactoryFunc - var RendererProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc { if !p.IsRenderer() { return nil @@ -52,13 +59,3 @@ var DefaultProvider PluginBackendProvider = func(_ context.Context, p *plugins.P cmd := plugins.ComposePluginStartCommand(p.Executable) return grpcplugin.NewBackendPlugin(p.ID, filepath.Join(p.PluginDir, cmd)) } - -var CorePluginProvider = func(ctx context.Context, registry *coreplugin.Registry) PluginBackendProvider { - return func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc { - if !p.IsCorePlugin() { - return nil - } - - return registry.Get(p.ID) - } -} diff --git a/pkg/plugins/manager/dashboard_import_test.go b/pkg/plugins/manager/dashboard_import_test.go index 3043a826cac..b4594930739 100644 --- a/pkg/plugins/manager/dashboard_import_test.go +++ b/pkg/plugins/manager/dashboard_import_test.go @@ -93,8 +93,8 @@ func pluginScenario(t *testing.T, desc string, fn func(*testing.T, *PluginManage } pmCfg := plugins.FromGrafanaCfg(cfg) - pm, err := ProvideService(cfg, nil, loader.New(pmCfg, nil, - &signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, &provider.Service{}), &sqlstore.SQLStore{}) + pm, err := ProvideService(cfg, loader.New(pmCfg, nil, signature.NewUnsignedAuthorizer(pmCfg), + &provider.Service{}), &sqlstore.SQLStore{}) require.NoError(t, err) t.Run(desc, func(t *testing.T) { diff --git a/pkg/plugins/manager/dashboards_test.go b/pkg/plugins/manager/dashboards_test.go index c3a834de961..36b3bfc8ae3 100644 --- a/pkg/plugins/manager/dashboards_test.go +++ b/pkg/plugins/manager/dashboards_test.go @@ -25,8 +25,8 @@ func TestGetPluginDashboards(t *testing.T) { }, } pmCfg := plugins.FromGrafanaCfg(cfg) - pm, err := ProvideService(cfg, nil, loader.New(pmCfg, nil, - &signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, &provider.Service{}), &sqlstore.SQLStore{}) + pm, err := ProvideService(cfg, loader.New(pmCfg, nil, + signature.NewUnsignedAuthorizer(pmCfg), &provider.Service{}), &sqlstore.SQLStore{}) require.NoError(t, err) bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error { diff --git a/pkg/plugins/manager/loader/loader_test.go b/pkg/plugins/manager/loader/loader_test.go index e178cd820d9..59cb67d8e86 100644 --- a/pkg/plugins/manager/loader/loader_test.go +++ b/pkg/plugins/manager/loader/loader_test.go @@ -1081,7 +1081,7 @@ func newLoader(cfg *plugins.Cfg) *Loader { cfg: cfg, pluginFinder: finder.New(), pluginInitializer: initializer.New(cfg, provider.ProvideService(coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))), &fakeLicensingService{}), - signatureValidator: signature.NewValidator(&signature.UnsignedPluginAuthorizer{Cfg: cfg}), + signatureValidator: signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), errs: make(map[string]*plugins.SignatureError), log: &fakeLogger{}, } diff --git a/pkg/plugins/manager/manager.go b/pkg/plugins/manager/manager.go index 459f037171b..228a5fd52f3 100644 --- a/pkg/plugins/manager/manager.go +++ b/pkg/plugins/manager/manager.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net/http" "path/filepath" "sync" "time" @@ -12,7 +11,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/instrumentation" @@ -33,20 +31,19 @@ var _ plugins.StaticRouteResolver = (*PluginManager)(nil) var _ plugins.RendererManager = (*PluginManager)(nil) type PluginManager struct { - cfg *plugins.Cfg - requestValidator models.PluginRequestValidator - sqlStore *sqlstore.SQLStore - store map[string]*plugins.Plugin - pluginInstaller plugins.Installer - pluginLoader plugins.Loader - pluginsMu sync.RWMutex - pluginPaths map[plugins.Class][]string - log log.Logger + cfg *plugins.Cfg + sqlStore *sqlstore.SQLStore + store map[string]*plugins.Plugin + pluginInstaller plugins.Installer + pluginLoader plugins.Loader + pluginsMu sync.RWMutex + pluginPaths map[plugins.Class][]string + log log.Logger } -func ProvideService(grafanaCfg *setting.Cfg, requestValidator models.PluginRequestValidator, pluginLoader plugins.Loader, +func ProvideService(grafanaCfg *setting.Cfg, pluginLoader plugins.Loader, sqlStore *sqlstore.SQLStore) (*PluginManager, error) { - pm := New(plugins.FromGrafanaCfg(grafanaCfg), requestValidator, map[plugins.Class][]string{ + pm := New(plugins.FromGrafanaCfg(grafanaCfg), map[plugins.Class][]string{ plugins.Core: corePluginPaths(grafanaCfg), plugins.Bundled: {grafanaCfg.BundledPluginsPath}, plugins.External: append([]string{grafanaCfg.PluginsPath}, pluginSettingPaths(grafanaCfg)...), @@ -57,17 +54,16 @@ func ProvideService(grafanaCfg *setting.Cfg, requestValidator models.PluginReque return pm, nil } -func New(cfg *plugins.Cfg, requestValidator models.PluginRequestValidator, pluginPaths map[plugins.Class][]string, - pluginLoader plugins.Loader, sqlStore *sqlstore.SQLStore) *PluginManager { +func New(cfg *plugins.Cfg, pluginPaths map[plugins.Class][]string, pluginLoader plugins.Loader, + sqlStore *sqlstore.SQLStore) *PluginManager { return &PluginManager{ - cfg: cfg, - requestValidator: requestValidator, - pluginLoader: pluginLoader, - pluginPaths: pluginPaths, - store: make(map[string]*plugins.Plugin), - log: log.New("plugin.manager"), - pluginInstaller: installer.New(false, cfg.BuildVersion, newInstallerLogger("plugin.installer", true)), - sqlStore: sqlStore, + cfg: cfg, + pluginLoader: pluginLoader, + pluginPaths: pluginPaths, + store: make(map[string]*plugins.Plugin), + log: log.New("plugin.manager"), + pluginInstaller: installer.New(false, cfg.BuildVersion, newInstallerLogger("plugin.installer", true)), + sqlStore: sqlStore, } } @@ -253,26 +249,13 @@ func (m *PluginManager) CollectMetrics(ctx context.Context, pluginID string) (*b } func (m *PluginManager) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { - var dsURL string - if req.PluginContext.DataSourceInstanceSettings != nil { - dsURL = req.PluginContext.DataSourceInstanceSettings.URL - } - - err := m.requestValidator.Validate(dsURL, nil) - if err != nil { - return &backend.CheckHealthResult{ - Status: http.StatusForbidden, - Message: "Access denied", - }, nil - } - p, exists := m.plugin(req.PluginContext.PluginID) if !exists { return nil, backendplugin.ErrPluginNotRegistered } var resp *backend.CheckHealthResult - err = instrumentation.InstrumentCheckHealthRequest(p.PluginID(), func() (innerErr error) { + err := instrumentation.InstrumentCheckHealthRequest(p.PluginID(), func() (innerErr error) { resp, innerErr = p.CheckHealth(ctx, &backend.CheckHealthRequest{PluginContext: req.PluginContext}) return }) diff --git a/pkg/plugins/manager/manager_integration_test.go b/pkg/plugins/manager/manager_integration_test.go index a6da7682ec2..fee12b49186 100644 --- a/pkg/plugins/manager/manager_integration_test.go +++ b/pkg/plugins/manager/manager_integration_test.go @@ -91,8 +91,8 @@ func TestPluginManager_int_init(t *testing.T) { coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf) pmCfg := plugins.FromGrafanaCfg(cfg) - pm, err := ProvideService(cfg, nil, loader.New(pmCfg, license, - &signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, provider.ProvideService(coreRegistry)), nil) + pm, err := ProvideService(cfg, loader.New(pmCfg, license, signature.NewUnsignedAuthorizer(pmCfg), + provider.ProvideService(coreRegistry)), nil) require.NoError(t, err) verifyCorePluginCatalogue(t, pm) diff --git a/pkg/plugins/manager/manager_test.go b/pkg/plugins/manager/manager_test.go index fd67c58b068..e85fdb59ef7 100644 --- a/pkg/plugins/manager/manager_test.go +++ b/pkg/plugins/manager/manager_test.go @@ -467,8 +467,7 @@ func TestPluginManager_lifecycle_unmanaged(t *testing.T) { func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager { t.Helper() - requestValidator := &testPluginRequestValidator{} - pm := New(&plugins.Cfg{}, requestValidator, nil, &fakeLoader{}, &sqlstore.SQLStore{}) + pm := New(&plugins.Cfg{}, nil, &fakeLoader{}, &sqlstore.SQLStore{}) for _, cb := range cbs { cb(pm) @@ -521,9 +520,8 @@ func newScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *managerS cfg.Azure.Cloud = "AzureCloud" cfg.Azure.ManagedIdentityClientId = "client-id" - requestValidator := &testPluginRequestValidator{} loader := &fakeLoader{} - manager := New(cfg, requestValidator, nil, loader, nil) + manager := New(cfg, nil, loader, nil) manager.pluginLoader = loader ctx := &managerScenarioCtx{ manager: manager, @@ -698,12 +696,6 @@ func (pc *fakePluginClient) RunStream(_ context.Context, _ *backend.RunStreamReq return backendplugin.ErrMethodNotImplemented } -type testPluginRequestValidator struct{} - -func (t *testPluginRequestValidator) Validate(string, *http.Request) error { - return nil -} - type fakeLogger struct { log.Logger } diff --git a/pkg/plugins/manager/signature/authorizer.go b/pkg/plugins/manager/signature/authorizer.go index e0bd7d8da05..24a8c97596a 100644 --- a/pkg/plugins/manager/signature/authorizer.go +++ b/pkg/plugins/manager/signature/authorizer.go @@ -5,14 +5,18 @@ import ( "github.com/grafana/grafana/pkg/setting" ) -func ProvideService(cfg *setting.Cfg) (*UnsignedPluginAuthorizer, error) { +func NewUnsignedAuthorizer(cfg *plugins.Cfg) *UnsignedPluginAuthorizer { return &UnsignedPluginAuthorizer{ - Cfg: plugins.FromGrafanaCfg(cfg), - }, nil + cfg: cfg, + } +} + +func ProvideOSSAuthorizer(cfg *setting.Cfg) *UnsignedPluginAuthorizer { + return NewUnsignedAuthorizer(plugins.FromGrafanaCfg(cfg)) } type UnsignedPluginAuthorizer struct { - Cfg *plugins.Cfg + cfg *plugins.Cfg } func (u *UnsignedPluginAuthorizer) CanLoadPlugin(p *plugins.Plugin) bool { @@ -20,11 +24,11 @@ func (u *UnsignedPluginAuthorizer) CanLoadPlugin(p *plugins.Plugin) bool { return true } - if u.Cfg.DevMode { + if u.cfg.DevMode { return true } - for _, pID := range u.Cfg.PluginsAllowUnsigned { + for _, pID := range u.cfg.PluginsAllowUnsigned { if pID == p.ID { return true } diff --git a/pkg/server/wireexts_oss.go b/pkg/server/wireexts_oss.go index 156e92b973a..20a47214160 100644 --- a/pkg/server/wireexts_oss.go +++ b/pkg/server/wireexts_oss.go @@ -61,7 +61,7 @@ var wireExtsBasicSet = wire.NewSet( wire.Bind(new(models.SearchUserFilter), new(*filters.OSSSearchUserFilter)), searchusers.ProvideUsersService, wire.Bind(new(searchusers.Service), new(*searchusers.OSSService)), - signature.ProvideService, + signature.ProvideOSSAuthorizer, wire.Bind(new(plugins.PluginLoaderAuthorizer), new(*signature.UnsignedPluginAuthorizer)), provider.ProvideService, wire.Bind(new(plugins.BackendFactoryProvider), new(*provider.Service)),