Plugins: Modify interface for plugin validations to allow taking PDC into account (#96089)

* Request interceptor: Do not block PDC

* Apply change after feedback received

* Add test

* Check if secure socks proxy configured for the instance

* Apply suggestions from code review

* Add dedicated service for datasource request URL validation (#99179)

---------

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
Sofia Papagiannaki 2025-01-24 17:01:46 +02:00 committed by GitHub
parent 33a53d170b
commit d192a44469
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 161 additions and 85 deletions

View File

@ -841,12 +841,7 @@ func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *data
Headers: map[string]string{}, Headers: map[string]string{},
} }
var dsURL string err = hs.DataSourceRequestValidator.Validate(ds, c.Req)
if req.PluginContext.DataSourceInstanceSettings != nil {
dsURL = req.PluginContext.DataSourceInstanceSettings.URL
}
err = hs.PluginRequestValidator.Validate(dsURL, c.Req)
if err != nil { if err != nil {
return response.Error(http.StatusForbidden, "Access denied", err) return response.Error(http.StatusForbidden, "Access denied", err)
} }

View File

@ -36,7 +36,7 @@ import (
"github.com/grafana/grafana/pkg/web/webtest" "github.com/grafana/grafana/pkg/web/webtest"
) )
type fakePluginRequestValidator struct { type fakeDataSourceRequestValidator struct {
err error err error
} }
@ -45,7 +45,7 @@ type secretsErrorResponseBody struct {
Message string `json:"message"` Message string `json:"message"`
} }
func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error { func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error {
return rv.err return rv.err
} }
@ -56,7 +56,7 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
cfg, cfg,
nil, nil,
nil, nil,
&fakePluginRequestValidator{}, &fakeDataSourceRequestValidator{},
&fakePluginClient{ &fakePluginClient{
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.Responses{ resp := backend.Responses{
@ -114,7 +114,7 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
cfg, cfg,
nil, nil,
nil, nil,
&fakePluginRequestValidator{}, &fakeDataSourceRequestValidator{},
&fakePluginClient{ &fakePluginClient{
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.Responses{ resp := backend.Responses{
@ -309,7 +309,7 @@ func TestDataSourceQueryError(t *testing.T) {
cfg, cfg,
&fakeDatasources.FakeCacheService{}, &fakeDatasources.FakeCacheService{},
nil, nil,
&fakePluginRequestValidator{}, &fakeDataSourceRequestValidator{},
pluginClient.ProvideService(r), pluginClient.ProvideService(r),
plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{ plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{pluginstore.ToGrafanaDTO(p)}, PluginList: []pluginstore.Plugin{pluginstore.ToGrafanaDTO(p)},

View File

@ -140,7 +140,7 @@ type HTTPServer struct {
License licensing.Licensing License licensing.Licensing
AccessControl accesscontrol.AccessControl AccessControl accesscontrol.AccessControl
DataProxy *datasourceproxy.DataSourceProxyService DataProxy *datasourceproxy.DataSourceProxyService
PluginRequestValidator validations.PluginRequestValidator DataSourceRequestValidator validations.DataSourceRequestValidator
pluginClient plugins.Client pluginClient plugins.Client
pluginStore pluginstore.Store pluginStore pluginstore.Store
pluginInstaller plugins.Installer pluginInstaller plugins.Installer
@ -237,7 +237,7 @@ type ServerOptions struct {
func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus, func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus,
renderService rendering.Service, licensing licensing.Licensing, hooksService *hooks.HooksService, renderService rendering.Service, licensing licensing.Licensing, hooksService *hooks.HooksService,
cacheService *localcache.CacheService, sqlStore db.DB, cacheService *localcache.CacheService, sqlStore db.DB,
pluginRequestValidator validations.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver, dataSourceRequestValidator validations.DataSourceRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
pluginDashboardService plugindashboards.Service, pluginStore pluginstore.Store, pluginClient plugins.Client, pluginDashboardService plugindashboards.Service, pluginStore pluginstore.Store, pluginClient plugins.Client,
pluginErrorResolver plugins.ErrorResolver, pluginInstaller plugins.Installer, settingsProvider setting.Provider, pluginErrorResolver plugins.ErrorResolver, pluginInstaller plugins.Installer, settingsProvider setting.Provider,
dataSourceCache datasources.CacheService, userTokenService auth.UserTokenService, dataSourceCache datasources.CacheService, userTokenService auth.UserTokenService,
@ -284,7 +284,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
HooksService: hooksService, HooksService: hooksService,
CacheService: cacheService, CacheService: cacheService,
SQLStore: sqlStore, SQLStore: sqlStore,
PluginRequestValidator: pluginRequestValidator, DataSourceRequestValidator: dataSourceRequestValidator,
pluginInstaller: pluginInstaller, pluginInstaller: pluginInstaller,
pluginClient: pluginClient, pluginClient: pluginClient,
pluginStore: pluginStore, pluginStore: pluginStore,

View File

@ -62,12 +62,7 @@ func (hs *HTTPServer) callPluginResourceWithDataSource(c *contextmodel.ReqContex
return return
} }
var dsURL string err = hs.DataSourceRequestValidator.Validate(ds, c.Req)
if pCtx.DataSourceInstanceSettings != nil {
dsURL = pCtx.DataSourceInstanceSettings.URL
}
err = hs.PluginRequestValidator.Validate(dsURL, c.Req)
if err != nil { if err != nil {
c.JsonApiErr(http.StatusForbidden, "Access denied", err) c.JsonApiErr(http.StatusForbidden, "Access denied", err)
return return

View File

@ -11,7 +11,7 @@ import (
const HostRedirectValidationMiddlewareName = "host-redirect-validation" const HostRedirectValidationMiddlewareName = "host-redirect-validation"
func RedirectLimitMiddleware(reqValidator validations.PluginRequestValidator) sdkhttpclient.Middleware { func RedirectLimitMiddleware(reqValidator validations.DataSourceRequestURLValidator) sdkhttpclient.Middleware {
return sdkhttpclient.NamedMiddlewareFunc(HostRedirectValidationMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper { return sdkhttpclient.NamedMiddlewareFunc(HostRedirectValidationMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper {
return sdkhttpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { return sdkhttpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
res, err := next.RoundTrip(req) res, err := next.RoundTrip(req)
@ -27,7 +27,7 @@ func RedirectLimitMiddleware(reqValidator validations.PluginRequestValidator) sd
return nil, locationErr return nil, locationErr
} }
if validationErr := reqValidator.Validate(location.String(), nil); validationErr != nil { if validationErr := reqValidator.Validate(location.String()); validationErr != nil {
return nil, validationErr return nil, validationErr
} }
} }

View File

@ -20,7 +20,7 @@ import (
var newProviderFunc = sdkhttpclient.NewProvider var newProviderFunc = sdkhttpclient.NewProvider
// New creates a new HTTP client provider with pre-configured middlewares. // New creates a new HTTP client provider with pre-configured middlewares.
func New(cfg *setting.Cfg, validator validations.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider { func New(cfg *setting.Cfg, validator validations.DataSourceRequestURLValidator, tracer tracing.Tracer) *sdkhttpclient.Provider {
logger := log.New("httpclient") logger := log.New("httpclient")
middlewares := []sdkhttpclient.Middleware{ middlewares := []sdkhttpclient.Middleware{

View File

@ -24,7 +24,7 @@ func TestHTTPClientProvider(t *testing.T) {
newProviderFunc = origNewProviderFunc newProviderFunc = origNewProviderFunc
}) })
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
_ = New(&setting.Cfg{SigV4AuthEnabled: false}, &validations.OSSPluginRequestValidator{}, tracer) _ = New(&setting.Cfg{SigV4AuthEnabled: false}, &validations.OSSDataSourceRequestURLValidator{}, tracer)
require.Len(t, providerOpts, 1) require.Len(t, providerOpts, 1)
o := providerOpts[0] o := providerOpts[0]
require.Len(t, o.Middlewares, 9) require.Len(t, o.Middlewares, 9)
@ -50,7 +50,7 @@ func TestHTTPClientProvider(t *testing.T) {
newProviderFunc = origNewProviderFunc newProviderFunc = origNewProviderFunc
}) })
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
_ = New(&setting.Cfg{SigV4AuthEnabled: true}, &validations.OSSPluginRequestValidator{}, tracer) _ = New(&setting.Cfg{SigV4AuthEnabled: true}, &validations.OSSDataSourceRequestURLValidator{}, tracer)
require.Len(t, providerOpts, 1) require.Len(t, providerOpts, 1)
o := providerOpts[0] o := providerOpts[0]
require.Len(t, o.Middlewares, 10) require.Len(t, o.Middlewares, 10)
@ -77,7 +77,7 @@ func TestHTTPClientProvider(t *testing.T) {
newProviderFunc = origNewProviderFunc newProviderFunc = origNewProviderFunc
}) })
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
_ = New(&setting.Cfg{PluginSettings: setting.PluginSettings{"example": {"har_log_enabled": "true"}}}, &validations.OSSPluginRequestValidator{}, tracer) _ = New(&setting.Cfg{PluginSettings: setting.PluginSettings{"example": {"har_log_enabled": "true"}}}, &validations.OSSDataSourceRequestURLValidator{}, tracer)
require.Len(t, providerOpts, 1) require.Len(t, providerOpts, 1)
o := providerOpts[0] o := providerOpts[0]
require.Len(t, o.Middlewares, 10) require.Len(t, o.Middlewares, 10)

View File

@ -70,7 +70,9 @@ var wireExtsBasicSet = wire.NewSet(
wire.Bind(new(pluginaccesscontrol.RoleRegistry), new(*acimpl.Service)), wire.Bind(new(pluginaccesscontrol.RoleRegistry), new(*acimpl.Service)),
wire.Bind(new(accesscontrol.Service), new(*acimpl.Service)), wire.Bind(new(accesscontrol.Service), new(*acimpl.Service)),
validations.ProvideValidator, validations.ProvideValidator,
wire.Bind(new(validations.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)), wire.Bind(new(validations.DataSourceRequestValidator), new(*validations.OSSDataSourceRequestValidator)),
validations.ProvideURLValidator,
wire.Bind(new(validations.DataSourceRequestURLValidator), new(*validations.OSSDataSourceRequestURLValidator)),
provisioning.ProvideService, provisioning.ProvideService,
wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)), wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)),
backgroundsvcs.ProvideBackgroundServiceRegistry, backgroundsvcs.ProvideBackgroundServiceRegistry,

View File

@ -24,35 +24,35 @@ import (
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator validations.PluginRequestValidator, func ProvideService(dataSourceCache datasources.CacheService, datasourceReqValidator validations.DataSourceRequestValidator,
pluginStore pluginstore.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore pluginstore.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider,
oauthTokenService *oauthtoken.Service, dsService datasources.DataSourceService, oauthTokenService *oauthtoken.Service, dsService datasources.DataSourceService,
tracer tracing.Tracer, secretsService secrets.Service, features featuremgmt.FeatureToggles) *DataSourceProxyService { tracer tracing.Tracer, secretsService secrets.Service, features featuremgmt.FeatureToggles) *DataSourceProxyService {
return &DataSourceProxyService{ return &DataSourceProxyService{
DataSourceCache: dataSourceCache, DataSourceCache: dataSourceCache,
PluginRequestValidator: plugReqValidator, DataSourceRequestValidator: datasourceReqValidator,
pluginStore: pluginStore, pluginStore: pluginStore,
Cfg: cfg, Cfg: cfg,
HTTPClientProvider: httpClientProvider, HTTPClientProvider: httpClientProvider,
OAuthTokenService: oauthTokenService, OAuthTokenService: oauthTokenService,
DataSourcesService: dsService, DataSourcesService: dsService,
tracer: tracer, tracer: tracer,
secretsService: secretsService, secretsService: secretsService,
features: features, features: features,
} }
} }
type DataSourceProxyService struct { type DataSourceProxyService struct {
DataSourceCache datasources.CacheService DataSourceCache datasources.CacheService
PluginRequestValidator validations.PluginRequestValidator DataSourceRequestValidator validations.DataSourceRequestValidator
pluginStore pluginstore.Store pluginStore pluginstore.Store
Cfg *setting.Cfg Cfg *setting.Cfg
HTTPClientProvider httpclient.Provider HTTPClientProvider httpclient.Provider
OAuthTokenService *oauthtoken.Service OAuthTokenService *oauthtoken.Service
DataSourcesService datasources.DataSourceService DataSourcesService datasources.DataSourceService
tracer tracing.Tracer tracer tracing.Tracer
secretsService secrets.Service secretsService secrets.Service
features featuremgmt.FeatureToggles features featuremgmt.FeatureToggles
} }
func (p *DataSourceProxyService) ProxyDataSourceRequest(c *contextmodel.ReqContext) { func (p *DataSourceProxyService) ProxyDataSourceRequest(c *contextmodel.ReqContext) {
@ -108,7 +108,7 @@ func toAPIError(c *contextmodel.ReqContext, err error) {
} }
func (p *DataSourceProxyService) proxyDatasourceRequest(c *contextmodel.ReqContext, ds *datasources.DataSource) { func (p *DataSourceProxyService) proxyDatasourceRequest(c *contextmodel.ReqContext, ds *datasources.DataSource) {
err := p.PluginRequestValidator.Validate(ds.URL, c.Req) err := p.DataSourceRequestValidator.Validate(ds, c.Req)
if err != nil { if err != nil {
c.JsonApiErr(http.StatusForbidden, "Access denied", err) c.JsonApiErr(http.StatusForbidden, "Access denied", err)
return return

View File

@ -94,8 +94,8 @@ func TestDatasourceProxy_proxyDatasourceRequest(t *testing.T) {
}} }}
p := DataSourceProxyService{ p := DataSourceProxyService{
PluginRequestValidator: &fakePluginRequestValidator{}, DataSourceRequestValidator: &fakeDataSourceRequestValidator{},
pluginStore: pluginStore, pluginStore: pluginStore,
} }
responseRecorder := httptest.NewRecorder() responseRecorder := httptest.NewRecorder()
@ -129,8 +129,8 @@ func TestDatasourceProxy_proxyDatasourceRequest(t *testing.T) {
} }
} }
type fakePluginRequestValidator struct{} type fakeDataSourceRequestValidator struct{}
func (rv *fakePluginRequestValidator) Validate(_ string, _ *http.Request) error { func (rv *fakeDataSourceRequestValidator) Validate(_ *datasources.DataSource, _ *http.Request) error {
return nil return nil
} }

View File

@ -70,6 +70,16 @@ type DataSource struct {
Created time.Time `json:"created,omitempty"` Created time.Time `json:"created,omitempty"`
Updated time.Time `json:"updated,omitempty"` Updated time.Time `json:"updated,omitempty"`
isSecureSocksDSProxyEnabled *bool `xorm:"-"`
}
func (ds *DataSource) IsSecureSocksDSProxyEnabled() bool {
if ds.isSecureSocksDSProxyEnabled == nil {
enabled := ds.JsonData != nil && ds.JsonData.Get("enableSecureSocksProxy").MustBool(false)
ds.isSecureSocksDSProxyEnabled = &enabled
}
return *ds.isSecureSocksDSProxyEnabled
} }
type TeamHTTPHeadersJSONData struct { type TeamHTTPHeadersJSONData struct {

View File

@ -102,3 +102,58 @@ func TestTeamHTTPHeaders(t *testing.T) {
}) })
} }
} }
func TestIsSecureSocksDSProxyEnabled(t *testing.T) {
testCases := []struct {
desc string
ds *DataSource
want bool
}{
{
desc: "Empty json",
ds: &DataSource{
JsonData: simplejson.New(),
},
want: false,
},
{
desc: "Json with enableSecureSocksProxy",
ds: &DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"enableSecureSocksProxy": true,
}),
},
want: true,
},
{
desc: "Json with string enableSecureSocksProxy",
ds: &DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"enableSecureSocksProxy": "true",
}),
},
want: false,
},
{
desc: "Json with enableSecureSocksProxy false",
ds: &DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"enableSecureSocksProxy": false,
}),
},
want: false,
},
{
desc: "Json with no json data",
ds: &DataSource{},
want: false,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
actual := tc.ds.IsSecureSocksDSProxyEnabled()
assert.Equal(t, tc.want, actual)
})
}
}

View File

@ -745,7 +745,7 @@ func (s *Service) httpClientOptions(ctx context.Context, ds *datasources.DataSou
} }
} }
if ds.JsonData != nil && ds.JsonData.Get("enableSecureSocksProxy").MustBool(false) { if ds.IsSecureSocksDSProxyEnabled() {
proxyOpts := &sdkproxy.Options{ proxyOpts := &sdkproxy.Options{
Enabled: true, Enabled: true,
Auth: &sdkproxy.AuthOptions{ Auth: &sdkproxy.AuthOptions{

View File

@ -152,18 +152,18 @@ func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakeP
setting.NewCfg(), setting.NewCfg(),
cs, cs,
nil, nil,
&fakePluginRequestValidator{}, &fakeDataSourceRequestValidator{},
fpc, fpc,
pCtxProvider, pCtxProvider,
) )
} }
// copied from pkg/api/metrics_test.go // copied from pkg/api/metrics_test.go
type fakePluginRequestValidator struct { type fakeDataSourceRequestValidator struct {
err error err error
} }
func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error { func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error {
return rv.err return rv.err
} }

View File

@ -42,19 +42,19 @@ func ProvideService(
cfg *setting.Cfg, cfg *setting.Cfg,
dataSourceCache datasources.CacheService, dataSourceCache datasources.CacheService,
expressionService *expr.Service, expressionService *expr.Service,
pluginRequestValidator validations.PluginRequestValidator, dataSourceRequestValidator validations.DataSourceRequestValidator,
pluginClient plugins.Client, pluginClient plugins.Client,
pCtxProvider *plugincontext.Provider, pCtxProvider *plugincontext.Provider,
) *ServiceImpl { ) *ServiceImpl {
g := &ServiceImpl{ g := &ServiceImpl{
cfg: cfg, cfg: cfg,
dataSourceCache: dataSourceCache, dataSourceCache: dataSourceCache,
expressionService: expressionService, expressionService: expressionService,
pluginRequestValidator: pluginRequestValidator, dataSourceRequestValidator: dataSourceRequestValidator,
pluginClient: pluginClient, pluginClient: pluginClient,
pCtxProvider: pCtxProvider, pCtxProvider: pCtxProvider,
log: log.New("query_data"), log: log.New("query_data"),
concurrentQueryLimit: cfg.SectionWithEnvOverrides("query").Key("concurrent_query_limit").MustInt(runtime.NumCPU()), concurrentQueryLimit: cfg.SectionWithEnvOverrides("query").Key("concurrent_query_limit").MustInt(runtime.NumCPU()),
} }
g.log.Info("Query Service initialization") g.log.Info("Query Service initialization")
return g return g
@ -70,14 +70,14 @@ type Service interface {
var _ Service = (*ServiceImpl)(nil) var _ Service = (*ServiceImpl)(nil)
type ServiceImpl struct { type ServiceImpl struct {
cfg *setting.Cfg cfg *setting.Cfg
dataSourceCache datasources.CacheService dataSourceCache datasources.CacheService
expressionService *expr.Service expressionService *expr.Service
pluginRequestValidator validations.PluginRequestValidator dataSourceRequestValidator validations.DataSourceRequestValidator
pluginClient plugins.Client pluginClient plugins.Client
pCtxProvider *plugincontext.Provider pCtxProvider *plugincontext.Provider
log log.Logger log log.Logger
concurrentQueryLimit int concurrentQueryLimit int
} }
// Run ServiceImpl. // Run ServiceImpl.
@ -244,7 +244,7 @@ func (s *ServiceImpl) handleExpressions(ctx context.Context, user identity.Reque
func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user identity.Requester, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) { func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user identity.Requester, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) {
queries := parsedReq.getFlattenedQueries() queries := parsedReq.getFlattenedQueries()
ds := queries[0].datasource ds := queries[0].datasource
if err := s.pluginRequestValidator.Validate(ds.URL, nil); err != nil { if err := s.dataSourceRequestValidator.Validate(ds, nil); err != nil {
return nil, datasources.ErrDataSourceAccessDenied return nil, datasources.ErrDataSourceAccessDenied
} }

View File

@ -462,7 +462,7 @@ func setup(t *testing.T) *testContext {
t.Helper() t.Helper()
pc := &fakePluginClient{} pc := &fakePluginClient{}
dc := &fakeDataSourceCache{cache: dss} dc := &fakeDataSourceCache{cache: dss}
rv := &fakePluginRequestValidator{} rv := &fakeDataSourceRequestValidator{}
sqlStore, cfg := db.InitTestDBWithCfg(t) sqlStore, cfg := db.InitTestDBWithCfg(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
@ -497,7 +497,7 @@ func setup(t *testing.T) *testContext {
type testContext struct { type testContext struct {
pluginContext *fakePluginClient pluginContext *fakePluginClient
secretStore secretskvs.SecretsKVStore secretStore secretskvs.SecretsKVStore
pluginRequestValidator *fakePluginRequestValidator pluginRequestValidator *fakeDataSourceRequestValidator
queryService *ServiceImpl // implementation belonging to this package queryService *ServiceImpl // implementation belonging to this package
signedInUser *user.SignedInUser signedInUser *user.SignedInUser
} }
@ -518,11 +518,11 @@ func metricRequestWithQueries(t *testing.T, rawQueries ...string) dtos.MetricReq
} }
} }
type fakePluginRequestValidator struct { type fakeDataSourceRequestValidator struct {
err error err error
} }
func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error { func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error {
return rv.err return rv.err
} }

View File

@ -2,14 +2,26 @@ package validations
import ( import (
"net/http" "net/http"
"github.com/grafana/grafana/pkg/services/datasources"
) )
type OSSPluginRequestValidator struct{} type OSSDataSourceRequestValidator struct{}
func (*OSSPluginRequestValidator) Validate(string, *http.Request) error { func (*OSSDataSourceRequestValidator) Validate(*datasources.DataSource, *http.Request) error {
return nil return nil
} }
func ProvideValidator() *OSSPluginRequestValidator { func ProvideValidator() *OSSDataSourceRequestValidator {
return &OSSPluginRequestValidator{} return &OSSDataSourceRequestValidator{}
}
type OSSDataSourceRequestURLValidator struct{}
func (*OSSDataSourceRequestURLValidator) Validate(string) error {
return nil
}
func ProvideURLValidator() *OSSDataSourceRequestURLValidator {
return &OSSDataSourceRequestURLValidator{}
} }

View File

@ -2,11 +2,18 @@ package validations
import ( import (
"net/http" "net/http"
"github.com/grafana/grafana/pkg/services/datasources"
) )
type PluginRequestValidator interface { type DataSourceRequestValidator interface {
// Validate performs a request validation based // Validate performs a request validation based
// on the data source URL and some of the request // on the data source URL and some of the request
// attributes (headers, cookies, etc). // attributes (headers, cookies, etc).
Validate(dsURL string, req *http.Request) error Validate(ds *datasources.DataSource, req *http.Request) error
}
type DataSourceRequestURLValidator interface {
// Validate performs a request validation based on the data source URL
Validate(dsURL string) error
} }