middleware_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. package middleware
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "path/filepath"
  7. "testing"
  8. "github.com/go-macaron/session"
  9. "github.com/grafana/grafana/pkg/bus"
  10. l "github.com/grafana/grafana/pkg/login"
  11. m "github.com/grafana/grafana/pkg/models"
  12. "github.com/grafana/grafana/pkg/setting"
  13. "github.com/grafana/grafana/pkg/util"
  14. . "github.com/smartystreets/goconvey/convey"
  15. "gopkg.in/macaron.v1"
  16. )
  17. func TestMiddlewareContext(t *testing.T) {
  18. Convey("Given the grafana middleware", t, func() {
  19. middlewareScenario("middleware should add context to injector", func(sc *scenarioContext) {
  20. sc.fakeReq("GET", "/").exec()
  21. So(sc.context, ShouldNotBeNil)
  22. })
  23. middlewareScenario("Default middleware should allow get request", func(sc *scenarioContext) {
  24. sc.fakeReq("GET", "/").exec()
  25. So(sc.resp.Code, ShouldEqual, 200)
  26. })
  27. middlewareScenario("middleware should add Cache-Control header for GET requests to API", func(sc *scenarioContext) {
  28. sc.fakeReq("GET", "/api/search").exec()
  29. So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
  30. })
  31. middlewareScenario("middleware should not add Cache-Control header to for non-API GET requests", func(sc *scenarioContext) {
  32. sc.fakeReq("GET", "/").exec()
  33. So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty)
  34. })
  35. middlewareScenario("Non api request should init session", func(sc *scenarioContext) {
  36. sc.fakeReq("GET", "/").exec()
  37. So(sc.resp.Header().Get("Set-Cookie"), ShouldContainSubstring, "grafana_sess")
  38. })
  39. middlewareScenario("Invalid api key", func(sc *scenarioContext) {
  40. sc.apiKey = "invalid_key_test"
  41. sc.fakeReq("GET", "/").exec()
  42. Convey("Should not init session", func() {
  43. So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty)
  44. })
  45. Convey("Should return 401", func() {
  46. So(sc.resp.Code, ShouldEqual, 401)
  47. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  48. })
  49. })
  50. middlewareScenario("Using basic auth", func(sc *scenarioContext) {
  51. bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error {
  52. query.Result = &m.User{
  53. Password: util.EncodePassword("myPass", "salt"),
  54. Salt: "salt",
  55. }
  56. return nil
  57. })
  58. bus.AddHandler("test", func(loginUserQuery *l.LoginUserQuery) error {
  59. return nil
  60. })
  61. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  62. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  63. return nil
  64. })
  65. setting.BasicAuthEnabled = true
  66. authHeader := util.GetBasicAuthHeader("myUser", "myPass")
  67. sc.fakeReq("GET", "/").withAuthoriziationHeader(authHeader).exec()
  68. Convey("Should init middleware context with user", func() {
  69. So(sc.context.IsSignedIn, ShouldEqual, true)
  70. So(sc.context.OrgId, ShouldEqual, 2)
  71. So(sc.context.UserId, ShouldEqual, 12)
  72. })
  73. })
  74. middlewareScenario("Valid api key", func(sc *scenarioContext) {
  75. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  76. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  77. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  78. return nil
  79. })
  80. sc.fakeReq("GET", "/").withValidApiKey().exec()
  81. Convey("Should return 200", func() {
  82. So(sc.resp.Code, ShouldEqual, 200)
  83. })
  84. Convey("Should init middleware context", func() {
  85. So(sc.context.IsSignedIn, ShouldEqual, true)
  86. So(sc.context.OrgId, ShouldEqual, 12)
  87. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  88. })
  89. })
  90. middlewareScenario("Valid api key, but does not match db hash", func(sc *scenarioContext) {
  91. keyhash := "something_not_matching"
  92. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  93. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  94. return nil
  95. })
  96. sc.fakeReq("GET", "/").withValidApiKey().exec()
  97. Convey("Should return api key invalid", func() {
  98. So(sc.resp.Code, ShouldEqual, 401)
  99. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  100. })
  101. })
  102. middlewareScenario("UserId in session", func(sc *scenarioContext) {
  103. sc.fakeReq("GET", "/").handler(func(c *Context) {
  104. c.Session.Set(SESS_KEY_USERID, int64(12))
  105. }).exec()
  106. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  107. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  108. return nil
  109. })
  110. sc.fakeReq("GET", "/").exec()
  111. Convey("should init context with user info", func() {
  112. So(sc.context.IsSignedIn, ShouldBeTrue)
  113. So(sc.context.UserId, ShouldEqual, 12)
  114. })
  115. })
  116. middlewareScenario("When anonymous access is enabled", func(sc *scenarioContext) {
  117. setting.AnonymousEnabled = true
  118. setting.AnonymousOrgName = "test"
  119. setting.AnonymousOrgRole = string(m.ROLE_EDITOR)
  120. bus.AddHandler("test", func(query *m.GetOrgByNameQuery) error {
  121. So(query.Name, ShouldEqual, "test")
  122. query.Result = &m.Org{Id: 2, Name: "test"}
  123. return nil
  124. })
  125. sc.fakeReq("GET", "/").exec()
  126. Convey("should init context with org info", func() {
  127. So(sc.context.UserId, ShouldEqual, 0)
  128. So(sc.context.OrgId, ShouldEqual, 2)
  129. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  130. })
  131. Convey("context signed in should be false", func() {
  132. So(sc.context.IsSignedIn, ShouldBeFalse)
  133. })
  134. })
  135. middlewareScenario("When auth_proxy is enabled enabled and user exists", func(sc *scenarioContext) {
  136. setting.AuthProxyEnabled = true
  137. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  138. setting.AuthProxyHeaderProperty = "username"
  139. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  140. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  141. return nil
  142. })
  143. sc.fakeReq("GET", "/")
  144. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  145. sc.exec()
  146. Convey("should init context with user info", func() {
  147. So(sc.context.IsSignedIn, ShouldBeTrue)
  148. So(sc.context.UserId, ShouldEqual, 12)
  149. So(sc.context.OrgId, ShouldEqual, 2)
  150. })
  151. })
  152. middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) {
  153. setting.AuthProxyEnabled = true
  154. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  155. setting.AuthProxyHeaderProperty = "username"
  156. setting.AuthProxyAutoSignUp = true
  157. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  158. if query.UserId > 0 {
  159. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  160. return nil
  161. } else {
  162. return m.ErrUserNotFound
  163. }
  164. })
  165. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  166. cmd.Result = m.User{Id: 33}
  167. return nil
  168. })
  169. sc.fakeReq("GET", "/")
  170. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  171. sc.exec()
  172. Convey("Should create user if auto sign up is enabled", func() {
  173. So(sc.context.IsSignedIn, ShouldBeTrue)
  174. So(sc.context.UserId, ShouldEqual, 33)
  175. So(sc.context.OrgId, ShouldEqual, 4)
  176. })
  177. })
  178. middlewareScenario("When auth_proxy is enabled and request RemoteAddr is not trusted", func(sc *scenarioContext) {
  179. setting.AuthProxyEnabled = true
  180. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  181. setting.AuthProxyHeaderProperty = "username"
  182. setting.AuthProxyWhitelist = "192.168.1.1, 192.168.2.1"
  183. sc.fakeReq("GET", "/")
  184. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  185. sc.req.RemoteAddr = "192.168.3.1:12345"
  186. sc.exec()
  187. Convey("should return 407 status code", func() {
  188. So(sc.resp.Code, ShouldEqual, 407)
  189. })
  190. })
  191. middlewareScenario("When auth_proxy is enabled and request RemoteAddr is trusted", func(sc *scenarioContext) {
  192. setting.AuthProxyEnabled = true
  193. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  194. setting.AuthProxyHeaderProperty = "username"
  195. setting.AuthProxyWhitelist = "192.168.1.1, 192.168.2.1"
  196. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  197. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  198. return nil
  199. })
  200. sc.fakeReq("GET", "/")
  201. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  202. sc.req.RemoteAddr = "192.168.2.1:12345"
  203. sc.exec()
  204. Convey("Should init context with user info", func() {
  205. So(sc.context.IsSignedIn, ShouldBeTrue)
  206. So(sc.context.UserId, ShouldEqual, 33)
  207. So(sc.context.OrgId, ShouldEqual, 4)
  208. })
  209. })
  210. middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) {
  211. setting.AuthProxyEnabled = true
  212. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  213. setting.AuthProxyHeaderProperty = "username"
  214. setting.AuthProxyWhitelist = ""
  215. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  216. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  217. return nil
  218. })
  219. // create session
  220. sc.fakeReq("GET", "/").handler(func(c *Context) {
  221. c.Session.Set(SESS_KEY_USERID, int64(33))
  222. }).exec()
  223. oldSessionID := sc.context.Session.ID()
  224. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  225. sc.exec()
  226. newSessionID := sc.context.Session.ID()
  227. Convey("Should not share session with other user", func() {
  228. So(oldSessionID, ShouldNotEqual, newSessionID)
  229. })
  230. })
  231. middlewareScenario("When auth_proxy and ldap enabled call sync with ldap user", func(sc *scenarioContext) {
  232. setting.AuthProxyEnabled = true
  233. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  234. setting.AuthProxyHeaderProperty = "username"
  235. setting.AuthProxyWhitelist = ""
  236. setting.LdapEnabled = true
  237. called := false
  238. syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQuery) error {
  239. called = true
  240. return nil
  241. }
  242. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  243. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  244. return nil
  245. })
  246. sc.fakeReq("GET", "/")
  247. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  248. sc.exec()
  249. Convey("Should call syncGrafanaUserWithLdapUser", func() {
  250. So(called, ShouldBeTrue)
  251. })
  252. })
  253. })
  254. }
  255. func middlewareScenario(desc string, fn scenarioFunc) {
  256. Convey(desc, func() {
  257. defer bus.ClearBusHandlers()
  258. sc := &scenarioContext{}
  259. viewsPath, _ := filepath.Abs("../../public/views")
  260. sc.m = macaron.New()
  261. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  262. Directory: viewsPath,
  263. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  264. }))
  265. sc.m.Use(GetContextHandler())
  266. // mock out gc goroutine
  267. startSessionGC = func() {}
  268. sc.m.Use(Sessioner(&session.Options{}))
  269. sc.m.Use(OrgRedirect())
  270. sc.m.Use(AddDefaultResponseHeaders())
  271. sc.defaultHandler = func(c *Context) {
  272. sc.context = c
  273. if sc.handlerFunc != nil {
  274. sc.handlerFunc(sc.context)
  275. }
  276. }
  277. sc.m.Get("/", sc.defaultHandler)
  278. fn(sc)
  279. })
  280. }
  281. type scenarioContext struct {
  282. m *macaron.Macaron
  283. context *Context
  284. resp *httptest.ResponseRecorder
  285. apiKey string
  286. authHeader string
  287. respJson map[string]interface{}
  288. handlerFunc handlerFunc
  289. defaultHandler macaron.Handler
  290. req *http.Request
  291. }
  292. func (sc *scenarioContext) withValidApiKey() *scenarioContext {
  293. sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9"
  294. return sc
  295. }
  296. func (sc *scenarioContext) withInvalidApiKey() *scenarioContext {
  297. sc.apiKey = "nvalidhhhhds"
  298. return sc
  299. }
  300. func (sc *scenarioContext) withAuthoriziationHeader(authHeader string) *scenarioContext {
  301. sc.authHeader = authHeader
  302. return sc
  303. }
  304. func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
  305. sc.resp = httptest.NewRecorder()
  306. req, err := http.NewRequest(method, url, nil)
  307. So(err, ShouldBeNil)
  308. sc.req = req
  309. // add session cookie from last request
  310. if sc.context != nil {
  311. if sc.context.Session.ID() != "" {
  312. req.Header.Add("Cookie", "grafana_sess="+sc.context.Session.ID()+";")
  313. }
  314. }
  315. return sc
  316. }
  317. func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
  318. sc.handlerFunc = fn
  319. return sc
  320. }
  321. func (sc *scenarioContext) exec() {
  322. if sc.apiKey != "" {
  323. sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey)
  324. }
  325. if sc.authHeader != "" {
  326. sc.req.Header.Add("Authorization", sc.authHeader)
  327. }
  328. sc.m.ServeHTTP(sc.resp, sc.req)
  329. if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
  330. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  331. So(err, ShouldBeNil)
  332. }
  333. }
  334. type scenarioFunc func(c *scenarioContext)
  335. type handlerFunc func(c *Context)