middleware_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. package middleware
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "path/filepath"
  7. "testing"
  8. ms "github.com/go-macaron/session"
  9. "github.com/grafana/grafana/pkg/bus"
  10. m "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/services/session"
  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. So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
  31. So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
  32. })
  33. middlewareScenario("middleware should not add Cache-Control header to for non-API GET requests", func(sc *scenarioContext) {
  34. sc.fakeReq("GET", "/").exec()
  35. So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty)
  36. })
  37. middlewareScenario("Non api request should init session", func(sc *scenarioContext) {
  38. sc.fakeReq("GET", "/").exec()
  39. So(sc.resp.Header().Get("Set-Cookie"), ShouldContainSubstring, "grafana_sess")
  40. })
  41. middlewareScenario("Invalid api key", func(sc *scenarioContext) {
  42. sc.apiKey = "invalid_key_test"
  43. sc.fakeReq("GET", "/").exec()
  44. Convey("Should not init session", func() {
  45. So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty)
  46. })
  47. Convey("Should return 401", func() {
  48. So(sc.resp.Code, ShouldEqual, 401)
  49. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  50. })
  51. })
  52. middlewareScenario("Using basic auth", func(sc *scenarioContext) {
  53. bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error {
  54. query.Result = &m.User{
  55. Password: util.EncodePassword("myPass", "salt"),
  56. Salt: "salt",
  57. }
  58. return nil
  59. })
  60. bus.AddHandler("test", func(loginUserQuery *m.LoginUserQuery) error {
  61. return nil
  62. })
  63. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  64. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  65. return nil
  66. })
  67. setting.BasicAuthEnabled = true
  68. authHeader := util.GetBasicAuthHeader("myUser", "myPass")
  69. sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
  70. Convey("Should init middleware context with user", func() {
  71. So(sc.context.IsSignedIn, ShouldEqual, true)
  72. So(sc.context.OrgId, ShouldEqual, 2)
  73. So(sc.context.UserId, ShouldEqual, 12)
  74. })
  75. })
  76. middlewareScenario("Valid api key", func(sc *scenarioContext) {
  77. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  78. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  79. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  80. return nil
  81. })
  82. sc.fakeReq("GET", "/").withValidApiKey().exec()
  83. Convey("Should return 200", func() {
  84. So(sc.resp.Code, ShouldEqual, 200)
  85. })
  86. Convey("Should init middleware context", func() {
  87. So(sc.context.IsSignedIn, ShouldEqual, true)
  88. So(sc.context.OrgId, ShouldEqual, 12)
  89. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  90. })
  91. })
  92. middlewareScenario("Valid api key, but does not match db hash", func(sc *scenarioContext) {
  93. keyhash := "something_not_matching"
  94. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  95. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  96. return nil
  97. })
  98. sc.fakeReq("GET", "/").withValidApiKey().exec()
  99. Convey("Should return api key invalid", func() {
  100. So(sc.resp.Code, ShouldEqual, 401)
  101. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  102. })
  103. })
  104. middlewareScenario("Valid api key via Basic auth", func(sc *scenarioContext) {
  105. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  106. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  107. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  108. return nil
  109. })
  110. authHeader := util.GetBasicAuthHeader("api_key", "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9")
  111. sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
  112. Convey("Should return 200", func() {
  113. So(sc.resp.Code, ShouldEqual, 200)
  114. })
  115. Convey("Should init middleware context", func() {
  116. So(sc.context.IsSignedIn, ShouldEqual, true)
  117. So(sc.context.OrgId, ShouldEqual, 12)
  118. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  119. })
  120. })
  121. middlewareScenario("UserId in session", func(sc *scenarioContext) {
  122. sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
  123. c.Session.Set(session.SESS_KEY_USERID, int64(12))
  124. }).exec()
  125. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  126. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  127. return nil
  128. })
  129. sc.fakeReq("GET", "/").exec()
  130. Convey("should init context with user info", func() {
  131. So(sc.context.IsSignedIn, ShouldBeTrue)
  132. So(sc.context.UserId, ShouldEqual, 12)
  133. })
  134. })
  135. middlewareScenario("When anonymous access is enabled", func(sc *scenarioContext) {
  136. setting.AnonymousEnabled = true
  137. setting.AnonymousOrgName = "test"
  138. setting.AnonymousOrgRole = string(m.ROLE_EDITOR)
  139. bus.AddHandler("test", func(query *m.GetOrgByNameQuery) error {
  140. So(query.Name, ShouldEqual, "test")
  141. query.Result = &m.Org{Id: 2, Name: "test"}
  142. return nil
  143. })
  144. sc.fakeReq("GET", "/").exec()
  145. Convey("should init context with org info", func() {
  146. So(sc.context.UserId, ShouldEqual, 0)
  147. So(sc.context.OrgId, ShouldEqual, 2)
  148. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  149. })
  150. Convey("context signed in should be false", func() {
  151. So(sc.context.IsSignedIn, ShouldBeFalse)
  152. })
  153. })
  154. middlewareScenario("When auth_proxy is enabled enabled and user exists", func(sc *scenarioContext) {
  155. setting.AuthProxyEnabled = true
  156. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  157. setting.AuthProxyHeaderProperty = "username"
  158. setting.LdapEnabled = false
  159. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  160. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  161. return nil
  162. })
  163. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  164. cmd.Result = &m.User{Id: 12}
  165. return nil
  166. })
  167. sc.fakeReq("GET", "/")
  168. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  169. sc.exec()
  170. Convey("should init context with user info", func() {
  171. So(sc.context.IsSignedIn, ShouldBeTrue)
  172. So(sc.context.UserId, ShouldEqual, 12)
  173. So(sc.context.OrgId, ShouldEqual, 2)
  174. })
  175. })
  176. middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) {
  177. setting.AuthProxyEnabled = true
  178. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  179. setting.AuthProxyHeaderProperty = "username"
  180. setting.AuthProxyAutoSignUp = true
  181. setting.LdapEnabled = false
  182. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  183. if query.UserId > 0 {
  184. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  185. return nil
  186. }
  187. return m.ErrUserNotFound
  188. })
  189. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  190. cmd.Result = &m.User{Id: 33}
  191. return nil
  192. })
  193. sc.fakeReq("GET", "/")
  194. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  195. sc.exec()
  196. Convey("Should create user if auto sign up is enabled", func() {
  197. So(sc.context.IsSignedIn, ShouldBeTrue)
  198. So(sc.context.UserId, ShouldEqual, 33)
  199. So(sc.context.OrgId, ShouldEqual, 4)
  200. })
  201. })
  202. middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is not trusted", func(sc *scenarioContext) {
  203. setting.AuthProxyEnabled = true
  204. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  205. setting.AuthProxyHeaderProperty = "username"
  206. setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
  207. sc.fakeReq("GET", "/")
  208. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  209. sc.req.RemoteAddr = "192.168.3.1:12345"
  210. sc.exec()
  211. Convey("should return 407 status code", func() {
  212. So(sc.resp.Code, ShouldEqual, 407)
  213. So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.3.1 is not from the authentication proxy")
  214. })
  215. })
  216. middlewareScenario("When auth_proxy is enabled and IPv6 request RemoteAddr is not trusted", func(sc *scenarioContext) {
  217. setting.AuthProxyEnabled = true
  218. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  219. setting.AuthProxyHeaderProperty = "username"
  220. setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
  221. sc.fakeReq("GET", "/")
  222. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  223. sc.req.RemoteAddr = "[2001:23]:12345"
  224. sc.exec()
  225. Convey("should return 407 status code", func() {
  226. So(sc.resp.Code, ShouldEqual, 407)
  227. So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 2001:23 is not from the authentication proxy")
  228. })
  229. })
  230. middlewareScenario("When auth_proxy is enabled and request RemoteAddr is trusted", func(sc *scenarioContext) {
  231. setting.AuthProxyEnabled = true
  232. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  233. setting.AuthProxyHeaderProperty = "username"
  234. setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
  235. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  236. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  237. return nil
  238. })
  239. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  240. cmd.Result = &m.User{Id: 33}
  241. return nil
  242. })
  243. sc.fakeReq("GET", "/")
  244. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  245. sc.req.RemoteAddr = "[2001::23]:12345"
  246. sc.exec()
  247. Convey("Should init context with user info", func() {
  248. So(sc.context.IsSignedIn, ShouldBeTrue)
  249. So(sc.context.UserId, ShouldEqual, 33)
  250. So(sc.context.OrgId, ShouldEqual, 4)
  251. })
  252. })
  253. middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is not trusted", func(sc *scenarioContext) {
  254. setting.AuthProxyEnabled = true
  255. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  256. setting.AuthProxyHeaderProperty = "username"
  257. setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
  258. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  259. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  260. return nil
  261. })
  262. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  263. cmd.Result = &m.User{Id: 33}
  264. return nil
  265. })
  266. sc.fakeReq("GET", "/")
  267. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  268. sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.1, 192.168.1.2")
  269. sc.exec()
  270. Convey("should return 407 status code", func() {
  271. So(sc.resp.Code, ShouldEqual, 407)
  272. So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.1.2 is not from the authentication proxy")
  273. })
  274. })
  275. middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is trusted", func(sc *scenarioContext) {
  276. setting.AuthProxyEnabled = true
  277. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  278. setting.AuthProxyHeaderProperty = "username"
  279. setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
  280. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  281. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  282. return nil
  283. })
  284. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  285. cmd.Result = &m.User{Id: 33}
  286. return nil
  287. })
  288. sc.fakeReq("GET", "/")
  289. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  290. sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.2, 192.168.1.1")
  291. sc.exec()
  292. Convey("Should init context with user info", func() {
  293. So(sc.context.IsSignedIn, ShouldBeTrue)
  294. So(sc.context.UserId, ShouldEqual, 33)
  295. So(sc.context.OrgId, ShouldEqual, 4)
  296. })
  297. })
  298. middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) {
  299. setting.AuthProxyEnabled = true
  300. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  301. setting.AuthProxyHeaderProperty = "username"
  302. setting.AuthProxyWhitelist = ""
  303. bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
  304. query.Result = &m.User{Id: 32}
  305. return nil
  306. })
  307. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  308. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  309. return nil
  310. })
  311. // create session
  312. sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
  313. c.Session.Set(session.SESS_KEY_USERID, int64(33))
  314. }).exec()
  315. oldSessionID := sc.context.Session.ID()
  316. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  317. sc.exec()
  318. newSessionID := sc.context.Session.ID()
  319. Convey("Should not share session with other user", func() {
  320. So(oldSessionID, ShouldNotEqual, newSessionID)
  321. })
  322. })
  323. middlewareScenario("When auth_proxy and ldap enabled call sync with ldap user", func(sc *scenarioContext) {
  324. setting.AuthProxyEnabled = true
  325. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  326. setting.AuthProxyHeaderProperty = "username"
  327. setting.AuthProxyWhitelist = ""
  328. setting.LdapEnabled = true
  329. called := false
  330. syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
  331. called = true
  332. query.User = &m.User{Id: 32}
  333. return nil
  334. }
  335. bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
  336. query.Result = &m.User{Id: 32}
  337. return nil
  338. })
  339. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  340. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  341. return nil
  342. })
  343. sc.fakeReq("GET", "/")
  344. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  345. sc.exec()
  346. Convey("Should call syncGrafanaUserWithLdapUser", func() {
  347. So(called, ShouldBeTrue)
  348. })
  349. })
  350. })
  351. }
  352. func middlewareScenario(desc string, fn scenarioFunc) {
  353. Convey(desc, func() {
  354. defer bus.ClearBusHandlers()
  355. sc := &scenarioContext{}
  356. viewsPath, _ := filepath.Abs("../../public/views")
  357. sc.m = macaron.New()
  358. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  359. Directory: viewsPath,
  360. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  361. }))
  362. sc.m.Use(GetContextHandler())
  363. // mock out gc goroutine
  364. session.StartSessionGC = func() {}
  365. sc.m.Use(Sessioner(&ms.Options{}, 0))
  366. sc.m.Use(OrgRedirect())
  367. sc.m.Use(AddDefaultResponseHeaders())
  368. sc.defaultHandler = func(c *m.ReqContext) {
  369. sc.context = c
  370. if sc.handlerFunc != nil {
  371. sc.handlerFunc(sc.context)
  372. }
  373. }
  374. sc.m.Get("/", sc.defaultHandler)
  375. fn(sc)
  376. })
  377. }
  378. type scenarioContext struct {
  379. m *macaron.Macaron
  380. context *m.ReqContext
  381. resp *httptest.ResponseRecorder
  382. apiKey string
  383. authHeader string
  384. respJson map[string]interface{}
  385. handlerFunc handlerFunc
  386. defaultHandler macaron.Handler
  387. url string
  388. req *http.Request
  389. }
  390. func (sc *scenarioContext) withValidApiKey() *scenarioContext {
  391. sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9"
  392. return sc
  393. }
  394. func (sc *scenarioContext) withInvalidApiKey() *scenarioContext {
  395. sc.apiKey = "nvalidhhhhds"
  396. return sc
  397. }
  398. func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
  399. sc.authHeader = authHeader
  400. return sc
  401. }
  402. func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
  403. sc.resp = httptest.NewRecorder()
  404. req, err := http.NewRequest(method, url, nil)
  405. So(err, ShouldBeNil)
  406. sc.req = req
  407. // add session cookie from last request
  408. if sc.context != nil {
  409. if sc.context.Session.ID() != "" {
  410. req.Header.Add("Cookie", "grafana_sess="+sc.context.Session.ID()+";")
  411. }
  412. }
  413. return sc
  414. }
  415. func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map[string]string) *scenarioContext {
  416. sc.resp = httptest.NewRecorder()
  417. req, err := http.NewRequest(method, url, nil)
  418. q := req.URL.Query()
  419. for k, v := range queryParams {
  420. q.Add(k, v)
  421. }
  422. req.URL.RawQuery = q.Encode()
  423. So(err, ShouldBeNil)
  424. sc.req = req
  425. return sc
  426. }
  427. func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
  428. sc.handlerFunc = fn
  429. return sc
  430. }
  431. func (sc *scenarioContext) exec() {
  432. if sc.apiKey != "" {
  433. sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey)
  434. }
  435. if sc.authHeader != "" {
  436. sc.req.Header.Add("Authorization", sc.authHeader)
  437. }
  438. sc.m.ServeHTTP(sc.resp, sc.req)
  439. if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
  440. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  441. So(err, ShouldBeNil)
  442. }
  443. }
  444. type scenarioFunc func(c *scenarioContext)
  445. type handlerFunc func(c *m.ReqContext)