mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 03:13:49 +08:00
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:
@ -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}
|
||||
}
|
||||
|
Reference in New Issue
Block a user