mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 13:42:09 +08:00
Plugins: Automatically forward plugin request HTTP headers in outgoing HTTP requests (#60417)
Automatically forward core plugin request HTTP headers in outgoing HTTP requests. Core datasource plugin authors don't have to specifically handle forwarding of HTTP headers, e.g. do not have to "hardcode" the header-names in the datasource plugin, if not having custom needs. Fixes #57065
This commit is contained in:

committed by
GitHub

parent
aaab477594
commit
c35c689a96
@ -26,92 +26,226 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const loginCookieName = "grafana_session"
|
||||
|
||||
func TestIntegrationBackendPlugins(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
regularQuery := func(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
|
||||
t.Helper()
|
||||
|
||||
return metricRequestWithQueries(t, fmt.Sprintf(`{
|
||||
"datasource": {
|
||||
"uid": "%s"
|
||||
}
|
||||
}`, tsCtx.uid))
|
||||
oauthToken := &oauth2.Token{
|
||||
TokenType: "bearer",
|
||||
AccessToken: "access-token",
|
||||
RefreshToken: "refresh-token",
|
||||
Expiry: time.Now().UTC().Add(24 * time.Hour),
|
||||
}
|
||||
oauthToken = oauthToken.WithExtra(map[string]interface{}{"id_token": "id-token"})
|
||||
|
||||
expressionQuery := func(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
|
||||
t.Helper()
|
||||
newTestScenario(t, "Datasource with no custom HTTP settings",
|
||||
options(
|
||||
withIncomingRequest(func(req *http.Request) {
|
||||
req.Header.Set("X-Custom", "custom")
|
||||
req.AddCookie(&http.Cookie{Name: "cookie1"})
|
||||
req.AddCookie(&http.Cookie{Name: "cookie2"})
|
||||
req.AddCookie(&http.Cookie{Name: "cookie3"})
|
||||
req.AddCookie(&http.Cookie{Name: loginCookieName})
|
||||
}),
|
||||
),
|
||||
func(t *testing.T, tsCtx *testScenarioContext) {
|
||||
verify := func(h backend.ForwardHTTPHeaders) {
|
||||
require.NotNil(t, h)
|
||||
require.Empty(t, h.GetHTTPHeader(backend.CookiesHeaderName))
|
||||
require.Empty(t, h.GetHTTPHeader("Authorization"))
|
||||
|
||||
return metricRequestWithQueries(t, fmt.Sprintf(`{
|
||||
"refId": "A",
|
||||
"datasource": {
|
||||
"uid": "%s",
|
||||
"type": "%s"
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.NotEmpty(t, tsCtx.outgoingRequest.Header)
|
||||
}
|
||||
}`, tsCtx.uid, tsCtx.testPluginID), `{
|
||||
"refId": "B",
|
||||
"datasource": {
|
||||
"type": "__expr__",
|
||||
"uid": "__expr__",
|
||||
"name": "Expression"
|
||||
},
|
||||
"type": "math",
|
||||
"expression": "$A - 50"
|
||||
}`)
|
||||
}
|
||||
|
||||
newTestScenario(t, "When oauth token not available", func(t *testing.T, tsCtx *testScenarioContext) {
|
||||
tsCtx.testEnv.OAuthTokenService.Token = nil
|
||||
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
|
||||
verify(pReq)
|
||||
})
|
||||
|
||||
tsCtx.runCheckHealthTest(t)
|
||||
tsCtx.runCallResourceTest(t)
|
||||
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
|
||||
verify(pReq)
|
||||
require.Equal(t, "custom", pReq.GetHTTPHeader("X-Custom"))
|
||||
require.Equal(t, "custom", tsCtx.outgoingRequest.Header.Get("X-Custom"))
|
||||
})
|
||||
|
||||
t.Run("regular query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, regularQuery(t, tsCtx))
|
||||
verifyQueryData := func(pReq *backend.QueryDataRequest) {
|
||||
verify(pReq)
|
||||
}
|
||||
|
||||
t.Run("regular query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
|
||||
|
||||
t.Run("expression query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("expression query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, expressionQuery(t, tsCtx))
|
||||
})
|
||||
})
|
||||
newTestScenario(t, "Datasource with most HTTP settings set except oauthPassThru and oauth token available",
|
||||
options(
|
||||
withIncomingRequest(func(req *http.Request) {
|
||||
req.AddCookie(&http.Cookie{Name: "cookie1"})
|
||||
req.AddCookie(&http.Cookie{Name: "cookie2"})
|
||||
req.AddCookie(&http.Cookie{Name: "cookie3"})
|
||||
req.AddCookie(&http.Cookie{Name: loginCookieName})
|
||||
}),
|
||||
withOAuthToken(oauthToken),
|
||||
withDsBasicAuth("basicAuthUser", "basicAuthPassword"),
|
||||
withDsCustomHeader(map[string]string{"X-CUSTOM-HEADER": "custom-header-value"}),
|
||||
withDsCookieForwarding([]string{"cookie1", "cookie3", loginCookieName}),
|
||||
),
|
||||
func(t *testing.T, tsCtx *testScenarioContext) {
|
||||
verify := func(h backend.ForwardHTTPHeaders) {
|
||||
require.NotNil(t, h)
|
||||
require.Equal(t, "cookie1=; cookie3=", h.GetHTTPHeader(backend.CookiesHeaderName))
|
||||
require.Empty(t, h.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
|
||||
require.Empty(t, h.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName))
|
||||
|
||||
newTestScenario(t, "When oauth token available", func(t *testing.T, tsCtx *testScenarioContext) {
|
||||
token := &oauth2.Token{
|
||||
TokenType: "bearer",
|
||||
AccessToken: "access-token",
|
||||
RefreshToken: "refresh-token",
|
||||
Expiry: time.Now().UTC().Add(24 * time.Hour),
|
||||
}
|
||||
token = token.WithExtra(map[string]interface{}{"id_token": "id-token"})
|
||||
tsCtx.testEnv.OAuthTokenService.Token = token
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.Equal(t, "cookie1=; cookie3=", tsCtx.outgoingRequest.Header.Get(backend.CookiesHeaderName))
|
||||
require.Equal(t, "custom-header-value", tsCtx.outgoingRequest.Header.Get("X-CUSTOM-HEADER"))
|
||||
|
||||
tsCtx.runCheckHealthTest(t)
|
||||
tsCtx.runCallResourceTest(t)
|
||||
username, pwd, ok := tsCtx.outgoingRequest.BasicAuth()
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "basicAuthUser", username)
|
||||
require.Equal(t, "basicAuthPassword", pwd)
|
||||
}
|
||||
|
||||
t.Run("regular query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, regularQuery(t, tsCtx))
|
||||
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
|
||||
verify(pReq)
|
||||
})
|
||||
|
||||
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
|
||||
verify(pReq)
|
||||
})
|
||||
|
||||
verifyQueryData := func(pReq *backend.QueryDataRequest) {
|
||||
verify(pReq)
|
||||
}
|
||||
|
||||
t.Run("regular query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
|
||||
|
||||
t.Run("expression query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("expression query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, expressionQuery(t, tsCtx))
|
||||
newTestScenario(t, "Datasource with oauthPassThru and basic auth configured and oauth token available",
|
||||
options(
|
||||
withOAuthToken(oauthToken),
|
||||
withDsOAuthForwarding(),
|
||||
withDsBasicAuth("basicAuthUser", "basicAuthPassword"),
|
||||
),
|
||||
func(t *testing.T, tsCtx *testScenarioContext) {
|
||||
verify := func(h backend.ForwardHTTPHeaders) {
|
||||
require.NotNil(t, h)
|
||||
|
||||
expectedAuthHeader := fmt.Sprintf("Bearer %s", oauthToken.AccessToken)
|
||||
expectedTokenHeader := oauthToken.Extra("id_token").(string)
|
||||
|
||||
require.Equal(t, expectedAuthHeader, h.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
|
||||
require.Equal(t, expectedTokenHeader, h.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName))
|
||||
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.Equal(t, expectedAuthHeader, tsCtx.outgoingRequest.Header.Get(backend.OAuthIdentityTokenHeaderName))
|
||||
require.Equal(t, expectedTokenHeader, tsCtx.outgoingRequest.Header.Get(backend.OAuthIdentityIDTokenHeaderName))
|
||||
}
|
||||
|
||||
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
|
||||
verify(pReq)
|
||||
})
|
||||
|
||||
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
|
||||
verify(pReq)
|
||||
})
|
||||
|
||||
verifyQueryData := func(pReq *backend.QueryDataRequest) {
|
||||
verify(pReq)
|
||||
}
|
||||
|
||||
t.Run("regular query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
|
||||
|
||||
t.Run("expression query", func(t *testing.T) {
|
||||
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type testScenarioContext struct {
|
||||
testPluginID string
|
||||
uid string
|
||||
grafanaListeningAddr string
|
||||
testEnv *server.TestEnv
|
||||
outgoingServer *httptest.Server
|
||||
outgoingRequest *http.Request
|
||||
backendTestPlugin *testPlugin
|
||||
rt http.RoundTripper
|
||||
testPluginID string
|
||||
uid string
|
||||
grafanaListeningAddr string
|
||||
testEnv *server.TestEnv
|
||||
outgoingServer *httptest.Server
|
||||
outgoingRequest *http.Request
|
||||
backendTestPlugin *testPlugin
|
||||
rt http.RoundTripper
|
||||
modifyIncomingRequest func(req *http.Request)
|
||||
}
|
||||
|
||||
func newTestScenario(t *testing.T, name string, callback func(t *testing.T, ctx *testScenarioContext)) {
|
||||
type testScenarioInput struct {
|
||||
ds *datasources.AddDataSourceCommand
|
||||
token *oauth2.Token
|
||||
modifyIncomingRequest func(req *http.Request)
|
||||
}
|
||||
|
||||
type testScenarioOption func(*testScenarioInput)
|
||||
|
||||
func options(opts ...testScenarioOption) []testScenarioOption {
|
||||
return opts
|
||||
}
|
||||
|
||||
func withIncomingRequest(cb func(req *http.Request)) testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
in.modifyIncomingRequest = cb
|
||||
}
|
||||
}
|
||||
|
||||
func withOAuthToken(token *oauth2.Token) testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
in.token = token
|
||||
}
|
||||
}
|
||||
|
||||
func withDsBasicAuth(username, password string) testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
in.ds.BasicAuth = true
|
||||
in.ds.BasicAuthUser = username
|
||||
in.ds.SecureJsonData["basicAuthPassword"] = password
|
||||
}
|
||||
}
|
||||
|
||||
func withDsCustomHeader(headers map[string]string) testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
index := 1
|
||||
for k, v := range headers {
|
||||
in.ds.JsonData.Set(fmt.Sprintf("httpHeaderName%d", index), k)
|
||||
in.ds.SecureJsonData[fmt.Sprintf("httpHeaderValue%d", index)] = v
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withDsOAuthForwarding() testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
in.ds.JsonData.Set("oauthPassThru", true)
|
||||
}
|
||||
}
|
||||
|
||||
func withDsCookieForwarding(names []string) testScenarioOption {
|
||||
return func(in *testScenarioInput) {
|
||||
in.ds.JsonData.Set("keepCookies", names)
|
||||
}
|
||||
}
|
||||
|
||||
func newTestScenario(t *testing.T, name string, opts []testScenarioOption, callback func(t *testing.T, ctx *testScenarioContext)) {
|
||||
tsCtx := testScenarioContext{
|
||||
testPluginID: "test-plugin",
|
||||
}
|
||||
@ -123,6 +257,7 @@ func newTestScenario(t *testing.T, name string, callback func(t *testing.T, ctx
|
||||
|
||||
grafanaListeningAddr, testEnv := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
tsCtx.grafanaListeningAddr = grafanaListeningAddr
|
||||
testEnv.SQLStore.Cfg.LoginCookieName = loginCookieName
|
||||
tsCtx.testEnv = testEnv
|
||||
ctx := context.Background()
|
||||
|
||||
@ -143,29 +278,30 @@ func newTestScenario(t *testing.T, name string, callback func(t *testing.T, ctx
|
||||
err := testEnv.PluginRegistry.Add(ctx, testPlugin)
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonData := simplejson.NewFromAny(map[string]interface{}{
|
||||
"httpHeaderName1": "X-CUSTOM-HEADER",
|
||||
"oauthPassThru": true,
|
||||
"keepCookies": []string{"cookie1", "cookie3", "grafana_session"},
|
||||
})
|
||||
secureJSONData := map[string]string{
|
||||
"basicAuthPassword": "basicAuthPassword",
|
||||
"httpHeaderValue1": "custom-header-value",
|
||||
}
|
||||
jsonData := simplejson.New()
|
||||
secureJSONData := map[string]string{}
|
||||
|
||||
tsCtx.uid = "test-plugin"
|
||||
err = testEnv.Server.HTTPServer.DataSourcesService.AddDataSource(ctx, &datasources.AddDataSourceCommand{
|
||||
cmd := &datasources.AddDataSourceCommand{
|
||||
OrgId: 1,
|
||||
Access: datasources.DS_ACCESS_PROXY,
|
||||
Name: "TestPlugin",
|
||||
Type: tsCtx.testPluginID,
|
||||
Uid: tsCtx.uid,
|
||||
Url: tsCtx.outgoingServer.URL,
|
||||
BasicAuth: true,
|
||||
BasicAuthUser: "basicAuthUser",
|
||||
JsonData: jsonData,
|
||||
SecureJsonData: secureJSONData,
|
||||
})
|
||||
}
|
||||
|
||||
in := &testScenarioInput{ds: cmd}
|
||||
for _, opt := range opts {
|
||||
opt(in)
|
||||
}
|
||||
|
||||
tsCtx.modifyIncomingRequest = in.modifyIncomingRequest
|
||||
tsCtx.testEnv.OAuthTokenService.Token = in.token
|
||||
|
||||
err = testEnv.Server.HTTPServer.DataSourcesService.AddDataSource(ctx, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
getDataSourceQuery := &datasources.GetDataSourceQuery{
|
||||
@ -185,17 +321,42 @@ func newTestScenario(t *testing.T, name string, callback func(t *testing.T, ctx
|
||||
})
|
||||
}
|
||||
|
||||
func (tsCtx *testScenarioContext) runQueryDataTest(t *testing.T, mr dtos.MetricRequest) {
|
||||
t.Run("When calling /api/ds/query should set expected headers on outgoing QueryData and HTTP request", func(t *testing.T) {
|
||||
var received *struct {
|
||||
ctx context.Context
|
||||
req *backend.QueryDataRequest
|
||||
func createRegularQuery(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
|
||||
t.Helper()
|
||||
|
||||
return metricRequestWithQueries(t, fmt.Sprintf(`{
|
||||
"datasource": {
|
||||
"uid": "%s"
|
||||
}
|
||||
}`, tsCtx.uid))
|
||||
}
|
||||
|
||||
func createExpressionQuery(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
|
||||
t.Helper()
|
||||
|
||||
return metricRequestWithQueries(t, fmt.Sprintf(`{
|
||||
"refId": "A",
|
||||
"datasource": {
|
||||
"uid": "%s",
|
||||
"type": "%s"
|
||||
}
|
||||
}`, tsCtx.uid, tsCtx.testPluginID), `{
|
||||
"refId": "B",
|
||||
"datasource": {
|
||||
"type": "__expr__",
|
||||
"uid": "__expr__",
|
||||
"name": "Expression"
|
||||
},
|
||||
"type": "math",
|
||||
"expression": "$A - 50"
|
||||
}`)
|
||||
}
|
||||
|
||||
func (tsCtx *testScenarioContext) runQueryDataTest(t *testing.T, mr dtos.MetricRequest, callback func(req *backend.QueryDataRequest)) {
|
||||
t.Run("When calling /api/ds/query should set expected headers on outgoing QueryData and HTTP request", func(t *testing.T) {
|
||||
var received *backend.QueryDataRequest
|
||||
tsCtx.backendTestPlugin.QueryDataHandler = backend.QueryDataHandlerFunc(func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
received = &struct {
|
||||
ctx context.Context
|
||||
req *backend.QueryDataRequest
|
||||
}{ctx, req}
|
||||
received = req
|
||||
|
||||
c := http.Client{
|
||||
Transport: tsCtx.rt,
|
||||
@ -227,18 +388,11 @@ func (tsCtx *testScenarioContext) runQueryDataTest(t *testing.T, mr dtos.MetricR
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, u, buf1)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie1",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie2",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie3",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "grafana_session",
|
||||
})
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
|
||||
|
||||
if tsCtx.modifyIncomingRequest != nil {
|
||||
tsCtx.modifyIncomingRequest(req)
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
@ -253,51 +407,18 @@ func (tsCtx *testScenarioContext) runQueryDataTest(t *testing.T, mr dtos.MetricR
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
// backend query data request
|
||||
require.NotNil(t, received)
|
||||
require.Equal(t, "cookie1=; cookie3=", received.req.Headers["Cookie"])
|
||||
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
|
||||
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
|
||||
|
||||
token := tsCtx.testEnv.OAuthTokenService.Token
|
||||
|
||||
var expectedAuthHeader string
|
||||
var expectedTokenHeader string
|
||||
|
||||
if token != nil {
|
||||
expectedAuthHeader = fmt.Sprintf("Bearer %s", token.AccessToken)
|
||||
expectedTokenHeader = token.Extra("id_token").(string)
|
||||
|
||||
require.Equal(t, expectedAuthHeader, received.req.Headers["Authorization"])
|
||||
require.Equal(t, expectedTokenHeader, received.req.Headers["X-ID-Token"])
|
||||
}
|
||||
|
||||
// outgoing HTTP request
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.Equal(t, "cookie1=; cookie3=", tsCtx.outgoingRequest.Header.Get("Cookie"))
|
||||
require.Equal(t, "custom-header-value", tsCtx.outgoingRequest.Header.Get("X-CUSTOM-HEADER"))
|
||||
|
||||
if token == nil {
|
||||
username, pwd, ok := tsCtx.outgoingRequest.BasicAuth()
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "basicAuthUser", username)
|
||||
require.Equal(t, "basicAuthPassword", pwd)
|
||||
} else {
|
||||
require.Equal(t, expectedAuthHeader, tsCtx.outgoingRequest.Header.Get("Authorization"))
|
||||
require.Equal(t, expectedTokenHeader, tsCtx.outgoingRequest.Header.Get("X-ID-Token"))
|
||||
}
|
||||
callback(received)
|
||||
})
|
||||
}
|
||||
|
||||
func (tsCtx *testScenarioContext) runCheckHealthTest(t *testing.T) {
|
||||
func (tsCtx *testScenarioContext) runCheckHealthTest(t *testing.T, callback func(req *backend.CheckHealthRequest)) {
|
||||
t.Run("When calling /api/datasources/uid/:uid/health should set expected headers on outgoing CheckHealth and HTTP request", func(t *testing.T) {
|
||||
var received *struct {
|
||||
ctx context.Context
|
||||
req *backend.CheckHealthRequest
|
||||
}
|
||||
var received *backend.CheckHealthRequest
|
||||
tsCtx.backendTestPlugin.CheckHealthHandler = backend.CheckHealthHandlerFunc(func(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
received = &struct {
|
||||
ctx context.Context
|
||||
req *backend.CheckHealthRequest
|
||||
}{ctx, req}
|
||||
received = req
|
||||
|
||||
c := http.Client{
|
||||
Transport: tsCtx.rt,
|
||||
@ -328,18 +449,11 @@ func (tsCtx *testScenarioContext) runCheckHealthTest(t *testing.T) {
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie1",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie2",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie3",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "grafana_session",
|
||||
})
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
|
||||
|
||||
if tsCtx.modifyIncomingRequest != nil {
|
||||
tsCtx.modifyIncomingRequest(req)
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
@ -354,51 +468,18 @@ func (tsCtx *testScenarioContext) runCheckHealthTest(t *testing.T) {
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
// backend query data request
|
||||
require.NotNil(t, received)
|
||||
require.Equal(t, "cookie1=; cookie3=", received.req.Headers["Cookie"])
|
||||
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
|
||||
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
|
||||
|
||||
token := tsCtx.testEnv.OAuthTokenService.Token
|
||||
|
||||
var expectedAuthHeader string
|
||||
var expectedTokenHeader string
|
||||
|
||||
if token != nil {
|
||||
expectedAuthHeader = fmt.Sprintf("Bearer %s", token.AccessToken)
|
||||
expectedTokenHeader = token.Extra("id_token").(string)
|
||||
|
||||
require.Equal(t, expectedAuthHeader, received.req.Headers["Authorization"])
|
||||
require.Equal(t, expectedTokenHeader, received.req.Headers["X-ID-Token"])
|
||||
}
|
||||
|
||||
// outgoing HTTP request
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.Equal(t, "cookie1=; cookie3=", tsCtx.outgoingRequest.Header.Get("Cookie"))
|
||||
require.Equal(t, "custom-header-value", tsCtx.outgoingRequest.Header.Get("X-CUSTOM-HEADER"))
|
||||
|
||||
if token == nil {
|
||||
username, pwd, ok := tsCtx.outgoingRequest.BasicAuth()
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "basicAuthUser", username)
|
||||
require.Equal(t, "basicAuthPassword", pwd)
|
||||
} else {
|
||||
require.Equal(t, expectedAuthHeader, tsCtx.outgoingRequest.Header.Get("Authorization"))
|
||||
require.Equal(t, expectedTokenHeader, tsCtx.outgoingRequest.Header.Get("X-ID-Token"))
|
||||
}
|
||||
callback(received)
|
||||
})
|
||||
}
|
||||
|
||||
func (tsCtx *testScenarioContext) runCallResourceTest(t *testing.T) {
|
||||
func (tsCtx *testScenarioContext) runCallResourceTest(t *testing.T, callback func(req *backend.CallResourceRequest)) {
|
||||
t.Run("When calling /api/datasources/uid/:uid/resources should set expected headers on outgoing CallResource and HTTP request", func(t *testing.T) {
|
||||
var received *struct {
|
||||
ctx context.Context
|
||||
req *backend.CallResourceRequest
|
||||
}
|
||||
var received *backend.CallResourceRequest
|
||||
tsCtx.backendTestPlugin.CallResourceHandler = backend.CallResourceHandlerFunc(func(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
received = &struct {
|
||||
ctx context.Context
|
||||
req *backend.CallResourceRequest
|
||||
}{ctx, req}
|
||||
received = req
|
||||
|
||||
c := http.Client{
|
||||
Transport: tsCtx.rt,
|
||||
@ -450,19 +531,11 @@ func (tsCtx *testScenarioContext) runCallResourceTest(t *testing.T) {
|
||||
req.Header.Set("Connection", "X-Some-Conn-Header")
|
||||
req.Header.Set("X-Some-Conn-Header", "should be deleted")
|
||||
req.Header.Set("Proxy-Connection", "should be deleted")
|
||||
req.Header.Set("X-Custom", "custom")
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie1",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie2",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "cookie3",
|
||||
})
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "grafana_session",
|
||||
})
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
|
||||
|
||||
if tsCtx.modifyIncomingRequest != nil {
|
||||
tsCtx.modifyIncomingRequest(req)
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
@ -484,45 +557,18 @@ func (tsCtx *testScenarioContext) runCallResourceTest(t *testing.T) {
|
||||
require.Empty(t, resp.Header.Get("Set-Cookie"))
|
||||
require.Equal(t, "should not be deleted", resp.Header.Get("X-Custom"))
|
||||
|
||||
// backend query data request
|
||||
require.NotNil(t, received)
|
||||
require.Equal(t, "cookie1=; cookie3=", received.req.Headers["Cookie"][0])
|
||||
require.Empty(t, received.req.Headers["Connection"])
|
||||
require.Empty(t, received.req.Headers["X-Some-Conn-Header"])
|
||||
require.Empty(t, received.req.Headers["Proxy-Connection"])
|
||||
require.Equal(t, "custom", received.req.Headers["X-Custom"][0])
|
||||
require.Empty(t, received.Headers["Connection"])
|
||||
require.Empty(t, received.Headers["X-Some-Conn-Header"])
|
||||
require.Empty(t, received.Headers["Proxy-Connection"])
|
||||
|
||||
token := tsCtx.testEnv.OAuthTokenService.Token
|
||||
|
||||
var expectedAuthHeader string
|
||||
var expectedTokenHeader string
|
||||
|
||||
if token != nil {
|
||||
expectedAuthHeader = fmt.Sprintf("Bearer %s", token.AccessToken)
|
||||
expectedTokenHeader = token.Extra("id_token").(string)
|
||||
|
||||
require.Equal(t, expectedAuthHeader, received.req.Headers["Authorization"][0])
|
||||
require.Equal(t, expectedTokenHeader, received.req.Headers["X-ID-Token"][0])
|
||||
}
|
||||
|
||||
// outgoing HTTP request
|
||||
require.NotNil(t, tsCtx.outgoingRequest)
|
||||
require.Equal(t, "cookie1=; cookie3=", tsCtx.outgoingRequest.Header.Get("Cookie"))
|
||||
require.Empty(t, tsCtx.outgoingRequest.Header.Get("Connection"))
|
||||
require.Empty(t, tsCtx.outgoingRequest.Header.Get("X-Some-Conn-Header"))
|
||||
require.Empty(t, tsCtx.outgoingRequest.Header.Get("Proxy-Connection"))
|
||||
require.Equal(t, "custom", tsCtx.outgoingRequest.Header.Get("X-Custom"))
|
||||
require.Equal(t, "custom-header-value", tsCtx.outgoingRequest.Header.Get("X-CUSTOM-HEADER"))
|
||||
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
|
||||
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
|
||||
|
||||
if token == nil {
|
||||
username, pwd, ok := tsCtx.outgoingRequest.BasicAuth()
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "basicAuthUser", username)
|
||||
require.Equal(t, "basicAuthPassword", pwd)
|
||||
} else {
|
||||
require.Equal(t, expectedAuthHeader, tsCtx.outgoingRequest.Header.Get("Authorization"))
|
||||
require.Equal(t, expectedTokenHeader, tsCtx.outgoingRequest.Header.Get("X-ID-Token"))
|
||||
}
|
||||
callback(received)
|
||||
})
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user