/** * @function: func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy * @description: This function initializes a reverse proxy. * @related issues: OWL-159 * @related issues: OWL-168, OWL-123, OWL-028, OWL-017, OWL-002 * @param: ds *m.DataSource * @param: proxyPath string * @param: targetUrl *url.URL * @return: *httputil.ReverseProxy * @author: Don Hsieh * @since: 07/17/2015 * @last modified: 11/30/2015 * @called by: func ProxyDataSourceRequest(c *middleware.Context) * in pkg/api/dataproxy.go */ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy { director := func(req *http.Request) { req.URL.Scheme = targetUrl.Scheme req.URL.Host = targetUrl.Host req.Host = targetUrl.Host reqQueryVals := req.URL.Query() if ds.Type == m.DS_INFLUXDB_08 { req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath) reqQueryVals.Add("u", ds.User) reqQueryVals.Add("p", ds.Password) req.URL.RawQuery = reqQueryVals.Encode() } else if ds.Type == m.DS_INFLUXDB { req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) reqQueryVals.Add("db", ds.Database) req.URL.RawQuery = reqQueryVals.Encode() if !ds.BasicAuth { req.Header.Del("Authorization") req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password)) } } else if ds.Type == "openfalcon" { reqQueryVals.Add("target", ds.Url) req.URL.RawQuery = reqQueryVals.Encode() proxyPath = "/" target, _ := url.Parse(ds.Url) req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.Host = target.Host req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath) } else { req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) } if ds.BasicAuth { req.Header.Del("Authorization") req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword)) } // clear cookie headers req.Header.Del("Cookie") req.Header.Del("Set-Cookie") } return &httputil.ReverseProxy{Director: director} }
func TestMiddlewareContext(t *testing.T) { Convey("Given the grafana middleware", t, func() { middlewareScenario("middleware should add context to injector", func(sc *scenarioContext) { sc.fakeReq("GET", "/").exec() So(sc.context, ShouldNotBeNil) }) middlewareScenario("Default middleware should allow get request", func(sc *scenarioContext) { sc.fakeReq("GET", "/").exec() So(sc.resp.Code, ShouldEqual, 200) }) middlewareScenario("Non api request should init session", func(sc *scenarioContext) { sc.fakeReq("GET", "/").exec() So(sc.resp.Header().Get("Set-Cookie"), ShouldContainSubstring, "grafana_sess") }) middlewareScenario("Invalid api key", func(sc *scenarioContext) { sc.apiKey = "invalid_key_test" sc.fakeReq("GET", "/").exec() Convey("Should not init session", func() { So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty) }) Convey("Should return 401", func() { So(sc.resp.Code, ShouldEqual, 401) So(sc.respJson["message"], ShouldEqual, "Invalid API key") }) }) middlewareScenario("Using basic auth", func(sc *scenarioContext) { bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error { query.Result = &m.User{ Password: util.EncodePassword("myPass", "salt"), Salt: "salt", } return nil }) bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { query.Result = &m.SignedInUser{OrgId: 2, UserId: 12} return nil }) setting.BasicAuthEnabled = true authHeader := util.GetBasicAuthHeader("myUser", "myPass") sc.fakeReq("GET", "/").withAuthoriziationHeader(authHeader).exec() Convey("Should init middleware context with user", func() { So(sc.context.IsSignedIn, ShouldEqual, true) So(sc.context.OrgId, ShouldEqual, 2) So(sc.context.UserId, ShouldEqual, 12) }) }) middlewareScenario("Valid api key", func(sc *scenarioContext) { keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash} return nil }) sc.fakeReq("GET", "/").withValidApiKey().exec() Convey("Should return 200", func() { So(sc.resp.Code, ShouldEqual, 200) }) Convey("Should init middleware context", func() { So(sc.context.IsSignedIn, ShouldEqual, true) So(sc.context.OrgId, ShouldEqual, 12) So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR) }) }) middlewareScenario("Valid api key, but does not match db hash", func(sc *scenarioContext) { keyhash := "something_not_matching" bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash} return nil }) sc.fakeReq("GET", "/").withValidApiKey().exec() Convey("Should return api key invalid", func() { So(sc.resp.Code, ShouldEqual, 401) So(sc.respJson["message"], ShouldEqual, "Invalid API key") }) }) middlewareScenario("UserId in session", func(sc *scenarioContext) { sc.fakeReq("GET", "/").handler(func(c *Context) { c.Session.Set(SESS_KEY_USERID, int64(12)) }).exec() bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { query.Result = &m.SignedInUser{OrgId: 2, UserId: 12} return nil }) sc.fakeReq("GET", "/").exec() Convey("should init context with user info", func() { So(sc.context.IsSignedIn, ShouldBeTrue) So(sc.context.UserId, ShouldEqual, 12) }) }) middlewareScenario("When anonymous access is enabled", func(sc *scenarioContext) { setting.AnonymousEnabled = true setting.AnonymousOrgName = "test" setting.AnonymousOrgRole = string(m.ROLE_EDITOR) bus.AddHandler("test", func(query *m.GetOrgByNameQuery) error { So(query.Name, ShouldEqual, "test") query.Result = &m.Org{Id: 2, Name: "test"} return nil }) sc.fakeReq("GET", "/").exec() Convey("should init context with org info", func() { So(sc.context.UserId, ShouldEqual, 0) So(sc.context.OrgId, ShouldEqual, 2) So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR) }) Convey("context signed in should be false", func() { So(sc.context.IsSignedIn, ShouldBeFalse) }) }) middlewareScenario("When auth_proxy is enabled enabled and user exists", func(sc *scenarioContext) { setting.AuthProxyEnabled = true setting.AuthProxyHeaderName = "X-WEBAUTH-USER" setting.AuthProxyHeaderProperty = "username" bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { query.Result = &m.SignedInUser{OrgId: 2, UserId: 12} return nil }) sc.fakeReq("GET", "/") sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") sc.exec() Convey("should init context with user info", func() { So(sc.context.IsSignedIn, ShouldBeTrue) So(sc.context.UserId, ShouldEqual, 12) So(sc.context.OrgId, ShouldEqual, 2) }) }) middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) { setting.AuthProxyEnabled = true setting.AuthProxyHeaderName = "X-WEBAUTH-USER" setting.AuthProxyHeaderProperty = "username" setting.AuthProxyAutoSignUp = true bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { if query.UserId > 0 { query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} return nil } else { return m.ErrUserNotFound } }) var createUserCmd *m.CreateUserCommand bus.AddHandler("test", func(cmd *m.CreateUserCommand) error { createUserCmd = cmd cmd.Result = m.User{Id: 33} return nil }) sc.fakeReq("GET", "/") sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") sc.exec() Convey("Should create user if auto sign up is enabled", func() { So(sc.context.IsSignedIn, ShouldBeTrue) So(sc.context.UserId, ShouldEqual, 33) So(sc.context.OrgId, ShouldEqual, 4) }) }) }) }
func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) { orgDataSources := make([]*m.DataSource, 0) if c.OrgId != 0 { query := m.GetDataSourcesQuery{OrgId: c.OrgId} err := bus.Dispatch(&query) if err != nil { return nil, err } orgDataSources = query.Result } datasources := make(map[string]interface{}) var defaultDatasource string for _, ds := range orgDataSources { url := ds.Url if ds.Access == m.DS_ACCESS_PROXY { url = setting.AppSubUrl + "/api/datasources/proxy/" + strconv.FormatInt(ds.Id, 10) } var dsMap = map[string]interface{}{ "type": ds.Type, "name": ds.Name, "url": url, } meta, exists := plugins.DataSources[ds.Type] if !exists { log.Error(3, "Could not find plugin definition for data source: %v", ds.Type) continue } dsMap["meta"] = meta if ds.IsDefault { defaultDatasource = ds.Name } if len(ds.JsonData) > 0 { dsMap["jsonData"] = ds.JsonData } if ds.Access == m.DS_ACCESS_DIRECT { if ds.BasicAuth { dsMap["basicAuth"] = util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword) } if ds.Type == m.DS_INFLUXDB_08 { dsMap["username"] = ds.User dsMap["password"] = ds.Password dsMap["url"] = url + "/db/" + ds.Database } if ds.Type == m.DS_INFLUXDB { dsMap["username"] = ds.User dsMap["password"] = ds.Password dsMap["database"] = ds.Database dsMap["url"] = url } } if ds.Type == m.DS_ES { dsMap["index"] = ds.Database } if ds.Type == m.DS_PROMETHEUS { // add unproxied server URL for link to Prometheus web UI dsMap["directUrl"] = ds.Url } datasources[ds.Name] = dsMap } // add grafana backend data source grafanaDatasourceMeta, _ := plugins.DataSources["grafana"] datasources["-- Grafana --"] = map[string]interface{}{ "type": "grafana", "meta": grafanaDatasourceMeta, } // add mixed backend data source datasources["-- Mixed --"] = map[string]interface{}{ "type": "mixed", "meta": plugins.DataSources["mixed"], } if defaultDatasource == "" { defaultDatasource = "-- Grafana --" } jsonObj := map[string]interface{}{ "defaultDatasource": defaultDatasource, "datasources": datasources, "appSubUrl": setting.AppSubUrl, "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, "buildInfo": map[string]interface{}{ "version": setting.BuildVersion, "commit": setting.BuildCommit, "buildstamp": setting.BuildStamp, }, } return jsonObj, nil }