middleware_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. package middleware
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "path/filepath"
  7. "testing"
  8. "github.com/go-macaron/session"
  9. "github.com/grafana/grafana/pkg/bus"
  10. l "github.com/grafana/grafana/pkg/login"
  11. m "github.com/grafana/grafana/pkg/models"
  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("Non api request should init session", func(sc *scenarioContext) {
  28. sc.fakeReq("GET", "/").exec()
  29. So(sc.resp.Header().Get("Set-Cookie"), ShouldContainSubstring, "grafana_sess")
  30. })
  31. middlewareScenario("Invalid api key", func(sc *scenarioContext) {
  32. sc.apiKey = "invalid_key_test"
  33. sc.fakeReq("GET", "/").exec()
  34. Convey("Should not init session", func() {
  35. So(sc.resp.Header().Get("Set-Cookie"), ShouldBeEmpty)
  36. })
  37. Convey("Should return 401", func() {
  38. So(sc.resp.Code, ShouldEqual, 401)
  39. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  40. })
  41. })
  42. middlewareScenario("Using basic auth", func(sc *scenarioContext) {
  43. bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error {
  44. query.Result = &m.User{
  45. Password: util.EncodePassword("myPass", "salt"),
  46. Salt: "salt",
  47. }
  48. return nil
  49. })
  50. bus.AddHandler("test", func(loginUserQuery *l.LoginUserQuery) error {
  51. return nil
  52. })
  53. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  54. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  55. return nil
  56. })
  57. setting.BasicAuthEnabled = true
  58. authHeader := util.GetBasicAuthHeader("myUser", "myPass")
  59. sc.fakeReq("GET", "/").withAuthoriziationHeader(authHeader).exec()
  60. Convey("Should init middleware context with user", func() {
  61. So(sc.context.IsSignedIn, ShouldEqual, true)
  62. So(sc.context.OrgId, ShouldEqual, 2)
  63. So(sc.context.UserId, ShouldEqual, 12)
  64. })
  65. })
  66. middlewareScenario("Valid api key", func(sc *scenarioContext) {
  67. keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
  68. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  69. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  70. return nil
  71. })
  72. sc.fakeReq("GET", "/").withValidApiKey().exec()
  73. Convey("Should return 200", func() {
  74. So(sc.resp.Code, ShouldEqual, 200)
  75. })
  76. Convey("Should init middleware context", func() {
  77. So(sc.context.IsSignedIn, ShouldEqual, true)
  78. So(sc.context.OrgId, ShouldEqual, 12)
  79. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  80. })
  81. })
  82. middlewareScenario("Valid api key, but does not match db hash", func(sc *scenarioContext) {
  83. keyhash := "something_not_matching"
  84. bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
  85. query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash}
  86. return nil
  87. })
  88. sc.fakeReq("GET", "/").withValidApiKey().exec()
  89. Convey("Should return api key invalid", func() {
  90. So(sc.resp.Code, ShouldEqual, 401)
  91. So(sc.respJson["message"], ShouldEqual, "Invalid API key")
  92. })
  93. })
  94. middlewareScenario("UserId in session", func(sc *scenarioContext) {
  95. sc.fakeReq("GET", "/").handler(func(c *Context) {
  96. c.Session.Set(SESS_KEY_USERID, int64(12))
  97. }).exec()
  98. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  99. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  100. return nil
  101. })
  102. sc.fakeReq("GET", "/").exec()
  103. Convey("should init context with user info", func() {
  104. So(sc.context.IsSignedIn, ShouldBeTrue)
  105. So(sc.context.UserId, ShouldEqual, 12)
  106. })
  107. })
  108. middlewareScenario("When anonymous access is enabled", func(sc *scenarioContext) {
  109. setting.AnonymousEnabled = true
  110. setting.AnonymousOrgName = "test"
  111. setting.AnonymousOrgRole = string(m.ROLE_EDITOR)
  112. bus.AddHandler("test", func(query *m.GetOrgByNameQuery) error {
  113. So(query.Name, ShouldEqual, "test")
  114. query.Result = &m.Org{Id: 2, Name: "test"}
  115. return nil
  116. })
  117. sc.fakeReq("GET", "/").exec()
  118. Convey("should init context with org info", func() {
  119. So(sc.context.UserId, ShouldEqual, 0)
  120. So(sc.context.OrgId, ShouldEqual, 2)
  121. So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR)
  122. })
  123. Convey("context signed in should be false", func() {
  124. So(sc.context.IsSignedIn, ShouldBeFalse)
  125. })
  126. })
  127. middlewareScenario("When auth_proxy is enabled enabled and user exists", func(sc *scenarioContext) {
  128. setting.AuthProxyEnabled = true
  129. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  130. setting.AuthProxyHeaderProperty = "username"
  131. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  132. query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
  133. return nil
  134. })
  135. sc.fakeReq("GET", "/")
  136. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  137. sc.exec()
  138. Convey("should init context with user info", func() {
  139. So(sc.context.IsSignedIn, ShouldBeTrue)
  140. So(sc.context.UserId, ShouldEqual, 12)
  141. So(sc.context.OrgId, ShouldEqual, 2)
  142. })
  143. })
  144. middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) {
  145. setting.AuthProxyEnabled = true
  146. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  147. setting.AuthProxyHeaderProperty = "username"
  148. setting.AuthProxyAutoSignUp = true
  149. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  150. if query.UserId > 0 {
  151. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  152. return nil
  153. } else {
  154. return m.ErrUserNotFound
  155. }
  156. })
  157. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  158. cmd.Result = m.User{Id: 33}
  159. return nil
  160. })
  161. sc.fakeReq("GET", "/")
  162. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  163. sc.exec()
  164. Convey("Should create user if auto sign up is enabled", func() {
  165. So(sc.context.IsSignedIn, ShouldBeTrue)
  166. So(sc.context.UserId, ShouldEqual, 33)
  167. So(sc.context.OrgId, ShouldEqual, 4)
  168. })
  169. })
  170. middlewareScenario("When auth_proxy is enabled and request RemoteAddr is not trusted", func(sc *scenarioContext) {
  171. setting.AuthProxyEnabled = true
  172. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  173. setting.AuthProxyHeaderProperty = "username"
  174. setting.AuthProxyWhitelist = "192.168.1.1, 192.168.2.1"
  175. sc.fakeReq("GET", "/")
  176. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  177. sc.req.RemoteAddr = "192.168.3.1:12345"
  178. sc.exec()
  179. Convey("should return 407 status code", func() {
  180. So(sc.resp.Code, ShouldEqual, 407)
  181. })
  182. })
  183. middlewareScenario("When auth_proxy is enabled and request RemoteAddr is trusted", func(sc *scenarioContext) {
  184. setting.AuthProxyEnabled = true
  185. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  186. setting.AuthProxyHeaderProperty = "username"
  187. setting.AuthProxyWhitelist = "192.168.1.1, 192.168.2.1"
  188. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  189. query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
  190. return nil
  191. })
  192. sc.fakeReq("GET", "/")
  193. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  194. sc.req.RemoteAddr = "192.168.2.1:12345"
  195. sc.exec()
  196. Convey("Should init context with user info", 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 session exists for previous user, create a new session", func(sc *scenarioContext) {
  203. setting.AuthProxyEnabled = true
  204. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  205. setting.AuthProxyHeaderProperty = "username"
  206. setting.AuthProxyWhitelist = ""
  207. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  208. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  209. return nil
  210. })
  211. // create session
  212. sc.fakeReq("GET", "/").handler(func(c *Context) {
  213. c.Session.Set(SESS_KEY_USERID, int64(33))
  214. }).exec()
  215. oldSessionID := sc.context.Session.ID()
  216. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  217. sc.exec()
  218. newSessionID := sc.context.Session.ID()
  219. Convey("Should not share session with other user", func() {
  220. So(oldSessionID, ShouldNotEqual, newSessionID)
  221. })
  222. })
  223. middlewareScenario("When auth_proxy and ldap enabled call sync with ldap user", func(sc *scenarioContext) {
  224. setting.AuthProxyEnabled = true
  225. setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
  226. setting.AuthProxyHeaderProperty = "username"
  227. setting.AuthProxyWhitelist = ""
  228. setting.LdapEnabled = true
  229. called := false
  230. syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQuery) error {
  231. called = true
  232. return nil
  233. }
  234. bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
  235. query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
  236. return nil
  237. })
  238. sc.fakeReq("GET", "/")
  239. sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
  240. sc.exec()
  241. Convey("Should call syncGrafanaUserWithLdapUser", func() {
  242. So(called, ShouldBeTrue)
  243. })
  244. })
  245. })
  246. }
  247. func middlewareScenario(desc string, fn scenarioFunc) {
  248. Convey(desc, func() {
  249. defer bus.ClearBusHandlers()
  250. sc := &scenarioContext{}
  251. viewsPath, _ := filepath.Abs("../../public/views")
  252. sc.m = macaron.New()
  253. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  254. Directory: viewsPath,
  255. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  256. }))
  257. sc.m.Use(GetContextHandler())
  258. // mock out gc goroutine
  259. startSessionGC = func() {}
  260. sc.m.Use(Sessioner(&session.Options{}))
  261. sc.m.Use(OrgRedirect())
  262. sc.defaultHandler = func(c *Context) {
  263. sc.context = c
  264. if sc.handlerFunc != nil {
  265. sc.handlerFunc(sc.context)
  266. }
  267. }
  268. sc.m.Get("/", sc.defaultHandler)
  269. fn(sc)
  270. })
  271. }
  272. type scenarioContext struct {
  273. m *macaron.Macaron
  274. context *Context
  275. resp *httptest.ResponseRecorder
  276. apiKey string
  277. authHeader string
  278. respJson map[string]interface{}
  279. handlerFunc handlerFunc
  280. defaultHandler macaron.Handler
  281. req *http.Request
  282. }
  283. func (sc *scenarioContext) withValidApiKey() *scenarioContext {
  284. sc.apiKey = "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9"
  285. return sc
  286. }
  287. func (sc *scenarioContext) withInvalidApiKey() *scenarioContext {
  288. sc.apiKey = "nvalidhhhhds"
  289. return sc
  290. }
  291. func (sc *scenarioContext) withAuthoriziationHeader(authHeader string) *scenarioContext {
  292. sc.authHeader = authHeader
  293. return sc
  294. }
  295. func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
  296. sc.resp = httptest.NewRecorder()
  297. req, err := http.NewRequest(method, url, nil)
  298. So(err, ShouldBeNil)
  299. sc.req = req
  300. // add session cookie from last request
  301. if sc.context != nil {
  302. if sc.context.Session.ID() != "" {
  303. req.Header.Add("Cookie", "grafana_sess="+sc.context.Session.ID()+";")
  304. }
  305. }
  306. return sc
  307. }
  308. func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
  309. sc.handlerFunc = fn
  310. return sc
  311. }
  312. func (sc *scenarioContext) exec() {
  313. if sc.apiKey != "" {
  314. sc.req.Header.Add("Authorization", "Bearer "+sc.apiKey)
  315. }
  316. if sc.authHeader != "" {
  317. sc.req.Header.Add("Authorization", sc.authHeader)
  318. }
  319. sc.m.ServeHTTP(sc.resp, sc.req)
  320. if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
  321. err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
  322. So(err, ShouldBeNil)
  323. }
  324. }
  325. type scenarioFunc func(c *scenarioContext)
  326. type handlerFunc func(c *Context)