Plugins: Support headers field for check health (#49930)

pull/49916/head^2
Marcus Efraimsson 3 years ago committed by GitHub
parent a91ecc566b
commit a7813275a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      docs/sources/developers/plugins/add-authentication-for-data-source-plugins.md
  2. 2
      go.mod
  3. 4
      go.sum
  4. 17
      pkg/api/datasources.go
  5. 9
      pkg/api/pluginproxy/ds_proxy.go
  6. 1
      pkg/api/plugins.go
  7. 12
      pkg/models/datasource.go
  8. 2
      pkg/plugins/backendplugin/grpcplugin/client_v2.go
  9. 10
      pkg/services/query/query.go

@ -293,6 +293,17 @@ To allow Grafana to pass the access token to the plugin, update the data source
When configured, Grafana will pass the user's token to the plugin in an Authorization header, available on the `QueryDataRequest` object on the `QueryData` request in your backend data source.
```go
func (ds *dataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
token := strings.Fields(req.Headers["Authorization"])
var (
tokenType = token[0]
accessToken = token[1]
)
// ...
return &backend.CheckHealthResult{Status: backend.HealthStatusOk}, nil
}
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
token := strings.Fields(req.Headers["Authorization"])
var (
@ -309,6 +320,13 @@ func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataReque
In addition, if the user's token includes an ID token, Grafana will pass the user's ID token to the plugin in an `X-ID-Token` header, available on the `QueryDataRequest` object on the `QueryData` request in your backend data source.
```go
func (ds *dataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
idToken := req.Headers["X-ID-Token"]
// ...
return &backend.CheckHealthResult{Status: backend.HealthStatusOk}, nil
}
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
idToken := req.Headers["X-ID-Token"]

@ -56,7 +56,7 @@ require (
github.com/grafana/cuetsy v0.0.2
github.com/grafana/grafana-aws-sdk v0.10.3
github.com/grafana/grafana-azure-sdk-go v1.2.0
github.com/grafana/grafana-plugin-sdk-go v0.134.0
github.com/grafana/grafana-plugin-sdk-go v0.135.0
github.com/grafana/loki v1.6.2-0.20211015002020-7832783b1caa
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/hashicorp/go-hclog v1.0.0

@ -1461,8 +1461,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.94.0/go.mod h1:3VXz4nCv6wH5SfgB3mlW3
github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
github.com/grafana/grafana-plugin-sdk-go v0.125.0/go.mod h1:9YiJ5GUxIsIEUC0qR9+BJVP5M7mCSP6uc6Ne62YKkgc=
github.com/grafana/grafana-plugin-sdk-go v0.129.0/go.mod h1:4edtosZepfQF9jkQwRywJsNSJzXTHmzbmcVcAl8MEQc=
github.com/grafana/grafana-plugin-sdk-go v0.134.0 h1:8j8vsvhU3GabRPWB1EnFKSWt60yQVFPEE/5P1QV2gFw=
github.com/grafana/grafana-plugin-sdk-go v0.134.0/go.mod h1:jmrxelOJKrIK0yrsIzcotS8pbqPZozbmJgGy7k3hK1k=
github.com/grafana/grafana-plugin-sdk-go v0.135.0 h1:IQrwA/RCPr5IhE3lVxIpgEXwZohx7gp3rJ1KJa0KT5g=
github.com/grafana/grafana-plugin-sdk-go v0.135.0/go.mod h1:jmrxelOJKrIK0yrsIzcotS8pbqPZozbmJgGy7k3hK1k=
github.com/grafana/loki v1.6.2-0.20211015002020-7832783b1caa h1:+pXjAxavVR2FKKNsuuCXGCWEj8XGc1Af6SPiyBpzU2A=
github.com/grafana/loki v1.6.2-0.20211015002020-7832783b1caa/go.mod h1:0O8o/juxNSKN/e+DzWDTRkl7Zm8CkZcz0NDqEdojlrk=
github.com/grafana/saml v0.0.0-20211007135653-aed1b2edd86b h1:YiSGp34F4V0G08HHx1cJBf2GVgwYAkXQjzuVs1t8jYk=

@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/permissions"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana/pkg/web"
)
@ -568,6 +569,7 @@ func (hs *HTTPServer) checkDatasourceHealth(c *models.ReqContext, ds *models.Dat
PluginID: plugin.ID,
DataSourceInstanceSettings: dsInstanceSettings,
},
Headers: map[string]string{},
}
var dsURL string
@ -580,6 +582,21 @@ func (hs *HTTPServer) checkDatasourceHealth(c *models.ReqContext, ds *models.Dat
return response.Error(http.StatusForbidden, "Access denied", err)
}
if hs.DataProxy.OAuthTokenService.IsOAuthPassThruEnabled(ds) {
if token := hs.DataProxy.OAuthTokenService.GetCurrentOAuthToken(c.Req.Context(), c.SignedInUser); token != nil {
req.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken)
idToken, ok := token.Extra("id_token").(string)
if ok && idToken != "" {
req.Headers["X-ID-Token"] = idToken
}
}
}
proxyutil.ClearCookieHeader(c.Req, ds.AllowedCookies())
if cookieStr := c.Req.Header.Get("Cookie"); cookieStr != "" {
req.Headers["Cookie"] = cookieStr
}
resp, err := hs.pluginClient.CheckHealth(c.Req.Context(), req)
if err != nil {
return translatePluginRequestErrorToAPIError(err)

@ -222,14 +222,7 @@ func (proxy *DataSourceProxy) director(req *http.Request) {
applyUserHeader(proxy.cfg.SendUserHeader, req, proxy.ctx.SignedInUser)
keepCookieNames := []string{}
if proxy.ds.JsonData != nil {
if keepCookies := proxy.ds.JsonData.Get("keepCookies"); keepCookies != nil {
keepCookieNames = keepCookies.MustStringArray()
}
}
proxyutil.ClearCookieHeader(req, keepCookieNames)
proxyutil.ClearCookieHeader(req, proxy.ds.AllowedCookies())
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
jsonData := make(map[string]interface{})

@ -318,6 +318,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) response.Response {
resp, err := hs.pluginClient.CheckHealth(c.Req.Context(), &backend.CheckHealthRequest{
PluginContext: pCtx,
Headers: map[string]string{},
})
if err != nil {
return translatePluginRequestErrorToAPIError(err)

@ -67,6 +67,18 @@ type DataSource struct {
Updated time.Time `json:"updated"`
}
// AllowedCookies parses the jsondata.keepCookies and returns a list of
// allowed cookies, otherwise an empty list.
func (ds DataSource) AllowedCookies() []string {
if ds.JsonData != nil {
if keepCookies := ds.JsonData.Get("keepCookies"); keepCookies != nil {
return keepCookies.MustStringArray()
}
}
return []string{}
}
// ----------------------
// COMMANDS

@ -115,7 +115,7 @@ func (c *ClientV2) CheckHealth(ctx context.Context, req *backend.CheckHealthRequ
}
protoContext := backend.ToProto().PluginContext(req.PluginContext)
protoResp, err := c.DiagnosticsClient.CheckHealth(ctx, &pluginv2.CheckHealthRequest{PluginContext: protoContext})
protoResp, err := c.DiagnosticsClient.CheckHealth(ctx, &pluginv2.CheckHealthRequest{PluginContext: protoContext, Headers: req.Headers})
if err != nil {
if status.Code(err) == codes.Unimplemented {

@ -151,14 +151,8 @@ func (s *Service) handleQueryData(ctx context.Context, user *models.SignedInUser
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 parsedReq.httpRequest != nil {
proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies())
if cookieStr := parsedReq.httpRequest.Header.Get("Cookie"); cookieStr != "" {
req.Headers["Cookie"] = cookieStr
}

Loading…
Cancel
Save