middleware_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. package middleware
  2. import (
  3. "context"
  4. "encoding/base32"
  5. "encoding/json"
  6. "fmt"
  7. "net/http"
  8. "net/http/httptest"
  9. "path/filepath"
  10. "testing"
  11. "time"
  12. . "github.com/smartystreets/goconvey/convey"
  13. "github.com/stretchr/testify/assert"
  14. "gopkg.in/macaron.v1"
  15. "github.com/grafana/grafana/pkg/api/dtos"
  16. "github.com/grafana/grafana/pkg/bus"
  17. "github.com/grafana/grafana/pkg/infra/remotecache"
  18. "github.com/grafana/grafana/pkg/models"
  19. "github.com/grafana/grafana/pkg/services/auth"
  20. "github.com/grafana/grafana/pkg/services/login"
  21. "github.com/grafana/grafana/pkg/setting"
  22. "github.com/grafana/grafana/pkg/util"
  23. )
  24. const errorTemplate = "error-template"
  25. func mockGetTime() {
  26. var timeSeed int64
  27. getTime = func() time.Time {
  28. fakeNow := time.Unix(timeSeed, 0)
  29. timeSeed++
  30. return fakeNow
  31. }
  32. }
  33. func resetGetTime() {
  34. getTime = time.Now
  35. }
  36. func TestMiddleWareSecurityHeaders(t *testing.T) {
  37. setting.ERR_TEMPLATE_NAME = errorTemplate
  38. Convey("Given the grafana middleware", t, func() {
  39. middlewareScenario(t, "middleware should get correct x-xss-protection header", func(sc *scenarioContext) {
  40. setting.XSSProtectionHeader = true
  41. sc.fakeReq("GET", "/api/").exec()
  42. So(sc.resp.Header().Get("X-XSS-Protection"), ShouldEqual, "1; mode=block")
  43. })
  44. middlewareScenario(t, "middleware should not get x-xss-protection when disabled", func(sc *scenarioContext) {
  45. setting.XSSProtectionHeader = false
  46. sc.fakeReq("GET", "/api/").exec()
  47. So(sc.resp.Header().Get("X-XSS-Protection"), ShouldBeEmpty)
  48. })
  49. middlewareScenario(t, "middleware should add correct Strict-Transport-Security header", func(sc *scenarioContext) {
  50. setting.StrictTransportSecurity = true
  51. setting.Protocol = setting.HTTPS
  52. setting.StrictTransportSecurityMaxAge = 64000
  53. sc.fakeReq("GET", "/api/").exec()
  54. So(sc.resp.Header().Get("Strict-Transport-Security"), ShouldEqual, "max-age=64000")
  55. setting.StrictTransportSecurityPreload = true
  56. sc.fakeReq("GET", "/api/").exec()
  57. So(sc.resp.Header().Get("Strict-Transport-Security"), ShouldEqual, "max-age=64000; preload")
  58. setting.StrictTransportSecuritySubDomains = true
  59. sc.fakeReq("GET", "/api/").exec()
  60. So(sc.resp.Header().Get("Strict-Transport-Security"), ShouldEqual, "max-age=64000; preload; includeSubDomains")
  61. })
  62. })
  63. }
  64. func TestMiddlewareContext(t *testing.T) {
  65. setting.ERR_TEMPLATE_NAME = errorTemplate
  66. Convey("Given the grafana middleware", t, func() {
  67. middlewareScenario(t, "middleware should add context to injector", func(sc *scenarioContext) {
  68. sc.fakeReq("GET", "/").exec()
  69. So(sc.context, ShouldNotBeNil)
  70. })
  71. middlewareScenario(t, "Default middleware should allow get request", func(sc *scenarioContext) {
  72. sc.fakeReq("GET", "/").exec()
  73. So(sc.resp.Code, ShouldEqual, 200)
  74. })
  75. middlewareScenario(t, "middleware should add Cache-Control header for requests to API", func(sc *scenarioContext) {
  76. sc.fakeReq("GET", "/api/search").exec()
  77. So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
  78. So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
  79. So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
  80. })
  81. middlewareScenario(t, "middleware should not add Cache-Control header for requests to datasource proxy API", func(sc *scenarioContext) {
  82. sc.fakeReq("GET", "/api/datasources/proxy/1/test").exec()
  83. So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty)
  84. So(sc.resp.Header().Get("Pragma"), ShouldBeEmpty)
  85. So(sc.resp.Header().Get("Expires"), ShouldBeEmpty)
  86. })
  87. middlewareScenario(t, "middleware should add Cache-Control header for requests with html response", func(sc *scenarioContext) {
  88. sc.handler(func(c *models.ReqContext) {
  89. data := &dtos.IndexViewData{
  90. User: &dtos.CurrentUser{},
  91. Settings: map[string]interface{}{},
  92. NavTree: []*dtos.NavLink{},
  93. }
  94. c.HTML(200, "index-template", data)
  95. })
  96. sc.fakeReq("GET", "/").exec()
  97. So(sc.resp.Code, ShouldEqual, 200)
  98. So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
  99. So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
  100. So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
  101. })
  102. middlewareScenario(t, "middleware should add X-Frame-Options header with deny for request when not allowing embedding", func(sc *scenarioContext) {
  103. sc.fakeReq("GET", "/api/search").exec()
  104. So(sc.resp.Header().Get("X-Frame-Options"), ShouldEqual, "deny")
  105. })
  106. middlewareScenario(t, "middleware should not add X-Frame-Options header for request when allowing embedding", func(sc *scenarioContext) {
  107. setting.AllowEmbedding = true
  108. sc.fakeReq("GET", "/api/search").exec()
  109. So(sc.resp.Header().Get("X-Frame-Options"), ShouldBeEmpty)
  110. })
  111. middlewareScenario(t, "Invalid api key", func(sc *scenarioContext) {
  112. sc.apiKey = "invalid_key_test"
  113. sc.fakeReq("GET", "/").exec()
  114. Convey("Should not init session", func() {
  115. So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty)
  116. })
  117. Convey("Should return 401", func() {
  118. So(sc.resp.Code, ShouldEqual, 401)
  119. So(sc.respJson["message"], ShouldEqual, errStringInvalidAPIKey)
  120. })
  121. })
  122. middlewareScenario(t, "Valid api key", func(sc *scenarioContext) {
  123. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  124. bus.AddHandler("test", func(query *models.GetApiKeyByNameQuery) error {
  125. query.Result = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash}
  126. return nil
  127. })
  128. sc.fakeReq("GET", "/").withValidApiKey().exec()
  129. Convey("Should return 200", func() {
  130. So(sc.resp.Code, ShouldEqual, 200)
  131. })
  132. Convey("Should init middleware context", func() {
  133. So(sc.context.IsSignedIn, ShouldEqual, true)
  134. So(sc.context.OrgId, ShouldEqual, 12)
  135. So(sc.context.OrgRole, ShouldEqual, models.ROLE_EDITOR)
  136. })
  137. })
  138. middlewareScenario(t, "Valid api key, but does not match db hash", func(sc *scenarioContext) {
  139. keyhash := "Something_not_matching"
  140. bus.AddHandler("test", func(query *models.GetApiKeyByNameQuery) error {
  141. query.Result = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash}
  142. return nil
  143. })
  144. sc.fakeReq("GET", "/").withValidApiKey().exec()
  145. Convey("Should return api key invalid", func() {
  146. So(sc.resp.Code, ShouldEqual, 401)
  147. So(sc.respJson["message"], ShouldEqual, errStringInvalidAPIKey)
  148. })
  149. })
  150. middlewareScenario(t, "Valid api key, but expired", func(sc *scenarioContext) {
  151. mockGetTime()
  152. defer resetGetTime()
  153. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  154. bus.AddHandler("test", func(query *models.GetApiKeyByNameQuery) error {
  155. // api key expired one second before
  156. expires := getTime().Add(-1 * time.Second).Unix()
  157. query.Result = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash,
  158. Expires: &expires}
  159. return nil
  160. })
  161. sc.fakeReq("GET", "/").withValidApiKey().exec()
  162. Convey("Should return 401", func() {
  163. So(sc.resp.Code, ShouldEqual, 401)
  164. So(sc.respJson["message"], ShouldEqual, "Expired API key")
  165. })
  166. })
  167. middlewareScenario(t, "Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) {
  168. sc.withTokenSessionCookie("token")
  169. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  170. query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
  171. return nil
  172. })
  173. sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
  174. return &models.UserToken{
  175. UserId: 12,
  176. UnhashedToken: unhashedToken,
  177. }, nil
  178. }
  179. sc.fakeReq("GET", "/").exec()
  180. Convey("Should init context with user info", func() {
  181. So(sc.context.IsSignedIn, ShouldBeTrue)
  182. So(sc.context.UserId, ShouldEqual, 12)
  183. So(sc.context.UserToken.UserId, ShouldEqual, 12)
  184. So(sc.context.UserToken.UnhashedToken, ShouldEqual, "token")
  185. })
  186. Convey("Should not set cookie", func() {
  187. So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, "")
  188. })
  189. })
  190. middlewareScenario(t, "Non-expired auth token in cookie which are being rotated", func(sc *scenarioContext) {
  191. sc.withTokenSessionCookie("token")
  192. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  193. query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
  194. return nil
  195. })
  196. sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
  197. return &models.UserToken{
  198. UserId: 12,
  199. UnhashedToken: "",
  200. }, nil
  201. }
  202. sc.userAuthTokenService.TryRotateTokenProvider = func(ctx context.Context, userToken *models.UserToken, clientIP, userAgent string) (bool, error) {
  203. userToken.UnhashedToken = "rotated"
  204. return true, nil
  205. }
  206. maxAgeHours := (time.Duration(setting.LoginMaxLifetimeDays) * 24 * time.Hour)
  207. maxAge := (maxAgeHours + time.Hour).Seconds()
  208. expectedCookie := &http.Cookie{
  209. Name: setting.LoginCookieName,
  210. Value: "rotated",
  211. Path: setting.AppSubUrl + "/",
  212. HttpOnly: true,
  213. MaxAge: int(maxAge),
  214. Secure: setting.CookieSecure,
  215. SameSite: setting.CookieSameSite,
  216. }
  217. sc.fakeReq("GET", "/").exec()
  218. Convey("Should init context with user info", func() {
  219. So(sc.context.IsSignedIn, ShouldBeTrue)
  220. So(sc.context.UserId, ShouldEqual, 12)
  221. So(sc.context.UserToken.UserId, ShouldEqual, 12)
  222. So(sc.context.UserToken.UnhashedToken, ShouldEqual, "rotated")
  223. })
  224. Convey("Should set cookie", func() {
  225. So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
  226. })
  227. })
  228. middlewareScenario(t, "Invalid/expired auth token in cookie", func(sc *scenarioContext) {
  229. sc.withTokenSessionCookie("token")
  230. sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
  231. return nil, models.ErrUserTokenNotFound
  232. }
  233. sc.fakeReq("GET", "/").exec()
  234. Convey("Should not init context with user info", func() {
  235. So(sc.context.IsSignedIn, ShouldBeFalse)
  236. So(sc.context.UserId, ShouldEqual, 0)
  237. So(sc.context.UserToken, ShouldBeNil)
  238. })
  239. })
  240. middlewareScenario(t, "When anonymous access is enabled", func(sc *scenarioContext) {
  241. setting.AnonymousEnabled = true
  242. setting.AnonymousOrgName = "test"
  243. setting.AnonymousOrgRole = string(models.ROLE_EDITOR)
  244. bus.AddHandler("test", func(query *models.GetOrgByNameQuery) error {
  245. So(query.Name, ShouldEqual, "test")
  246. query.Result = &models.Org{Id: 2, Name: "test"}
  247. return nil
  248. })
  249. sc.fakeReq("GET", "/").exec()
  250. Convey("Should init context with org info", func() {
  251. So(sc.context.UserId, ShouldEqual, 0)
  252. So(sc.context.OrgId, ShouldEqual, 2)
  253. So(sc.context.OrgRole, ShouldEqual, models.ROLE_EDITOR)
  254. })
  255. Convey("context signed in should be false", func() {
  256. So(sc.context.IsSignedIn, ShouldBeFalse)
  257. })
  258. })
  259. Convey("auth_proxy", func() {
  260. setting.AuthProxyEnabled = true
  261. setting.AuthProxyWhitelist = ""
  262. setting.AuthProxyAutoSignUp = true
  263. setting.LDAPEnabled = true
  264. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  265. setting.AuthProxyHeaderProperty = "username"
  266. setting.AuthProxyHeaders = map[string]string{"Groups": "X-WEBAUTH-GROUPS"}
  267. name := "markelog"
  268. group := "grafana-core-team"
  269. middlewareScenario(t, "Should not sync the user if it's in the cache", func(sc *scenarioContext) {
  270. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  271. query.Result = &models.SignedInUser{OrgId: 4, UserId: query.UserId}
  272. return nil
  273. })
  274. key := fmt.Sprintf(cachePrefix, base32.StdEncoding.EncodeToString([]byte(name+"-"+group)))
  275. sc.remoteCacheService.Set(key, int64(33), 0)
  276. sc.fakeReq("GET", "/")
  277. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  278. sc.req.Header.Add("X-WEBAUTH-GROUPS", group)
  279. sc.exec()
  280. Convey("Should init user via cache", func() {
  281. So(sc.context.IsSignedIn, ShouldBeTrue)
  282. So(sc.context.UserId, ShouldEqual, 33)
  283. So(sc.context.OrgId, ShouldEqual, 4)
  284. })
  285. })
  286. middlewareScenario(t, "Should respect auto signup option", func(sc *scenarioContext) {
  287. setting.LDAPEnabled = false
  288. setting.AuthProxyAutoSignUp = false
  289. var actualAuthProxyAutoSignUp *bool = nil
  290. bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
  291. actualAuthProxyAutoSignUp = &cmd.SignupAllowed
  292. return login.ErrInvalidCredentials
  293. })
  294. sc.fakeReq("GET", "/")
  295. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  296. sc.exec()
  297. assert.False(t, *actualAuthProxyAutoSignUp)
  298. assert.Equal(t, sc.resp.Code, 500)
  299. assert.Nil(t, sc.context)
  300. })
  301. middlewareScenario(t, "Should create an user from a header", func(sc *scenarioContext) {
  302. setting.LDAPEnabled = false
  303. setting.AuthProxyAutoSignUp = true
  304. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  305. if query.UserId > 0 {
  306. query.Result = &models.SignedInUser{OrgId: 4, UserId: 33}
  307. return nil
  308. }
  309. return models.ErrUserNotFound
  310. })
  311. bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
  312. cmd.Result = &models.User{Id: 33}
  313. return nil
  314. })
  315. sc.fakeReq("GET", "/")
  316. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  317. sc.exec()
  318. Convey("Should create user from header info", func() {
  319. So(sc.context.IsSignedIn, ShouldBeTrue)
  320. So(sc.context.UserId, ShouldEqual, 33)
  321. So(sc.context.OrgId, ShouldEqual, 4)
  322. })
  323. })
  324. middlewareScenario(t, "Should get an existing user from header", func(sc *scenarioContext) {
  325. setting.LDAPEnabled = false
  326. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  327. query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
  328. return nil
  329. })
  330. bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
  331. cmd.Result = &models.User{Id: 12}
  332. return nil
  333. })
  334. sc.fakeReq("GET", "/")
  335. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  336. sc.exec()
  337. Convey("Should init context with user info", func() {
  338. So(sc.context.IsSignedIn, ShouldBeTrue)
  339. So(sc.context.UserId, ShouldEqual, 12)
  340. So(sc.context.OrgId, ShouldEqual, 2)
  341. })
  342. })
  343. middlewareScenario(t, "Should allow the request from whitelist IP", func(sc *scenarioContext) {
  344. setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
  345. setting.LDAPEnabled = false
  346. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  347. query.Result = &models.SignedInUser{OrgId: 4, UserId: 33}
  348. return nil
  349. })
  350. bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
  351. cmd.Result = &models.User{Id: 33}
  352. return nil
  353. })
  354. sc.fakeReq("GET", "/")
  355. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  356. sc.req.RemoteAddr = "[2001::23]:12345"
  357. sc.exec()
  358. Convey("Should init context with user info", func() {
  359. So(sc.context.IsSignedIn, ShouldBeTrue)
  360. So(sc.context.UserId, ShouldEqual, 33)
  361. So(sc.context.OrgId, ShouldEqual, 4)
  362. })
  363. })
  364. middlewareScenario(t, "Should not allow the request from whitelist IP", func(sc *scenarioContext) {
  365. setting.AuthProxyWhitelist = "8.8.8.8"
  366. setting.LDAPEnabled = false
  367. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  368. query.Result = &models.SignedInUser{OrgId: 4, UserId: 33}
  369. return nil
  370. })
  371. bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
  372. cmd.Result = &models.User{Id: 33}
  373. return nil
  374. })
  375. sc.fakeReq("GET", "/")
  376. sc.req.Header.Add(setting.AuthProxyHeaderName, name)
  377. sc.req.RemoteAddr = "[2001::23]:12345"
  378. sc.exec()
  379. Convey("Should return 407 status code", func() {
  380. So(sc.resp.Code, ShouldEqual, 407)
  381. So(sc.context, ShouldBeNil)
  382. })
  383. })
  384. })
  385. })
  386. }
  387. func TestMiddlewareBasicAuth(t *testing.T) {
  388. Convey("Given the basic auth", t, func() {
  389. old := setting.BasicAuthEnabled
  390. Convey("Setup", func() {
  391. setting.BasicAuthEnabled = true
  392. })
  393. middlewareScenario(t, "Valid API key", func(sc *scenarioContext) {
  394. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  395. bus.AddHandler("test", func(query *models.GetApiKeyByNameQuery) error {
  396. query.Result = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash}
  397. return nil
  398. })
  399. authHeader := util.GetBasicAuthHeader("api_key", "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9")
  400. sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
  401. Convey("Should return 200", func() {
  402. So(sc.resp.Code, ShouldEqual, 200)
  403. })
  404. Convey("Should init middleware context", func() {
  405. So(sc.context.IsSignedIn, ShouldEqual, true)
  406. So(sc.context.OrgId, ShouldEqual, 12)
  407. So(sc.context.OrgRole, ShouldEqual, models.ROLE_EDITOR)
  408. })
  409. })
  410. middlewareScenario(t, "Handle auth", func(sc *scenarioContext) {
  411. bus.AddHandler("test", func(query *models.GetUserByLoginQuery) error {
  412. query.Result = &models.User{
  413. Password: util.EncodePassword("myPass", "Salt"),
  414. Salt: "Salt",
  415. }
  416. return nil
  417. })
  418. bus.AddHandler("test", func(loginUserQuery *models.LoginUserQuery) error {
  419. return nil
  420. })
  421. bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
  422. query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
  423. return nil
  424. })
  425. authHeader := util.GetBasicAuthHeader("myUser", "myPass")
  426. sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
  427. Convey("Should init middleware context with user", func() {
  428. So(sc.context.IsSignedIn, ShouldEqual, true)
  429. So(sc.context.OrgId, ShouldEqual, 2)
  430. So(sc.context.UserId, ShouldEqual, 12)
  431. })
  432. })
  433. middlewareScenario(t, "Should return error if user is not found", func(sc *scenarioContext) {
  434. sc.fakeReqWithBasicAuth("GET", "/", "test", "test").exec()
  435. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  436. So(err, ShouldNotBeNil)
  437. So(sc.resp.Code, ShouldEqual, 401)
  438. So(sc.respJson["message"], ShouldEqual, errStringInvalidUsernamePassword)
  439. })
  440. middlewareScenario(t, "Should return error if user & password do not match", func(sc *scenarioContext) {
  441. bus.AddHandler("test", func(loginUserQuery *models.GetUserByLoginQuery) error {
  442. return nil
  443. })
  444. sc.fakeReqWithBasicAuth("GET", "/", "test", "test").exec()
  445. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  446. So(err, ShouldNotBeNil)
  447. So(sc.resp.Code, ShouldEqual, 401)
  448. So(sc.respJson["message"], ShouldEqual, errStringInvalidUsernamePassword)
  449. })
  450. Convey("Destroy", func() {
  451. setting.BasicAuthEnabled = old
  452. })
  453. })
  454. }
  455. func middlewareScenario(t *testing.T, desc string, fn scenarioFunc) {
  456. Convey(desc, func() {
  457. defer bus.ClearBusHandlers()
  458. setting.LoginCookieName = "grafana_session"
  459. setting.LoginMaxLifetimeDays = 30
  460. sc := &scenarioContext{}
  461. viewsPath, _ := filepath.Abs("../../public/views")
  462. sc.m = macaron.New()
  463. sc.m.Use(AddDefaultResponseHeaders())
  464. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  465. Directory: viewsPath,
  466. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  467. }))
  468. sc.userAuthTokenService = auth.NewFakeUserAuthTokenService()
  469. sc.remoteCacheService = remotecache.NewFakeStore(t)
  470. sc.m.Use(GetContextHandler(sc.userAuthTokenService, sc.remoteCacheService))
  471. sc.m.Use(OrgRedirect())
  472. sc.defaultHandler = func(c *models.ReqContext) {
  473. sc.context = c
  474. if sc.handlerFunc != nil {
  475. sc.handlerFunc(sc.context)
  476. }
  477. }
  478. sc.m.Get("/", sc.defaultHandler)
  479. fn(sc)
  480. })
  481. }
  482. type scenarioContext struct {
  483. m *macaron.Macaron
  484. context *models.ReqContext
  485. resp *httptest.ResponseRecorder
  486. apiKey string
  487. authHeader string
  488. tokenSessionCookie string
  489. respJson map[string]interface{}
  490. handlerFunc handlerFunc
  491. defaultHandler macaron.Handler
  492. url string
  493. userAuthTokenService *auth.FakeUserAuthTokenService
  494. remoteCacheService *remotecache.RemoteCache
  495. req *http.Request
  496. }
  497. func (sc *scenarioContext) withValidApiKey() *scenarioContext {
  498. sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9"
  499. return sc
  500. }
  501. func (sc *scenarioContext) withTokenSessionCookie(unhashedToken string) *scenarioContext {
  502. sc.tokenSessionCookie = unhashedToken
  503. return sc
  504. }
  505. func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
  506. sc.authHeader = authHeader
  507. return sc
  508. }
  509. func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
  510. sc.resp = httptest.NewRecorder()
  511. req, err := http.NewRequest(method, url, nil)
  512. So(err, ShouldBeNil)
  513. sc.req = req
  514. return sc
  515. }
  516. func (sc *scenarioContext) fakeReqWithBasicAuth(method, url, user, password string) *scenarioContext {
  517. sc.resp = httptest.NewRecorder()
  518. req, err := http.NewRequest(method, url, nil)
  519. req.SetBasicAuth(user, password)
  520. So(err, ShouldBeNil)
  521. sc.req = req
  522. return sc
  523. }
  524. func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map[string]string) *scenarioContext {
  525. sc.resp = httptest.NewRecorder()
  526. req, err := http.NewRequest(method, url, nil)
  527. q := req.URL.Query()
  528. for k, v := range queryParams {
  529. q.Add(k, v)
  530. }
  531. req.URL.RawQuery = q.Encode()
  532. So(err, ShouldBeNil)
  533. sc.req = req
  534. return sc
  535. }
  536. func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
  537. sc.handlerFunc = fn
  538. return sc
  539. }
  540. func (sc *scenarioContext) exec() {
  541. if sc.apiKey != "" {
  542. sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey)
  543. }
  544. if sc.authHeader != "" {
  545. sc.req.Header.Add("Authorization", sc.authHeader)
  546. }
  547. if sc.tokenSessionCookie != "" {
  548. sc.req.AddCookie(&http.Cookie{
  549. Name: setting.LoginCookieName,
  550. Value: sc.tokenSessionCookie,
  551. })
  552. }
  553. sc.m.ServeHTTP(sc.resp, sc.req)
  554. if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
  555. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  556. So(err, ShouldBeNil)
  557. }
  558. }
  559. type scenarioFunc func(c *scenarioContext)
  560. type handlerFunc func(c *models.ReqContext)