Datasource: Fix allowed cookies to be forwarded as header to backend datasources (#49541)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
Marcus Efraimsson 2022-05-31 17:02:58 +02:00 committed by GitHub
parent e82784bff0
commit 1196b4a609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 0 deletions

View File

@ -3,6 +3,7 @@ package dtos
import ( import (
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"net/http"
"regexp" "regexp"
"strings" "strings"
@ -68,6 +69,8 @@ type MetricRequest struct {
Queries []*simplejson.Json `json:"queries"` Queries []*simplejson.Json `json:"queries"`
// required: false // required: false
Debug bool `json:"debug"` Debug bool `json:"debug"`
HTTPRequest *http.Request `json:"-"`
} }
func GetGravatarUrl(text string) string { func GetGravatarUrl(text string) string {

View File

@ -44,6 +44,8 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
reqDTO.HTTPRequest = c.Req
resp, err := hs.queryDataService.QueryData(c.Req.Context(), c.SignedInUser, c.SkipCache, reqDTO, true) resp, err := hs.queryDataService.QueryData(c.Req.Context(), c.SignedInUser, c.SkipCache, reqDTO, true)
if err != nil { if err != nil {
return hs.handleQueryMetricsError(err) return hs.handleQueryMetricsError(err)

View File

@ -3,6 +3,7 @@ package query
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
"time" "time"
@ -18,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/grafanads" "github.com/grafana/grafana/pkg/tsdb/grafanads"
"github.com/grafana/grafana/pkg/tsdb/legacydata" "github.com/grafana/grafana/pkg/tsdb/legacydata"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
) )
@ -149,6 +151,19 @@ func (s *Service) handleQueryData(ctx context.Context, user *models.SignedInUser
req.Headers[k] = v req.Headers[k] = v
} }
if parsedReq.httpRequest != nil && parsedReq.httpRequest.Header.Get("Cookie") != "" && ds.JsonData != nil {
keepCookieNames := []string{}
if keepCookies := ds.JsonData.Get("keepCookies"); keepCookies != nil {
keepCookieNames = keepCookies.MustStringArray()
}
proxyutil.ClearCookieHeader(parsedReq.httpRequest, keepCookieNames)
if cookieStr := parsedReq.httpRequest.Header.Get("Cookie"); cookieStr != "" {
req.Headers["Cookie"] = cookieStr
}
}
for _, q := range parsedReq.parsedQueries { for _, q := range parsedReq.parsedQueries {
req.Queries = append(req.Queries, q.query) req.Queries = append(req.Queries, q.query)
} }
@ -164,6 +179,7 @@ type parsedQuery struct {
type parsedRequest struct { type parsedRequest struct {
hasExpression bool hasExpression bool
parsedQueries []parsedQuery parsedQueries []parsedQuery
httpRequest *http.Request
} }
func customHeaders(jsonData *simplejson.Json, decryptedJsonData map[string]string) map[string]string { func customHeaders(jsonData *simplejson.Json, decryptedJsonData map[string]string) map[string]string {
@ -243,6 +259,10 @@ func (s *Service) parseMetricRequest(ctx context.Context, user *models.SignedInU
} }
} }
if reqDTO.HTTPRequest != nil {
req.httpRequest = reqDTO.HTTPRequest
}
return req, nil return req, nil
} }

View File

@ -61,6 +61,43 @@ func TestQueryData(t *testing.T) {
} }
require.Equal(t, expected, tc.pluginContext.req.Headers) require.Equal(t, expected, tc.pluginContext.req.Headers)
}) })
t.Run("it doesn't add cookie header to the request when keepCookies configured and no cookies provided", func(t *testing.T) {
tc := setup(t)
json, err := simplejson.NewJson([]byte(`{"keepCookies": [ "foo", "bar" ]}`))
require.NoError(t, err)
tc.dataSourceCache.ds.JsonData = json
metricReq := metricRequest()
httpReq, err := http.NewRequest(http.MethodGet, "/", nil)
require.NoError(t, err)
metricReq.HTTPRequest = httpReq
_, err = tc.queryService.QueryData(context.Background(), nil, true, metricReq, false)
require.NoError(t, err)
require.Empty(t, tc.pluginContext.req.Headers)
})
t.Run("it adds cookie header to the request when keepCookies configured and cookie provided", func(t *testing.T) {
tc := setup(t)
json, err := simplejson.NewJson([]byte(`{"keepCookies": [ "foo", "bar" ]}`))
require.NoError(t, err)
tc.dataSourceCache.ds.JsonData = json
metricReq := metricRequest()
httpReq, err := http.NewRequest(http.MethodGet, "/", nil)
require.NoError(t, err)
httpReq.AddCookie(&http.Cookie{Name: "a"})
httpReq.AddCookie(&http.Cookie{Name: "bar", Value: "rab"})
httpReq.AddCookie(&http.Cookie{Name: "b"})
httpReq.AddCookie(&http.Cookie{Name: "foo", Value: "oof"})
httpReq.AddCookie(&http.Cookie{Name: "c"})
metricReq.HTTPRequest = httpReq
_, err = tc.queryService.QueryData(context.Background(), nil, true, metricReq, false)
require.NoError(t, err)
require.Equal(t, map[string]string{"Cookie": "bar=rab; foo=oof"}, tc.pluginContext.req.Headers)
})
} }
func setup(t *testing.T) *testContext { func setup(t *testing.T) *testContext {