middleware_test.go 15 KB

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