middleware_test.go 15 KB

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