dashboard_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package api
  2. import (
  3. "encoding/json"
  4. "path/filepath"
  5. "testing"
  6. macaron "gopkg.in/macaron.v1"
  7. "github.com/go-macaron/session"
  8. "github.com/grafana/grafana/pkg/api/dtos"
  9. "github.com/grafana/grafana/pkg/bus"
  10. "github.com/grafana/grafana/pkg/components/simplejson"
  11. "github.com/grafana/grafana/pkg/middleware"
  12. "github.com/grafana/grafana/pkg/models"
  13. "github.com/grafana/grafana/pkg/services/alerting"
  14. . "github.com/smartystreets/goconvey/convey"
  15. )
  16. func TestDashboardApiEndpoint(t *testing.T) {
  17. Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
  18. fakeDash := models.NewDashboard("Child dash")
  19. fakeDash.ParentId = 1
  20. fakeDash.HasAcl = false
  21. bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
  22. query.Result = fakeDash
  23. return nil
  24. })
  25. cmd := models.SaveDashboardCommand{
  26. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  27. "parentId": fakeDash.ParentId,
  28. "title": fakeDash.Title,
  29. }),
  30. }
  31. Convey("When user is an Org Viewer", func() {
  32. role := models.ROLE_VIEWER
  33. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  34. dash := GetDashboardShouldReturn200(sc)
  35. Convey("Should not be able to edit or save dashboard", func() {
  36. So(dash.Meta.CanEdit, ShouldBeFalse)
  37. So(dash.Meta.CanSave, ShouldBeFalse)
  38. })
  39. })
  40. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  41. CallDeleteDashboard(sc)
  42. So(sc.resp.Code, ShouldEqual, 403)
  43. })
  44. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  45. CallPostDashboard(sc)
  46. So(sc.resp.Code, ShouldEqual, 403)
  47. })
  48. })
  49. Convey("When user is an Org Read Only Editor", func() {
  50. role := models.ROLE_READ_ONLY_EDITOR
  51. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  52. dash := GetDashboardShouldReturn200(sc)
  53. Convey("Should be able to edit but not save the dashboard", func() {
  54. So(dash.Meta.CanEdit, ShouldBeTrue)
  55. So(dash.Meta.CanSave, ShouldBeFalse)
  56. })
  57. })
  58. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  59. CallDeleteDashboard(sc)
  60. So(sc.resp.Code, ShouldEqual, 403)
  61. })
  62. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  63. CallPostDashboard(sc)
  64. So(sc.resp.Code, ShouldEqual, 403)
  65. })
  66. })
  67. Convey("When user is an Org Editor", func() {
  68. role := models.ROLE_EDITOR
  69. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  70. dash := GetDashboardShouldReturn200(sc)
  71. Convey("Should be able to edit or save dashboard", func() {
  72. So(dash.Meta.CanEdit, ShouldBeTrue)
  73. So(dash.Meta.CanSave, ShouldBeTrue)
  74. })
  75. })
  76. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  77. CallDeleteDashboard(sc)
  78. So(sc.resp.Code, ShouldEqual, 200)
  79. })
  80. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  81. CallPostDashboard(sc)
  82. So(sc.resp.Code, ShouldEqual, 200)
  83. })
  84. })
  85. })
  86. Convey("Given a dashboard with a parent folder which has an acl", t, func() {
  87. fakeDash := models.NewDashboard("Child dash")
  88. fakeDash.ParentId = 1
  89. fakeDash.HasAcl = true
  90. bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
  91. query.Result = fakeDash
  92. return nil
  93. })
  94. bus.AddHandler("test", func(query *models.GetUserGroupsByUserQuery) error {
  95. query.Result = []*models.UserGroup{}
  96. return nil
  97. })
  98. cmd := models.SaveDashboardCommand{
  99. ParentId: fakeDash.ParentId,
  100. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  101. "parentId": fakeDash.ParentId,
  102. "title": fakeDash.Title,
  103. }),
  104. }
  105. Convey("When user is an Org Viewer and has no permissions for this dashboard", func() {
  106. role := models.ROLE_VIEWER
  107. bus.AddHandler("test", func(query *models.GetDashboardPermissionsQuery) error {
  108. query.Result = []*models.DashboardAclInfoDTO{}
  109. return nil
  110. })
  111. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  112. sc.handlerFunc = GetDashboard
  113. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  114. Convey("Should be denied access", func() {
  115. So(sc.resp.Code, ShouldEqual, 403)
  116. })
  117. })
  118. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  119. CallDeleteDashboard(sc)
  120. So(sc.resp.Code, ShouldEqual, 403)
  121. })
  122. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  123. CallPostDashboard(sc)
  124. So(sc.resp.Code, ShouldEqual, 403)
  125. })
  126. })
  127. Convey("When user is an Org Editor and has no permissions for this dashboard", func() {
  128. role := models.ROLE_EDITOR
  129. bus.AddHandler("test", func(query *models.GetDashboardPermissionsQuery) error {
  130. query.Result = []*models.DashboardAclInfoDTO{}
  131. return nil
  132. })
  133. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  134. sc.handlerFunc = GetDashboard
  135. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  136. Convey("Should be denied access", func() {
  137. So(sc.resp.Code, ShouldEqual, 403)
  138. })
  139. })
  140. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  141. CallDeleteDashboard(sc)
  142. So(sc.resp.Code, ShouldEqual, 403)
  143. })
  144. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  145. CallPostDashboard(sc)
  146. So(sc.resp.Code, ShouldEqual, 403)
  147. })
  148. })
  149. Convey("When user is an Org Viewer but has an edit permission", func() {
  150. role := models.ROLE_VIEWER
  151. mockResult := []*models.DashboardAclInfoDTO{
  152. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, PermissionType: models.PERMISSION_EDIT},
  153. }
  154. bus.AddHandler("test", func(query *models.GetDashboardPermissionsQuery) error {
  155. query.Result = mockResult
  156. return nil
  157. })
  158. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  159. dash := GetDashboardShouldReturn200(sc)
  160. Convey("Should be able to get dashboard with edit rights", func() {
  161. So(dash.Meta.CanEdit, ShouldBeTrue)
  162. So(dash.Meta.CanSave, ShouldBeTrue)
  163. })
  164. })
  165. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  166. CallDeleteDashboard(sc)
  167. So(sc.resp.Code, ShouldEqual, 200)
  168. })
  169. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  170. CallPostDashboard(sc)
  171. So(sc.resp.Code, ShouldEqual, 200)
  172. })
  173. })
  174. Convey("When user is an Org Editor but has a view permission", func() {
  175. role := models.ROLE_EDITOR
  176. mockResult := []*models.DashboardAclInfoDTO{
  177. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, PermissionType: models.PERMISSION_VIEW},
  178. }
  179. bus.AddHandler("test", func(query *models.GetDashboardPermissionsQuery) error {
  180. query.Result = mockResult
  181. return nil
  182. })
  183. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  184. dash := GetDashboardShouldReturn200(sc)
  185. Convey("Should not be able to edit or save dashboard", func() {
  186. So(dash.Meta.CanEdit, ShouldBeFalse)
  187. So(dash.Meta.CanSave, ShouldBeFalse)
  188. })
  189. })
  190. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  191. CallDeleteDashboard(sc)
  192. So(sc.resp.Code, ShouldEqual, 403)
  193. })
  194. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  195. CallPostDashboard(sc)
  196. So(sc.resp.Code, ShouldEqual, 403)
  197. })
  198. })
  199. })
  200. }
  201. func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
  202. sc.handlerFunc = GetDashboard
  203. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  204. So(sc.resp.Code, ShouldEqual, 200)
  205. dash := dtos.DashboardFullWithMeta{}
  206. err := json.NewDecoder(sc.resp.Body).Decode(&dash)
  207. So(err, ShouldBeNil)
  208. return dash
  209. }
  210. func CallDeleteDashboard(sc *scenarioContext) {
  211. bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
  212. return nil
  213. })
  214. sc.handlerFunc = DeleteDashboard
  215. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  216. }
  217. func CallPostDashboard(sc *scenarioContext) {
  218. bus.AddHandler("test", func(cmd *alerting.ValidateDashboardAlertsCommand) error {
  219. return nil
  220. })
  221. bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
  222. cmd.Result = &models.Dashboard{Id: 2, Slug: "Dash", Version: 2}
  223. return nil
  224. })
  225. bus.AddHandler("test", func(cmd *alerting.UpdateDashboardAlertsCommand) error {
  226. return nil
  227. })
  228. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  229. }
  230. func postDashboardScenario(desc string, url string, routePattern string, role models.RoleType, cmd models.SaveDashboardCommand, fn scenarioFunc) {
  231. Convey(desc+" "+url, func() {
  232. defer bus.ClearBusHandlers()
  233. sc := &scenarioContext{
  234. url: url,
  235. }
  236. viewsPath, _ := filepath.Abs("../../public/views")
  237. sc.m = macaron.New()
  238. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  239. Directory: viewsPath,
  240. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  241. }))
  242. sc.m.Use(middleware.GetContextHandler())
  243. sc.m.Use(middleware.Sessioner(&session.Options{}))
  244. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  245. sc.context = c
  246. sc.context.UserId = TestUserID
  247. sc.context.OrgId = TestOrgID
  248. sc.context.OrgRole = role
  249. return PostDashboard(c, cmd)
  250. })
  251. sc.m.Post(routePattern, sc.defaultHandler)
  252. fn(sc)
  253. })
  254. }