diff --git a/pkg/bus/bus.go b/pkg/bus/bus.go index 2865c740fd1..80b80698a1c 100644 --- a/pkg/bus/bus.go +++ b/pkg/bus/bus.go @@ -121,3 +121,7 @@ func Dispatch(msg Msg) error { func Publish(msg Msg) error { return globalBus.Publish(msg) } + +func ClearBusHandlers() { + globalBus = New() +} diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index b93cd517364..efeee0ac3e7 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -1,6 +1,7 @@ package middleware import ( + "fmt" "strconv" "strings" @@ -78,11 +79,13 @@ func initContextWithUserSessionCookie(ctx *Context) bool { var userId int64 if userId = getRequestUserId(ctx); userId == 0 { + fmt.Printf("Not userId") return false } query := m.GetSignedInUserQuery{UserId: userId} if err := bus.Dispatch(&query); err != nil { + log.Error(3, "Failed to get user with id %v", userId) return false } else { ctx.SignedInUser = query.Result diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index e8e75465c82..ebbf0a1f038 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -10,82 +10,33 @@ import ( "github.com/Unknwon/macaron" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" "github.com/macaron-contrib/session" . "github.com/smartystreets/goconvey/convey" ) -type scenarioContext struct { - m *macaron.Macaron - context *Context - resp *httptest.ResponseRecorder - apiKey string - respJson map[string]interface{} -} - -func (sc *scenarioContext) PerformGet(url string) { - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - if sc.apiKey != "" { - req.Header.Add("Authorization", "Bearer "+sc.apiKey) - } - sc.m.ServeHTTP(sc.resp, req) - - if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" { - err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson) - So(err, ShouldBeNil) - } -} - -type scenarioFunc func(c *scenarioContext) -type reqModifier func(c *http.Request) - -func middlewareScenario(desc string, fn scenarioFunc) { - Convey(desc, func() { - sc := &scenarioContext{} - viewsPath, _ := filepath.Abs("../../public/views") - - sc.m = macaron.New() - sc.m.Use(macaron.Renderer(macaron.RenderOptions{ - Directory: viewsPath, - Delims: macaron.Delims{Left: "[[", Right: "]]"}, - })) - - sc.m.Use(GetContextHandler()) - // mock out gc goroutine - startSessionGC = func() {} - sc.m.Use(Sessioner(&session.Options{})) - - sc.m.Get("/", func(c *Context) { - sc.context = c - }) - - sc.resp = httptest.NewRecorder() - fn(sc) - }) -} - func TestMiddlewareContext(t *testing.T) { - Convey("Given grafana context", t, func() { + Convey("Given the grafana middleware", t, func() { middlewareScenario("middleware should add context to injector", func(sc *scenarioContext) { - sc.PerformGet("/") + sc.fakeReq("GET", "/").exec() So(sc.context, ShouldNotBeNil) }) middlewareScenario("Default middleware should allow get request", func(sc *scenarioContext) { - sc.PerformGet("/") + sc.fakeReq("GET", "/").exec() So(sc.resp.Code, ShouldEqual, 200) }) middlewareScenario("Non api request should init session", func(sc *scenarioContext) { - sc.PerformGet("/") + 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.PerformGet("/") + sc.fakeReq("GET", "/").exec() Convey("Should not init session", func() { So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty) @@ -98,7 +49,6 @@ func TestMiddlewareContext(t *testing.T) { }) middlewareScenario("Valid api key", func(sc *scenarioContext) { - sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9" keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { @@ -106,17 +56,164 @@ func TestMiddlewareContext(t *testing.T) { return nil }) - sc.PerformGet("/") + 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) + }) + }) }) } + +func middlewareScenario(desc string, fn scenarioFunc) { + Convey(desc, func() { + defer bus.ClearBusHandlers() + + sc := &scenarioContext{} + viewsPath, _ := filepath.Abs("../../public/views") + + sc.m = macaron.New() + sc.m.Use(macaron.Renderer(macaron.RenderOptions{ + Directory: viewsPath, + Delims: macaron.Delims{Left: "[[", Right: "]]"}, + })) + + sc.m.Use(GetContextHandler()) + // mock out gc goroutine + startSessionGC = func() {} + sc.m.Use(Sessioner(&session.Options{})) + + sc.m.Get("/", func(c *Context) { + sc.context = c + if sc.handlerFunc != nil { + sc.handlerFunc(sc.context) + } + }) + + fn(sc) + }) +} + +type scenarioContext struct { + m *macaron.Macaron + context *Context + resp *httptest.ResponseRecorder + apiKey string + respJson map[string]interface{} + handlerFunc handlerFunc + + req *http.Request +} + +func (sc *scenarioContext) withValidApiKey() *scenarioContext { + sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9" + return sc +} + +func (sc *scenarioContext) withInvalidApiKey() *scenarioContext { + sc.apiKey = "nvalidhhhhds" + return sc +} + +func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext { + sc.resp = httptest.NewRecorder() + req, err := http.NewRequest(method, url, nil) + So(err, ShouldBeNil) + sc.req = req + + // add session cookie from last request + if sc.context != nil { + if sc.context.Session.ID() != "" { + req.Header.Add("Cookie", "grafana_sess="+sc.context.Session.ID()+";") + } + } + + return sc +} + +func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext { + sc.handlerFunc = fn + return sc +} + +func (sc *scenarioContext) exec() { + if sc.apiKey != "" { + sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey) + } + + sc.m.ServeHTTP(sc.resp, sc.req) + + if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" { + err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson) + So(err, ShouldBeNil) + } +} + +type scenarioFunc func(c *scenarioContext) +type handlerFunc func(c *Context)