소스 검색

Pass configured/auth headers to a Datasource.

In some setups (ex openshift), the Datasource will require Grafana
to pass oauth token as header when sending queries.
Also, this PR allow to send any header which is something
Grafana currently does not support.
mrsiano 7 년 전
부모
커밋
cc1e3a0e3c
4개의 변경된 파일63개의 추가작업 그리고 0개의 파일을 삭제
  1. 3 0
      conf/provisioning/datasources/sample.yaml
  2. 27 0
      pkg/api/pluginproxy/ds_proxy.go
  3. 32 0
      pkg/api/pluginproxy/ds_proxy_test.go
  4. 1 0
      public/app/features/plugins/ds_edit_ctrl.ts

+ 3 - 0
conf/provisioning/datasources/sample.yaml

@@ -40,11 +40,14 @@ apiVersion: 1
 #      graphiteVersion: "1.1"
 #      tlsAuth: true
 #      tlsAuthWithCACert: true
+#      httpHeaderName1: "Authorization"
 #   # <string> json object of data that will be encrypted.
 #   secureJsonData:
 #     tlsCACert: "..."
 #     tlsClientCert: "..."
 #     tlsClientKey: "..."
+#     # <openshift\kubernetes token example>
+#     httpHeaderValue1: "Bearer xf5yhfkpsnmgo"
 #   version: 1
 #   # <bool> allow users to edit datasources from the UI.
 #   editable: false

+ 27 - 0
pkg/api/pluginproxy/ds_proxy.go

@@ -109,6 +109,28 @@ func (proxy *DataSourceProxy) addTraceFromHeaderValue(span opentracing.Span, hea
 	}
 }
 
+func (proxy *DataSourceProxy) useCustomHeaders(req *http.Request) {
+	decryptSdj := proxy.ds.SecureJsonData.Decrypt()
+	index := 1
+	for {
+		headerNameSuffix := fmt.Sprintf("httpHeaderName%d", index)
+		headerValueSuffix := fmt.Sprintf("httpHeaderValue%d", index)
+		if key := proxy.ds.JsonData.Get(headerNameSuffix).MustString(); key != "" {
+			if val, ok := decryptSdj[headerValueSuffix]; ok {
+				// remove if exists
+				if req.Header.Get(key) != "" {
+					req.Header.Del(key)
+				}
+				req.Header.Add(key, val)
+				logger.Debug("Using custom header ", "CustomHeaders", key)
+			}
+		} else {
+			break
+		}
+		index += 1
+	}
+}
+
 func (proxy *DataSourceProxy) getDirector() func(req *http.Request) {
 	return func(req *http.Request) {
 		req.URL.Scheme = proxy.targetUrl.Scheme
@@ -138,6 +160,11 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) {
 			req.Header.Add("Authorization", util.GetBasicAuthHeader(proxy.ds.BasicAuthUser, proxy.ds.BasicAuthPassword))
 		}
 
+		// Lookup and use custom headers
+		if proxy.ds.SecureJsonData != nil {
+			proxy.useCustomHeaders(req)
+		}
+
 		dsAuth := req.Header.Get("X-DS-Authorization")
 		if len(dsAuth) > 0 {
 			req.Header.Del("X-DS-Authorization")

+ 32 - 0
pkg/api/pluginproxy/ds_proxy_test.go

@@ -8,6 +8,7 @@ import (
 	macaron "gopkg.in/macaron.v1"
 
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	"github.com/grafana/grafana/pkg/log"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
@@ -212,5 +213,36 @@ func TestDSRouteRule(t *testing.T) {
 			So(interpolated, ShouldEqual, "0asd+asd")
 		})
 
+		Convey("When proxying a data source with custom headers specified", func() {
+			plugin := &plugins.DataSourcePlugin{}
+
+			encryptedData, err := util.Encrypt([]byte(`Bearer xf5yhfkpsnmgo`), setting.SecretKey)
+			ds := &m.DataSource{
+				Type: m.DS_PROMETHEUS,
+				Url:  "http://prometheus:9090",
+				JsonData: simplejson.NewFromAny(map[string]interface{}{
+					"httpHeaderName1": "Authorization",
+				}),
+				SecureJsonData: map[string][]byte{
+					"httpHeaderValue1": encryptedData,
+				},
+			}
+
+			ctx := &m.ReqContext{}
+			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
+
+			requestURL, _ := url.Parse("http://grafana.com/sub")
+			req := http.Request{URL: requestURL, Header: make(http.Header)}
+			proxy.getDirector()(&req)
+
+			if err != nil {
+				log.Fatal(4, err.Error())
+			}
+
+			Convey("Match header value after decryption", func() {
+				So(req.Header.Get("Authorization"), ShouldEqual, "Bearer xf5yhfkpsnmgo")
+			})
+		})
+
 	})
 }

+ 1 - 0
public/app/features/plugins/ds_edit_ctrl.ts

@@ -13,6 +13,7 @@ var defaults = {
   access: 'proxy',
   jsonData: {},
   secureJsonFields: {},
+  secureJsonData: {},
 };
 
 var datasourceCreated = false;