pluginproxy.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package pluginproxy
  2. import (
  3. "encoding/json"
  4. "net"
  5. "net/http"
  6. "net/http/httputil"
  7. "net/url"
  8. "github.com/grafana/grafana/pkg/setting"
  9. "github.com/grafana/grafana/pkg/bus"
  10. "github.com/grafana/grafana/pkg/infra/log"
  11. m "github.com/grafana/grafana/pkg/models"
  12. "github.com/grafana/grafana/pkg/plugins"
  13. "github.com/grafana/grafana/pkg/util"
  14. )
  15. type templateData struct {
  16. JsonData map[string]interface{}
  17. SecureJsonData map[string]string
  18. }
  19. func getHeaders(route *plugins.AppPluginRoute, orgId int64, appID string) (http.Header, error) {
  20. result := http.Header{}
  21. query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appID}
  22. if err := bus.Dispatch(&query); err != nil {
  23. return nil, err
  24. }
  25. data := templateData{
  26. JsonData: query.Result.JsonData,
  27. SecureJsonData: query.Result.SecureJsonData.Decrypt(),
  28. }
  29. err := addHeaders(&result, route, data)
  30. return result, err
  31. }
  32. func updateURL(route *plugins.AppPluginRoute, orgId int64, appID string) (string, error) {
  33. query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appID}
  34. if err := bus.Dispatch(&query); err != nil {
  35. return "", err
  36. }
  37. data := templateData{
  38. JsonData: query.Result.JsonData,
  39. SecureJsonData: query.Result.SecureJsonData.Decrypt(),
  40. }
  41. interpolated, err := InterpolateString(route.Url, data)
  42. if err != nil {
  43. return "", err
  44. }
  45. return interpolated, err
  46. }
  47. // NewApiPluginProxy create a plugin proxy
  48. func NewApiPluginProxy(ctx *m.ReqContext, proxyPath string, route *plugins.AppPluginRoute, appID string, cfg *setting.Cfg) *httputil.ReverseProxy {
  49. targetURL, _ := url.Parse(route.Url)
  50. director := func(req *http.Request) {
  51. req.URL.Scheme = targetURL.Scheme
  52. req.URL.Host = targetURL.Host
  53. req.Host = targetURL.Host
  54. req.URL.Path = util.JoinURLFragments(targetURL.Path, proxyPath)
  55. // clear cookie headers
  56. req.Header.Del("Cookie")
  57. req.Header.Del("Set-Cookie")
  58. // clear X-Forwarded Host/Port/Proto headers
  59. req.Header.Del("X-Forwarded-Host")
  60. req.Header.Del("X-Forwarded-Port")
  61. req.Header.Del("X-Forwarded-Proto")
  62. // set X-Forwarded-For header
  63. if req.RemoteAddr != "" {
  64. remoteAddr, _, err := net.SplitHostPort(req.RemoteAddr)
  65. if err != nil {
  66. remoteAddr = req.RemoteAddr
  67. }
  68. if req.Header.Get("X-Forwarded-For") != "" {
  69. req.Header.Set("X-Forwarded-For", req.Header.Get("X-Forwarded-For")+", "+remoteAddr)
  70. } else {
  71. req.Header.Set("X-Forwarded-For", remoteAddr)
  72. }
  73. }
  74. // Create a HTTP header with the context in it.
  75. ctxJSON, err := json.Marshal(ctx.SignedInUser)
  76. if err != nil {
  77. ctx.JsonApiErr(500, "failed to marshal context to json.", err)
  78. return
  79. }
  80. req.Header.Add("X-Grafana-Context", string(ctxJSON))
  81. if cfg.SendUserHeader && !ctx.SignedInUser.IsAnonymous {
  82. req.Header.Add("X-Grafana-User", ctx.SignedInUser.Login)
  83. }
  84. if len(route.Headers) > 0 {
  85. headers, err := getHeaders(route, ctx.OrgId, appID)
  86. if err != nil {
  87. ctx.JsonApiErr(500, "Could not generate plugin route header", err)
  88. return
  89. }
  90. for key, value := range headers {
  91. log.Trace("setting key %v value <redacted>", key)
  92. req.Header.Set(key, value[0])
  93. }
  94. }
  95. if len(route.Url) > 0 {
  96. interpolatedURL, err := updateURL(route, ctx.OrgId, appID)
  97. if err != nil {
  98. ctx.JsonApiErr(500, "Could not interpolate plugin route url", err)
  99. }
  100. targetURL, err := url.Parse(interpolatedURL)
  101. if err != nil {
  102. ctx.JsonApiErr(500, "Could not parse custom url: %v", err)
  103. return
  104. }
  105. req.URL.Scheme = targetURL.Scheme
  106. req.URL.Host = targetURL.Host
  107. req.Host = targetURL.Host
  108. req.URL.Path = util.JoinURLFragments(targetURL.Path, proxyPath)
  109. }
  110. // reqBytes, _ := httputil.DumpRequestOut(req, true);
  111. // log.Trace("Proxying plugin request: %s", string(reqBytes))
  112. }
  113. return &httputil.ReverseProxy{Director: director}
  114. }