dashboard_acl_test.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package api
  2. import (
  3. "path/filepath"
  4. "testing"
  5. "github.com/go-macaron/session"
  6. "github.com/grafana/grafana/pkg/api/dtos"
  7. "github.com/grafana/grafana/pkg/bus"
  8. "github.com/grafana/grafana/pkg/components/simplejson"
  9. "github.com/grafana/grafana/pkg/middleware"
  10. m "github.com/grafana/grafana/pkg/models"
  11. macaron "gopkg.in/macaron.v1"
  12. . "github.com/smartystreets/goconvey/convey"
  13. )
  14. func TestDashboardAclApiEndpoint(t *testing.T) {
  15. Convey("Given a dashboard acl", t, func() {
  16. mockResult := []*m.DashboardAclInfoDTO{
  17. {Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: m.PERMISSION_VIEW},
  18. {Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: m.PERMISSION_EDIT},
  19. {Id: 3, OrgId: 1, DashboardId: 1, UserId: 4, Permission: m.PERMISSION_ADMIN},
  20. {Id: 4, OrgId: 1, DashboardId: 1, TeamId: 1, Permission: m.PERMISSION_VIEW},
  21. {Id: 5, OrgId: 1, DashboardId: 1, TeamId: 2, Permission: m.PERMISSION_ADMIN},
  22. }
  23. dtoRes := transformDashboardAclsToDTOs(mockResult)
  24. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  25. query.Result = dtoRes
  26. return nil
  27. })
  28. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  29. query.Result = mockResult
  30. return nil
  31. })
  32. teamResp := []*m.Team{}
  33. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  34. query.Result = teamResp
  35. return nil
  36. })
  37. Convey("When user is org admin", func() {
  38. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardsId/acl", m.ROLE_ADMIN, func(sc *scenarioContext) {
  39. Convey("Should be able to access ACL", func() {
  40. sc.handlerFunc = GetDashboardAclList
  41. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  42. So(sc.resp.Code, ShouldEqual, 200)
  43. respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
  44. So(err, ShouldBeNil)
  45. So(len(respJSON.MustArray()), ShouldEqual, 5)
  46. So(respJSON.GetIndex(0).Get("userId").MustInt(), ShouldEqual, 2)
  47. So(respJSON.GetIndex(0).Get("permission").MustInt(), ShouldEqual, m.PERMISSION_VIEW)
  48. })
  49. })
  50. })
  51. Convey("When user is editor and has admin permission in the ACL", func() {
  52. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", m.ROLE_EDITOR, func(sc *scenarioContext) {
  53. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 6, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN})
  54. Convey("Should be able to access ACL", func() {
  55. sc.handlerFunc = GetDashboardAclList
  56. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  57. So(sc.resp.Code, ShouldEqual, 200)
  58. })
  59. })
  60. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
  61. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 6, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN})
  62. bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
  63. return nil
  64. })
  65. Convey("Should be able to delete permission", func() {
  66. sc.handlerFunc = DeleteDashboardAcl
  67. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  68. So(sc.resp.Code, ShouldEqual, 200)
  69. })
  70. })
  71. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/6", "/api/dashboards/id/:dashboardId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
  72. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 6, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN})
  73. bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
  74. return nil
  75. })
  76. Convey("Should not be able to delete their own Admin permission", func() {
  77. sc.handlerFunc = DeleteDashboardAcl
  78. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  79. So(sc.resp.Code, ShouldEqual, 403)
  80. })
  81. })
  82. Convey("Should not be able to downgrade their own Admin permission", func() {
  83. cmd := dtos.UpdateDashboardAclCommand{
  84. Items: []dtos.DashboardAclUpdateItem{
  85. {UserId: TestUserID, Permission: m.PERMISSION_EDIT},
  86. },
  87. }
  88. postAclScenario("When calling POST on", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
  89. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 6, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN})
  90. CallPostAcl(sc)
  91. So(sc.resp.Code, ShouldEqual, 403)
  92. })
  93. })
  94. Convey("Should be able to update permissions", func() {
  95. cmd := dtos.UpdateDashboardAclCommand{
  96. Items: []dtos.DashboardAclUpdateItem{
  97. {UserId: TestUserID, Permission: m.PERMISSION_ADMIN},
  98. {UserId: 2, Permission: m.PERMISSION_EDIT},
  99. },
  100. }
  101. postAclScenario("When calling POST on", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
  102. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 6, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN})
  103. CallPostAcl(sc)
  104. So(sc.resp.Code, ShouldEqual, 200)
  105. })
  106. })
  107. Convey("When user is a member of a team in the ACL with admin permission", func() {
  108. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardsId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
  109. teamResp = append(teamResp, &m.Team{Id: 2, OrgId: 1, Name: "UG2"})
  110. bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
  111. return nil
  112. })
  113. Convey("Should be able to delete permission", func() {
  114. sc.handlerFunc = DeleteDashboardAcl
  115. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  116. So(sc.resp.Code, ShouldEqual, 200)
  117. })
  118. })
  119. })
  120. })
  121. Convey("When user is editor and has edit permission in the ACL", func() {
  122. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", m.ROLE_EDITOR, func(sc *scenarioContext) {
  123. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_EDIT})
  124. Convey("Should not be able to access ACL", func() {
  125. sc.handlerFunc = GetDashboardAclList
  126. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  127. So(sc.resp.Code, ShouldEqual, 403)
  128. })
  129. })
  130. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
  131. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_EDIT})
  132. bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
  133. return nil
  134. })
  135. Convey("Should be not be able to delete permission", func() {
  136. sc.handlerFunc = DeleteDashboardAcl
  137. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  138. So(sc.resp.Code, ShouldEqual, 403)
  139. })
  140. })
  141. })
  142. Convey("When user is editor and not in the ACL", func() {
  143. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardsId/acl", m.ROLE_EDITOR, func(sc *scenarioContext) {
  144. Convey("Should not be able to access ACL", func() {
  145. sc.handlerFunc = GetDashboardAclList
  146. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  147. So(sc.resp.Code, ShouldEqual, 403)
  148. })
  149. })
  150. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/user/1", "/api/dashboards/id/:dashboardsId/acl/user/:userId", m.ROLE_EDITOR, func(sc *scenarioContext) {
  151. mockResult = append(mockResult, &m.DashboardAclInfoDTO{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_VIEW})
  152. bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
  153. return nil
  154. })
  155. Convey("Should be not be able to delete permission", func() {
  156. sc.handlerFunc = DeleteDashboardAcl
  157. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  158. So(sc.resp.Code, ShouldEqual, 403)
  159. })
  160. })
  161. })
  162. })
  163. }
  164. func transformDashboardAclsToDTOs(acls []*m.DashboardAclInfoDTO) []*m.DashboardAclInfoDTO {
  165. dtos := make([]*m.DashboardAclInfoDTO, 0)
  166. for _, acl := range acls {
  167. dto := &m.DashboardAclInfoDTO{
  168. Id: acl.Id,
  169. OrgId: acl.OrgId,
  170. DashboardId: acl.DashboardId,
  171. Permission: acl.Permission,
  172. UserId: acl.UserId,
  173. TeamId: acl.TeamId,
  174. }
  175. dtos = append(dtos, dto)
  176. }
  177. return dtos
  178. }
  179. func CallPostAcl(sc *scenarioContext) {
  180. bus.AddHandler("test", func(cmd *m.UpdateDashboardAclCommand) error {
  181. return nil
  182. })
  183. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  184. }
  185. func postAclScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.UpdateDashboardAclCommand, fn scenarioFunc) {
  186. Convey(desc+" "+url, func() {
  187. defer bus.ClearBusHandlers()
  188. sc := &scenarioContext{
  189. url: url,
  190. }
  191. viewsPath, _ := filepath.Abs("../../public/views")
  192. sc.m = macaron.New()
  193. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  194. Directory: viewsPath,
  195. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  196. }))
  197. sc.m.Use(middleware.GetContextHandler())
  198. sc.m.Use(middleware.Sessioner(&session.Options{}))
  199. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  200. sc.context = c
  201. sc.context.UserId = TestUserID
  202. sc.context.OrgId = TestOrgID
  203. sc.context.OrgRole = role
  204. return UpdateDashboardAcl(c, cmd)
  205. })
  206. sc.m.Post(routePattern, sc.defaultHandler)
  207. fn(sc)
  208. })
  209. }