diff --git a/pkg/api/api_plugin.go b/pkg/api/api_plugin.go index b14afec3a62..751c234bfe8 100644 --- a/pkg/api/api_plugin.go +++ b/pkg/api/api_plugin.go @@ -1,13 +1,17 @@ package api import ( + "bytes" "encoding/json" + "fmt" "net/http" "net/http/httputil" "net/url" + "text/template" "gopkg.in/macaron.v1" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" @@ -34,32 +38,28 @@ func InitApiPluginRoutes(r *macaron.Macaron) { handlers = append(handlers, middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)) } } - handlers = append(handlers, ApiPlugin(route.Url)) + handlers = append(handlers, ApiPlugin(route, plugin.IncludedInAppId)) r.Route(url, route.Method, handlers...) log.Info("Plugin: Adding route %s", url) } } } -func ApiPlugin(routeUrl string) macaron.Handler { +func ApiPlugin(route *plugins.ApiPluginRoute, includedInAppId string) macaron.Handler { return func(c *middleware.Context) { path := c.Params("*") - //Create a HTTP header with the context in it. - ctx, err := json.Marshal(c.SignedInUser) - if err != nil { - c.JsonApiErr(500, "failed to marshal context to json.", err) - return - } - targetUrl, _ := url.Parse(routeUrl) - proxy := NewApiPluginProxy(string(ctx), path, targetUrl) + proxy := NewApiPluginProxy(c, path, route, includedInAppId) proxy.Transport = dataProxyTransport proxy.ServeHTTP(c.Resp, c.Req.Request) } } -func NewApiPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy { +func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.ApiPluginRoute, includedInAppId string) *httputil.ReverseProxy { + targetUrl, _ := url.Parse(route.Url) + director := func(req *http.Request) { + req.URL.Scheme = targetUrl.Scheme req.URL.Host = targetUrl.Host req.Host = targetUrl.Host @@ -69,7 +69,46 @@ func NewApiPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httput // clear cookie headers req.Header.Del("Cookie") req.Header.Del("Set-Cookie") - req.Header.Add("Grafana-Context", ctx) + + //Create a HTTP header with the context in it. + ctxJson, err := json.Marshal(ctx.SignedInUser) + if err != nil { + ctx.JsonApiErr(500, "failed to marshal context to json.", err) + return + } + + req.Header.Add("Grafana-Context", string(ctxJson)) + // add custom headers defined in the plugin config. + for _, header := range route.Headers { + var contentBuf bytes.Buffer + t, err := template.New("content").Parse(header.Content) + if err != nil { + ctx.JsonApiErr(500, fmt.Sprintf("could not parse header content template for header %s.", header.Name), err) + return + } + + jsonData := make(map[string]interface{}) + + if includedInAppId != "" { + //lookup appSettings + query := m.GetAppSettingByAppIdQuery{OrgId: ctx.OrgId, AppId: includedInAppId} + + if err := bus.Dispatch(&query); err != nil { + ctx.JsonApiErr(500, "failed to get AppSettings of includedAppId.", err) + return + } + + jsonData = query.Result.JsonData + } + + err = t.Execute(&contentBuf, jsonData) + if err != nil { + ctx.JsonApiErr(500, fmt.Sprintf("failed to execute header content template for header %s.", header.Name), err) + return + } + log.Debug("Adding header to proxy request. %s: %s", header.Name, contentBuf.String()) + req.Header.Add(header.Name, contentBuf.String()) + } } return &httputil.ReverseProxy{Director: director} diff --git a/pkg/models/app_settings.go b/pkg/models/app_settings.go index 558946d0277..f3b60502cb0 100644 --- a/pkg/models/app_settings.go +++ b/pkg/models/app_settings.go @@ -1,6 +1,13 @@ package models -import "time" +import ( + "errors" + "time" +) + +var ( + ErrAppSettingNotFound = errors.New("AppSetting not found") +) type AppSettings struct { Id int64 @@ -33,3 +40,9 @@ type GetAppSettingsQuery struct { OrgId int64 Result []*AppSettings } + +type GetAppSettingByAppIdQuery struct { + AppId string + OrgId int64 + Result *AppSettings +} diff --git a/pkg/plugins/api_plugin.go b/pkg/plugins/api_plugin.go index 0a47b7d18b2..efb16e696d8 100644 --- a/pkg/plugins/api_plugin.go +++ b/pkg/plugins/api_plugin.go @@ -7,12 +7,13 @@ import ( ) type ApiPluginRoute struct { - Path string `json:"path"` - Method string `json:"method"` - ReqSignedIn bool `json:"reqSignedIn"` - ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` - ReqRole models.RoleType `json:"reqRole"` - Url string `json:"url"` + Path string `json:"path"` + Method string `json:"method"` + ReqSignedIn bool `json:"reqSignedIn"` + ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` + ReqRole models.RoleType `json:"reqRole"` + Url string `json:"url"` + Headers []ApiPluginHeader `json:"headers"` } type ApiPlugin struct { @@ -20,11 +21,18 @@ type ApiPlugin struct { Routes []*ApiPluginRoute `json:"routes"` } +type ApiPluginHeader struct { + Name string `json:"name"` + Content string `json:"content"` +} + func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { if err := decoder.Decode(&app); err != nil { return err } + app.PluginDir = pluginDir + ApiPlugins[app.Id] = app return nil } diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index 898065be8a2..d2291d487ac 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -59,6 +59,18 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { } } + // check if we have child apiPlugins + for _, plugin := range ApiPlugins { + if strings.HasPrefix(plugin.PluginDir, app.PluginDir) { + plugin.IncludedInAppId = app.Id + app.Includes = append(app.Includes, AppIncludeInfo{ + Name: plugin.Name, + Id: plugin.Id, + Type: plugin.Type, + }) + } + } + Apps[app.Id] = app return nil } diff --git a/pkg/services/sqlstore/app_settings.go b/pkg/services/sqlstore/app_settings.go index e9bfbeaa73b..7d9482e7b22 100644 --- a/pkg/services/sqlstore/app_settings.go +++ b/pkg/services/sqlstore/app_settings.go @@ -9,6 +9,7 @@ import ( func init() { bus.AddHandler("sql", GetAppSettings) + bus.AddHandler("sql", GetAppSettingByAppId) bus.AddHandler("sql", UpdateAppSettings) } @@ -19,6 +20,18 @@ func GetAppSettings(query *m.GetAppSettingsQuery) error { return sess.Find(&query.Result) } +func GetAppSettingByAppId(query *m.GetAppSettingByAppIdQuery) error { + appSetting := m.AppSettings{OrgId: query.OrgId, AppId: query.AppId} + has, err := x.Get(&appSetting) + if err != nil { + return err + } else if has == false { + return m.ErrAppSettingNotFound + } + query.Result = &appSetting + return nil +} + func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error { return inTransaction2(func(sess *session) error { var app m.AppSettings