From 2601070902403de3eefeeead3498d48380696cd0 Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 18:16:56 +0300 Subject: [PATCH] [v9.5.x] DataSourceProxy: Fix url validation error handling (#73320) * DataSourceProxy: Fix url validation error handling (#73087) * DataSourceProxy: Fix url validation error handling * DataSourceProxy: Add unit test for proxyDatasourceRequest * DataSourceProxy: Fix lint error (cherry picked from commit 8ec4c1bdc87c43fe43e6bc56db924a9107edf137) * fix test --------- Co-authored-by: Ricardo Christmann <80476005+ricci2511@users.noreply.github.com> Co-authored-by: Will Browne --- .../datasourceproxy/datasourceproxy.go | 3 +- .../datasourceproxy/datasourceproxy_test.go | 81 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/pkg/services/datasourceproxy/datasourceproxy.go b/pkg/services/datasourceproxy/datasourceproxy.go index 00893b2ef03..6bb3fe64fed 100644 --- a/pkg/services/datasourceproxy/datasourceproxy.go +++ b/pkg/services/datasourceproxy/datasourceproxy.go @@ -122,7 +122,8 @@ func (p *DataSourceProxyService) proxyDatasourceRequest(c *contextmodel.ReqConte proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin.Routes, c, proxyPath, p.Cfg, p.HTTPClientProvider, p.OAuthTokenService, p.DataSourcesService, p.tracer) if err != nil { - if errors.Is(err, datasource.URLValidationError{}) { + var urlValidationError datasource.URLValidationError + if errors.As(err, &urlValidationError) { c.JsonApiErr(http.StatusBadRequest, fmt.Sprintf("Invalid data source URL: %q", ds.URL), err) } else { c.JsonApiErr(http.StatusInternalServerError, "Failed creating data source proxy", err) diff --git a/pkg/services/datasourceproxy/datasourceproxy_test.go b/pkg/services/datasourceproxy/datasourceproxy_test.go index 57da4aa33c0..75835558b0e 100644 --- a/pkg/services/datasourceproxy/datasourceproxy_test.go +++ b/pkg/services/datasourceproxy/datasourceproxy_test.go @@ -1,9 +1,21 @@ package datasourceproxy import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" "testing" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/web" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDataProxy(t *testing.T) { @@ -52,3 +64,72 @@ func TestDataProxy(t *testing.T) { } }) } + +// Tests request to datasource proxy service +func TestDatasourceProxy_proxyDatasourceRequest(t *testing.T) { + tcs := []struct { + name string + dsURL string + expectedErrorMsg string + }{ + { + name: "Empty datasource URL will return a 400 HTTP status code", + dsURL: "", + expectedErrorMsg: "validation of data source URL \"\" failed: empty URL string", + }, + { + name: "Invalid datasource URL will return a 400 HTTP status code", + dsURL: "://host/path", + expectedErrorMsg: "validation of data source URL \"://host/path\" failed: parse \"://host/path\": missing protocol scheme", + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + pluginID := datasources.DS_PROMETHEUS + + pluginStore := &plugins.FakePluginStore{PluginList: []plugins.PluginDTO{ + {JSONData: plugins.JSONData{ID: pluginID}}, + }} + + p := DataSourceProxyService{ + PluginRequestValidator: &fakePluginRequestValidator{}, + pluginStore: pluginStore, + } + + responseRecorder := httptest.NewRecorder() + c := &contextmodel.ReqContext{ + Context: &web.Context{ + Req: &http.Request{URL: &url.URL{}}, + Resp: web.NewResponseWriter("GET", responseRecorder), + }, + Logger: log.NewNopLogger(), + } + + p.proxyDatasourceRequest(c, &datasources.DataSource{ + Type: pluginID, + URL: tc.dsURL, + }) + + resp := responseRecorder.Result() + body := resp.Body + + b, err := io.ReadAll(body) + require.NoError(t, err) + require.NoError(t, body.Close()) + + jsonBody := make(map[string]string) + err = json.Unmarshal(b, &jsonBody) + require.NoError(t, err) + + require.Equal(t, http.StatusBadRequest, resp.StatusCode) + require.Equal(t, fmt.Sprintf("Invalid data source URL: %q", tc.dsURL), jsonBody["message"]) + require.Equal(t, tc.expectedErrorMsg, jsonBody["error"]) + }) + } +} + +type fakePluginRequestValidator struct{} + +func (rv *fakePluginRequestValidator) Validate(_ string, _ *http.Request) error { + return nil +}