Fix panic when using complex dynamic URLs in app plugin routes (#27977)

* remove unused function to interpolate URLs

* share function to add headers between ds/plugin proxies

* stop performing unnecessary plugin setting lookup

* fix bug causing runtime errors when using complex templated URLs

* lower case util functions not used outside of pluginproxy package

* change test URL to a (valid) dummy URL to make intent clearer

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Victor Cinaglia
2020-11-17 04:56:42 -05:00
committed by GitHub
parent 0f3bebb38d
commit 967e9b39e8
8 changed files with 104 additions and 152 deletions

View File

@ -7,7 +7,6 @@ import (
"net/url"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
@ -20,55 +19,35 @@ type templateData struct {
SecureJsonData map[string]string
}
func getHeaders(route *plugins.AppPluginRoute, orgId int64, appID string) (http.Header, error) {
result := http.Header{}
query := models.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appID}
if err := bus.Dispatch(&query); err != nil {
return nil, err
}
data := templateData{
JsonData: query.Result.JsonData,
SecureJsonData: query.Result.SecureJsonData.Decrypt(),
}
err := addHeaders(&result, route, data)
return result, err
}
func updateURL(route *plugins.AppPluginRoute, orgId int64, appID string) (string, error) {
query := models.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appID}
if err := bus.Dispatch(&query); err != nil {
return "", err
}
data := templateData{
JsonData: query.Result.JsonData,
SecureJsonData: query.Result.SecureJsonData.Decrypt(),
}
interpolated, err := InterpolateString(route.URL, data)
if err != nil {
return "", err
}
return interpolated, err
}
// NewApiPluginProxy create a plugin proxy
func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins.AppPluginRoute, appID string,
cfg *setting.Cfg) (*httputil.ReverseProxy, error) {
targetURL, err := url.Parse(route.URL)
if err != nil {
return nil, err
}
func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins.AppPluginRoute, appID string, cfg *setting.Cfg) *httputil.ReverseProxy {
director := func(req *http.Request) {
query := models.GetPluginSettingByIdQuery{OrgId: ctx.OrgId, PluginId: appID}
if err := bus.Dispatch(&query); err != nil {
ctx.JsonApiErr(500, "Failed to fetch plugin settings", err)
return
}
data := templateData{
JsonData: query.Result.JsonData,
SecureJsonData: query.Result.SecureJsonData.Decrypt(),
}
interpolatedURL, err := interpolateString(route.URL, data)
if err != nil {
ctx.JsonApiErr(500, "Could not interpolate plugin route url", err)
return
}
targetURL, err := url.Parse(interpolatedURL)
if err != nil {
ctx.JsonApiErr(500, "Could not parse url", err)
return
}
req.URL.Scheme = targetURL.Scheme
req.URL.Host = targetURL.Host
req.Host = targetURL.Host
req.URL.Path = util.JoinURLFragments(targetURL.Path, proxyPath)
// clear cookie headers
req.Header.Del("Cookie")
req.Header.Del("Set-Cookie")
@ -86,38 +65,11 @@ func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins.
applyUserHeader(cfg.SendUserHeader, req, ctx.SignedInUser)
if len(route.Headers) > 0 {
headers, err := getHeaders(route, ctx.OrgId, appID)
if err != nil {
ctx.JsonApiErr(500, "Could not generate plugin route header", err)
return
}
for key, value := range headers {
log.Tracef("setting key %v value <redacted>", key)
req.Header.Set(key, value[0])
}
if err := addHeaders(&req.Header, route, data); err != nil {
ctx.JsonApiErr(500, "Failed to render plugin headers", err)
return
}
if len(route.URL) > 0 {
interpolatedURL, err := updateURL(route, ctx.OrgId, appID)
if err != nil {
ctx.JsonApiErr(500, "Could not interpolate plugin route url", err)
}
targetURL, err := url.Parse(interpolatedURL)
if err != nil {
ctx.JsonApiErr(500, "Could not parse custom url: %v", err)
return
}
req.URL.Scheme = targetURL.Scheme
req.URL.Host = targetURL.Host
req.Host = targetURL.Host
req.URL.Path = util.JoinURLFragments(targetURL.Path, proxyPath)
}
// reqBytes, _ := httputil.DumpRequestOut(req, true);
// log.Tracef("Proxying plugin request: %s", string(reqBytes))
}
return &httputil.ReverseProxy{Director: director}, nil
return &httputil.ReverseProxy{Director: director}
}