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

This commit is contained in:
Marcus Efraimsson 2022-05-31 17:58:06 +02:00 committed by GitHub
parent a91ecc566b
commit a7813275a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 20 deletions

View File

@ -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"]

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

View File

@ -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)

View File

@ -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{})

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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
}