middleware_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. package middleware
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "net/http/httptest"
  7. "path/filepath"
  8. "testing"
  9. "time"
  10. msession "github.com/go-macaron/session"
  11. "github.com/grafana/grafana/pkg/bus"
  12. "github.com/grafana/grafana/pkg/infra/remotecache"
  13. m "github.com/grafana/grafana/pkg/models"
  14. "github.com/grafana/grafana/pkg/services/auth"
  15. "github.com/grafana/grafana/pkg/services/session"
  16. "github.com/grafana/grafana/pkg/setting"
  17. "github.com/grafana/grafana/pkg/util"
  18. . "github.com/smartystreets/goconvey/convey"
  19. "gopkg.in/macaron.v1"
  20. )
  21. func TestMiddlewareContext(t *testing.T) {
  22. setting.ERR_TEMPLATE_NAME = "error-template"
  23. Convey("Given the grafana middleware", t, func() {
  24. middlewareScenario(t, "middleware should add context to injector", func(sc *scenarioContext) {
  25. sc.fakeReq("GET", "/").exec()
  26. So(sc.context, ShouldNotBeNil)
  27. })
  28. middlewareScenario(t, "Default middleware should allow get request", func(sc *scenarioContext) {
  29. sc.fakeReq("GET", "/").exec()
  30. So(sc.resp.Code, ShouldEqual, 200)
  31. })
  32. middlewareScenario(t, "middleware should add Cache-Control header for GET requests to API", func(sc *scenarioContext) {
  33. sc.fakeReq("GET", "/api/search").exec()
  34. So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
  35. So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
  36. So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
  37. })
  38. middlewareScenario(t, "middleware should not add Cache-Control header to for non-API GET requests", func(sc *scenarioContext) {
  39. sc.fakeReq("GET", "/").exec()
  40. So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty)
  41. })
  42. middlewareScenario(t, "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(t, "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(t, "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(t, "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(t, "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(t, "Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) {
  123. sc.withTokenSessionCookie("token")
  124. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  125. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  126. return nil
  127. })
  128. sc.userAuthTokenService.LookupTokenProvider = func(unhashedToken string) (*m.UserToken, error) {
  129. return &m.UserToken{
  130. UserId: 12,
  131. UnhashedToken: unhashedToken,
  132. }, nil
  133. }
  134. sc.fakeReq("GET", "/").exec()
  135. Convey("should init context with user info", func() {
  136. So(sc.context.IsSignedIn, ShouldBeTrue)
  137. So(sc.context.UserId, ShouldEqual, 12)
  138. So(sc.context.UserToken.UserId, ShouldEqual, 12)
  139. So(sc.context.UserToken.UnhashedToken, ShouldEqual, "token")
  140. })
  141. Convey("should not set cookie", func() {
  142. So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, "")
  143. })
  144. })
  145. middlewareScenario(t, "Non-expired auth token in cookie which are being rotated", func(sc *scenarioContext) {
  146. sc.withTokenSessionCookie("token")
  147. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  148. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  149. return nil
  150. })
  151. sc.userAuthTokenService.LookupTokenProvider = func(unhashedToken string) (*m.UserToken, error) {
  152. return &m.UserToken{
  153. UserId: 12,
  154. UnhashedToken: "",
  155. }, nil
  156. }
  157. sc.userAuthTokenService.TryRotateTokenProvider = func(userToken *m.UserToken, clientIP, userAgent string) (bool, error) {
  158. userToken.UnhashedToken = "rotated"
  159. return true, nil
  160. }
  161. maxAgeHours := (time.Duration(setting.LoginMaxLifetimeDays) * 24 * time.Hour)
  162. maxAge := (maxAgeHours + time.Hour).Seconds()
  163. expectedCookie := &http.Cookie{
  164. Name: setting.LoginCookieName,
  165. Value: "rotated",
  166. Path: setting.AppSubUrl + "/",
  167. HttpOnly: true,
  168. MaxAge: int(maxAge),
  169. Secure: setting.CookieSecure,
  170. SameSite: setting.CookieSameSite,
  171. }
  172. sc.fakeReq("GET", "/").exec()
  173. Convey("should init context with user info", func() {
  174. So(sc.context.IsSignedIn, ShouldBeTrue)
  175. So(sc.context.UserId, ShouldEqual, 12)
  176. So(sc.context.UserToken.UserId, ShouldEqual, 12)
  177. So(sc.context.UserToken.UnhashedToken, ShouldEqual, "rotated")
  178. })
  179. Convey("should set cookie", func() {
  180. So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
  181. })
  182. })
  183. middlewareScenario(t, "Invalid/expired auth token in cookie", func(sc *scenarioContext) {
  184. sc.withTokenSessionCookie("token")
  185. sc.userAuthTokenService.LookupTokenProvider = func(unhashedToken string) (*m.UserToken, error) {
  186. return nil, m.ErrUserTokenNotFound
  187. }
  188. sc.fakeReq("GET", "/").exec()
  189. Convey("should not init context with user info", func() {
  190. So(sc.context.IsSignedIn, ShouldBeFalse)
  191. So(sc.context.UserId, ShouldEqual, 0)
  192. So(sc.context.UserToken, ShouldBeNil)
  193. })
  194. })
  195. middlewareScenario(t, "When anonymous access is enabled", func(sc *scenarioContext) {
  196. setting.AnonymousEnabled = true
  197. setting.AnonymousOrgName = "test"
  198. setting.AnonymousOrgRole = string(m.ROLE_EDITOR)
  199. bus.AddHandler("test", func(query *m.GetOrgByNameQuery) error {
  200. So(query.Name, ShouldEqual, "test")
  201. query.Result = &m.Org{Id: 2, Name: "test"}
  202. return nil
  203. })
  204. sc.fakeReq("GET", "/").exec()
  205. Convey("should init context with org info", func() {
  206. So(sc.context.UserId, ShouldEqual, 0)
  207. So(sc.context.OrgId, ShouldEqual, 2)
  208. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  209. })
  210. Convey("context signed in should be false", func() {
  211. So(sc.context.IsSignedIn, ShouldBeFalse)
  212. })
  213. })
  214. Convey("auth_proxy", func() {
  215. setting.AuthProxyEnabled = true
  216. setting.AuthProxyWhitelist = ""
  217. setting.AuthProxyAutoSignUp = true
  218. setting.LdapEnabled = true
  219. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  220. setting.AuthProxyHeaderProperty = "username"
  221. name := "markelog"
  222. middlewareScenario(t, "should sync the user if it's not in the cache", func(sc *scenarioContext) {
  223. called := false
  224. syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
  225. called = true
  226. query.User = &m.User{Id: 32}
  227. return nil
  228. }
  229. bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
  230. query.Result = &m.User{Id: 32}
  231. return nil
  232. })
  233. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  234. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  235. return nil
  236. })
  237. sc.fakeReq("GET", "/")
  238. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  239. sc.exec()
  240. Convey("Should init user via ldap", func() {
  241. So(called, ShouldBeTrue)
  242. So(sc.context.IsSignedIn, ShouldBeTrue)
  243. So(sc.context.UserId, ShouldEqual, 32)
  244. So(sc.context.OrgId, ShouldEqual, 4)
  245. })
  246. })
  247. middlewareScenario(t, "should not sync the user if it's in the cache", func(sc *scenarioContext) {
  248. called := false
  249. syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
  250. called = true
  251. query.User = &m.User{Id: 32}
  252. return nil
  253. }
  254. bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
  255. query.Result = &m.User{Id: 32}
  256. return nil
  257. })
  258. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  259. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  260. return nil
  261. })
  262. key := fmt.Sprintf(cachePrefix, name)
  263. sc.remoteCacheService.Set(key, int64(33), 0)
  264. sc.fakeReq("GET", "/")
  265. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  266. sc.exec()
  267. cacheValue, cacheErr := sc.remoteCacheService.Get(key)
  268. Convey("Should init user via cache", func() {
  269. So(called, ShouldBeFalse)
  270. So(sc.context.IsSignedIn, ShouldBeTrue)
  271. So(sc.context.UserId, ShouldEqual, 32)
  272. So(sc.context.OrgId, ShouldEqual, 4)
  273. So(cacheValue, ShouldEqual, 33)
  274. So(cacheErr, ShouldBeNil)
  275. })
  276. })
  277. middlewareScenario(t, "should create an user from a header", func(sc *scenarioContext) {
  278. setting.LdapEnabled = false
  279. setting.AuthProxyAutoSignUp = true
  280. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  281. if query.UserId > 0 {
  282. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  283. return nil
  284. }
  285. return m.ErrUserNotFound
  286. })
  287. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  288. cmd.Result = &m.User{Id: 33}
  289. return nil
  290. })
  291. sc.fakeReq("GET", "/")
  292. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  293. sc.exec()
  294. Convey("Should create user from header info", func() {
  295. So(sc.context.IsSignedIn, ShouldBeTrue)
  296. So(sc.context.UserId, ShouldEqual, 33)
  297. So(sc.context.OrgId, ShouldEqual, 4)
  298. })
  299. })
  300. middlewareScenario(t, "should get an existing user from header", func(sc *scenarioContext) {
  301. setting.LdapEnabled = false
  302. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  303. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  304. return nil
  305. })
  306. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  307. cmd.Result = &m.User{Id: 12}
  308. return nil
  309. })
  310. sc.fakeReq("GET", "/")
  311. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  312. sc.exec()
  313. Convey("should init context with user info", func() {
  314. So(sc.context.IsSignedIn, ShouldBeTrue)
  315. So(sc.context.UserId, ShouldEqual, 12)
  316. So(sc.context.OrgId, ShouldEqual, 2)
  317. })
  318. })
  319. middlewareScenario(t, "should allow the request from whitelist IP", func(sc *scenarioContext) {
  320. setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
  321. setting.LdapEnabled = false
  322. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  323. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  324. return nil
  325. })
  326. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  327. cmd.Result = &m.User{Id: 33}
  328. return nil
  329. })
  330. sc.fakeReq("GET", "/")
  331. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  332. sc.req.RemoteAddr = "[2001::23]:12345"
  333. sc.exec()
  334. Convey("Should init context with user info", func() {
  335. So(sc.context.IsSignedIn, ShouldBeTrue)
  336. So(sc.context.UserId, ShouldEqual, 33)
  337. So(sc.context.OrgId, ShouldEqual, 4)
  338. })
  339. })
  340. middlewareScenario(t, "should not allow the request from whitelist IP", func(sc *scenarioContext) {
  341. setting.AuthProxyWhitelist = "8.8.8.8"
  342. setting.LdapEnabled = false
  343. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  344. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  345. return nil
  346. })
  347. bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
  348. cmd.Result = &m.User{Id: 33}
  349. return nil
  350. })
  351. sc.fakeReq("GET", "/")
  352. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  353. sc.req.RemoteAddr = "[2001::23]:12345"
  354. sc.exec()
  355. Convey("should return 407 status code", func() {
  356. So(sc.resp.Code, ShouldEqual, 407)
  357. So(sc.context, ShouldBeNil)
  358. })
  359. })
  360. })
  361. })
  362. }
  363. func middlewareScenario(t *testing.T, desc string, fn scenarioFunc) {
  364. Convey(desc, func() {
  365. defer bus.ClearBusHandlers()
  366. setting.LoginCookieName = "grafana_session"
  367. setting.LoginMaxLifetimeDays = 30
  368. sc := &scenarioContext{}
  369. viewsPath, _ := filepath.Abs("../../public/views")
  370. sc.m = macaron.New()
  371. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  372. Directory: viewsPath,
  373. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  374. }))
  375. sc.userAuthTokenService = auth.NewFakeUserAuthTokenService()
  376. sc.remoteCacheService = remotecache.NewFakeStore(t)
  377. sc.m.Use(GetContextHandler(sc.userAuthTokenService, sc.remoteCacheService))
  378. // mock out gc goroutine
  379. session.StartSessionGC = func() {}
  380. setting.SessionOptions = msession.Options{}
  381. sc.m.Use(OrgRedirect())
  382. sc.m.Use(AddDefaultResponseHeaders())
  383. sc.defaultHandler = func(c *m.ReqContext) {
  384. sc.context = c
  385. if sc.handlerFunc != nil {
  386. sc.handlerFunc(sc.context)
  387. }
  388. }
  389. sc.m.Get("/", sc.defaultHandler)
  390. fn(sc)
  391. })
  392. }
  393. type scenarioContext struct {
  394. m *macaron.Macaron
  395. context *m.ReqContext
  396. resp *httptest.ResponseRecorder
  397. apiKey string
  398. authHeader string
  399. tokenSessionCookie string
  400. respJson map[string]interface{}
  401. handlerFunc handlerFunc
  402. defaultHandler macaron.Handler
  403. url string
  404. userAuthTokenService *auth.FakeUserAuthTokenService
  405. remoteCacheService *remotecache.RemoteCache
  406. req *http.Request
  407. }
  408. func (sc *scenarioContext) withValidApiKey() *scenarioContext {
  409. sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9"
  410. return sc
  411. }
  412. func (sc *scenarioContext) withTokenSessionCookie(unhashedToken string) *scenarioContext {
  413. sc.tokenSessionCookie = unhashedToken
  414. return sc
  415. }
  416. func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
  417. sc.authHeader = authHeader
  418. return sc
  419. }
  420. func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
  421. sc.resp = httptest.NewRecorder()
  422. req, err := http.NewRequest(method, url, nil)
  423. So(err, ShouldBeNil)
  424. sc.req = req
  425. return sc
  426. }
  427. func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map[string]string) *scenarioContext {
  428. sc.resp = httptest.NewRecorder()
  429. req, err := http.NewRequest(method, url, nil)
  430. q := req.URL.Query()
  431. for k, v := range queryParams {
  432. q.Add(k, v)
  433. }
  434. req.URL.RawQuery = q.Encode()
  435. So(err, ShouldBeNil)
  436. sc.req = req
  437. return sc
  438. }
  439. func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
  440. sc.handlerFunc = fn
  441. return sc
  442. }
  443. func (sc *scenarioContext) exec() {
  444. if sc.apiKey != "" {
  445. sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey)
  446. }
  447. if sc.authHeader != "" {
  448. sc.req.Header.Add("Authorization", sc.authHeader)
  449. }
  450. if sc.tokenSessionCookie != "" {
  451. sc.req.AddCookie(&http.Cookie{
  452. Name: setting.LoginCookieName,
  453. Value: sc.tokenSessionCookie,
  454. })
  455. }
  456. sc.m.ServeHTTP(sc.resp, sc.req)
  457. if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
  458. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  459. So(err, ShouldBeNil)
  460. }
  461. }
  462. type scenarioFunc func(c *scenarioContext)
  463. type handlerFunc func(c *m.ReqContext)