pluginproxy.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package pluginproxy
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "net/http/httputil"
  10. "net/url"
  11. "text/template"
  12. "github.com/grafana/grafana/pkg/bus"
  13. "github.com/grafana/grafana/pkg/log"
  14. "github.com/grafana/grafana/pkg/middleware"
  15. m "github.com/grafana/grafana/pkg/models"
  16. "github.com/grafana/grafana/pkg/plugins"
  17. "github.com/grafana/grafana/pkg/util"
  18. )
  19. type templateData struct {
  20. JsonData map[string]interface{}
  21. SecureJsonData map[string]string
  22. }
  23. func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
  24. result := http.Header{}
  25. query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appId}
  26. if err := bus.Dispatch(&query); err != nil {
  27. return nil, err
  28. }
  29. data := templateData{
  30. JsonData: query.Result.JsonData,
  31. SecureJsonData: query.Result.SecureJsonData.Decrypt(),
  32. }
  33. for _, header := range route.Headers {
  34. var contentBuf bytes.Buffer
  35. t, err := template.New("content").Parse(header.Content)
  36. if err != nil {
  37. return nil, errors.New(fmt.Sprintf("could not parse header content template for header %s.", header.Name))
  38. }
  39. err = t.Execute(&contentBuf, data)
  40. if err != nil {
  41. return nil, errors.New(fmt.Sprintf("failed to execute header content template for header %s.", header.Name))
  42. }
  43. log.Trace("Adding header to proxy request. %s: %s", header.Name, contentBuf.String())
  44. result.Add(header.Name, contentBuf.String())
  45. }
  46. return result, nil
  47. }
  48. func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *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 len(route.Headers) > 0 {
  82. headers, err := getHeaders(route, ctx.OrgId, appId)
  83. if err != nil {
  84. ctx.JsonApiErr(500, "Could not generate plugin route header", err)
  85. return
  86. }
  87. for key, value := range headers {
  88. log.Trace("setting key %v value %v", key, value[0])
  89. req.Header.Set(key, value[0])
  90. }
  91. }
  92. // reqBytes, _ := httputil.DumpRequestOut(req, true);
  93. // log.Trace("Proxying plugin request: %s", string(reqBytes))
  94. }
  95. return &httputil.ReverseProxy{Director: director}
  96. }