folder_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. package api
  2. import (
  3. "encoding/json"
  4. "path/filepath"
  5. "testing"
  6. "github.com/go-macaron/session"
  7. "github.com/grafana/grafana/pkg/api/dtos"
  8. "github.com/grafana/grafana/pkg/bus"
  9. "github.com/grafana/grafana/pkg/middleware"
  10. m "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/services/dashboards"
  12. macaron "gopkg.in/macaron.v1"
  13. . "github.com/smartystreets/goconvey/convey"
  14. )
  15. func TestFoldersApiEndpoint(t *testing.T) {
  16. Convey("Given a dashboard", t, func() {
  17. fakeDash := m.NewDashboard("Child dash")
  18. fakeDash.Id = 1
  19. fakeDash.FolderId = 1
  20. fakeDash.HasAcl = false
  21. var getDashboardQueries []*m.GetDashboardQuery
  22. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  23. query.Result = fakeDash
  24. getDashboardQueries = append(getDashboardQueries, query)
  25. return nil
  26. })
  27. updateFolderCmd := m.UpdateFolderCommand{}
  28. Convey("When user is an Org Editor", func() {
  29. role := m.ROLE_EDITOR
  30. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  31. callGetFolder(sc)
  32. So(sc.resp.Code, ShouldEqual, 404)
  33. Convey("Should lookup folder by uid", func() {
  34. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  35. })
  36. })
  37. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
  38. callGetFolder(sc)
  39. So(sc.resp.Code, ShouldEqual, 404)
  40. Convey("Should lookup folder by id", func() {
  41. So(getDashboardQueries[0].Id, ShouldEqual, 1)
  42. })
  43. })
  44. updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", role, updateFolderCmd, func(sc *scenarioContext) {
  45. callUpdateFolder(sc)
  46. So(sc.resp.Code, ShouldEqual, 404)
  47. Convey("Should lookup folder by uid", func() {
  48. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  49. })
  50. })
  51. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  52. callDeleteFolder(sc)
  53. So(sc.resp.Code, ShouldEqual, 404)
  54. Convey("Should lookup folder by uid", func() {
  55. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  56. })
  57. })
  58. })
  59. })
  60. Convey("Given a folder which does not have an acl", t, func() {
  61. fakeFolder := m.NewDashboardFolder("Folder")
  62. fakeFolder.Id = 1
  63. fakeFolder.HasAcl = false
  64. var getDashboardQueries []*m.GetDashboardQuery
  65. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  66. query.Result = fakeFolder
  67. getDashboardQueries = append(getDashboardQueries, query)
  68. return nil
  69. })
  70. viewerRole := m.ROLE_VIEWER
  71. editorRole := m.ROLE_EDITOR
  72. aclMockResp := []*m.DashboardAclInfoDTO{
  73. {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
  74. {Role: &editorRole, Permission: m.PERMISSION_EDIT},
  75. }
  76. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  77. query.Result = aclMockResp
  78. return nil
  79. })
  80. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  81. query.Result = []*m.Team{}
  82. return nil
  83. })
  84. cmd := m.CreateFolderCommand{
  85. Title: fakeFolder.Title,
  86. }
  87. updateFolderCmd := m.UpdateFolderCommand{
  88. Title: fakeFolder.Title,
  89. }
  90. Convey("When user is an Org Viewer", func() {
  91. role := m.ROLE_VIEWER
  92. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  93. folder := getFolderShouldReturn200(sc)
  94. Convey("Should lookup folder by uid", func() {
  95. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  96. })
  97. Convey("Should not be able to edit or save folder", func() {
  98. So(folder.CanEdit, ShouldBeFalse)
  99. So(folder.CanSave, ShouldBeFalse)
  100. So(folder.CanAdmin, ShouldBeFalse)
  101. })
  102. })
  103. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
  104. folder := getFolderShouldReturn200(sc)
  105. Convey("Should lookup folder by id", func() {
  106. So(getDashboardQueries[0].Id, ShouldEqual, 1)
  107. })
  108. Convey("Should not be able to edit or save folder", func() {
  109. So(folder.CanEdit, ShouldBeFalse)
  110. So(folder.CanSave, ShouldBeFalse)
  111. So(folder.CanAdmin, ShouldBeFalse)
  112. })
  113. })
  114. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  115. callDeleteFolder(sc)
  116. So(sc.resp.Code, ShouldEqual, 403)
  117. Convey("Should lookup folder by uid", func() {
  118. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  119. })
  120. })
  121. createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
  122. callCreateFolder(sc)
  123. So(sc.resp.Code, ShouldEqual, 403)
  124. })
  125. updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", role, updateFolderCmd, func(sc *scenarioContext) {
  126. callUpdateFolder(sc)
  127. So(sc.resp.Code, ShouldEqual, 403)
  128. Convey("Should lookup folder by uid", func() {
  129. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  130. })
  131. })
  132. })
  133. Convey("When user is an Org Editor", func() {
  134. role := m.ROLE_EDITOR
  135. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  136. folder := getFolderShouldReturn200(sc)
  137. Convey("Should lookup folder by uid", func() {
  138. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  139. })
  140. Convey("Should be able to edit or save folder", func() {
  141. So(folder.CanEdit, ShouldBeTrue)
  142. So(folder.CanSave, ShouldBeTrue)
  143. So(folder.CanAdmin, ShouldBeFalse)
  144. })
  145. })
  146. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
  147. folder := getFolderShouldReturn200(sc)
  148. Convey("Should lookup folder by id", func() {
  149. So(getDashboardQueries[0].Id, ShouldEqual, 1)
  150. })
  151. Convey("Should be able to edit or save folder", func() {
  152. So(folder.CanEdit, ShouldBeTrue)
  153. So(folder.CanSave, ShouldBeTrue)
  154. So(folder.CanAdmin, ShouldBeFalse)
  155. })
  156. })
  157. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  158. callDeleteFolder(sc)
  159. So(sc.resp.Code, ShouldEqual, 200)
  160. Convey("Should lookup folder by uid", func() {
  161. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  162. })
  163. })
  164. createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
  165. callCreateFolder(sc)
  166. So(sc.resp.Code, ShouldEqual, 200)
  167. })
  168. updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", role, updateFolderCmd, func(sc *scenarioContext) {
  169. callUpdateFolder(sc)
  170. So(sc.resp.Code, ShouldEqual, 200)
  171. Convey("Should lookup folder by uid", func() {
  172. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  173. })
  174. })
  175. })
  176. })
  177. Convey("Given a folder which have an acl", t, func() {
  178. fakeFolder := m.NewDashboardFolder("Folder")
  179. fakeFolder.Id = 1
  180. fakeFolder.HasAcl = true
  181. var getDashboardQueries []*m.GetDashboardQuery
  182. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  183. query.Result = fakeFolder
  184. getDashboardQueries = append(getDashboardQueries, query)
  185. return nil
  186. })
  187. aclMockResp := []*m.DashboardAclInfoDTO{
  188. {
  189. DashboardId: 1,
  190. Permission: m.PERMISSION_EDIT,
  191. UserId: 200,
  192. },
  193. }
  194. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  195. query.Result = aclMockResp
  196. return nil
  197. })
  198. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  199. query.Result = []*m.Team{}
  200. return nil
  201. })
  202. cmd := m.CreateFolderCommand{
  203. Title: fakeFolder.Title,
  204. }
  205. updateFolderCmd := m.UpdateFolderCommand{
  206. Title: fakeFolder.Title,
  207. }
  208. Convey("When user is an Org Viewer and has no permissions for this folder", func() {
  209. role := m.ROLE_VIEWER
  210. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  211. callGetFolder(sc)
  212. Convey("Should lookup folder by uid", func() {
  213. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  214. })
  215. Convey("Should be denied access", func() {
  216. So(sc.resp.Code, ShouldEqual, 403)
  217. })
  218. })
  219. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
  220. callGetFolder(sc)
  221. Convey("Should lookup folder by id", func() {
  222. So(getDashboardQueries[0].Id, ShouldEqual, 1)
  223. })
  224. Convey("Should be denied access", func() {
  225. So(sc.resp.Code, ShouldEqual, 403)
  226. })
  227. })
  228. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  229. callDeleteFolder(sc)
  230. So(sc.resp.Code, ShouldEqual, 403)
  231. Convey("Should lookup folder by uid", func() {
  232. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  233. })
  234. })
  235. createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
  236. callCreateFolder(sc)
  237. So(sc.resp.Code, ShouldEqual, 403)
  238. })
  239. updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", role, updateFolderCmd, func(sc *scenarioContext) {
  240. callUpdateFolder(sc)
  241. So(sc.resp.Code, ShouldEqual, 403)
  242. Convey("Should lookup folder by uid", func() {
  243. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  244. })
  245. })
  246. })
  247. Convey("When user is an Org Editor and has no permissions for this folder", func() {
  248. role := m.ROLE_EDITOR
  249. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  250. callGetFolder(sc)
  251. Convey("Should lookup folder by uid", func() {
  252. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  253. })
  254. Convey("Should be denied access", func() {
  255. So(sc.resp.Code, ShouldEqual, 403)
  256. })
  257. })
  258. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
  259. callGetFolder(sc)
  260. Convey("Should lookup folder by id", func() {
  261. So(getDashboardQueries[0].Id, ShouldEqual, 1)
  262. })
  263. Convey("Should be denied access", func() {
  264. So(sc.resp.Code, ShouldEqual, 403)
  265. })
  266. })
  267. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/uid", "/api/folders/:uid", role, func(sc *scenarioContext) {
  268. callDeleteFolder(sc)
  269. So(sc.resp.Code, ShouldEqual, 403)
  270. Convey("Should lookup folder by uid", func() {
  271. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  272. })
  273. })
  274. createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
  275. callCreateFolder(sc)
  276. So(sc.resp.Code, ShouldEqual, 403)
  277. })
  278. updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", role, updateFolderCmd, func(sc *scenarioContext) {
  279. callUpdateFolder(sc)
  280. So(sc.resp.Code, ShouldEqual, 403)
  281. Convey("Should lookup folder by uid", func() {
  282. So(getDashboardQueries[0].Uid, ShouldEqual, "uid")
  283. })
  284. })
  285. })
  286. })
  287. }
  288. func getFolderShouldReturn200(sc *scenarioContext) dtos.Folder {
  289. callGetFolder(sc)
  290. So(sc.resp.Code, ShouldEqual, 200)
  291. folder := dtos.Folder{}
  292. err := json.NewDecoder(sc.resp.Body).Decode(&folder)
  293. So(err, ShouldBeNil)
  294. return folder
  295. }
  296. func callGetFolder(sc *scenarioContext) {
  297. sc.handlerFunc = GetFolder
  298. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  299. }
  300. func callDeleteFolder(sc *scenarioContext) {
  301. bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
  302. return nil
  303. })
  304. sc.handlerFunc = DeleteFolder
  305. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  306. }
  307. func callCreateFolder(sc *scenarioContext) {
  308. bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
  309. cmd.Result = &m.Dashboard{Id: 1, Slug: "folder", Version: 2}
  310. return nil
  311. })
  312. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  313. }
  314. func callUpdateFolder(sc *scenarioContext) {
  315. bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
  316. cmd.Result = &m.Dashboard{Id: 1, Slug: "folder", Version: 2}
  317. return nil
  318. })
  319. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  320. }
  321. func createFolderScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.CreateFolderCommand, fn scenarioFunc) {
  322. Convey(desc+" "+url, func() {
  323. defer bus.ClearBusHandlers()
  324. sc := &scenarioContext{
  325. url: url,
  326. }
  327. viewsPath, _ := filepath.Abs("../../public/views")
  328. sc.m = macaron.New()
  329. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  330. Directory: viewsPath,
  331. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  332. }))
  333. sc.m.Use(middleware.GetContextHandler())
  334. sc.m.Use(middleware.Sessioner(&session.Options{}))
  335. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  336. sc.context = c
  337. sc.context.UserId = TestUserID
  338. sc.context.OrgId = TestOrgID
  339. sc.context.OrgRole = role
  340. return CreateFolder(c, cmd)
  341. })
  342. fakeRepo = &fakeDashboardRepo{}
  343. dashboards.SetRepository(fakeRepo)
  344. sc.m.Post(routePattern, sc.defaultHandler)
  345. fn(sc)
  346. })
  347. }
  348. func updateFolderScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.UpdateFolderCommand, fn scenarioFunc) {
  349. Convey(desc+" "+url, func() {
  350. defer bus.ClearBusHandlers()
  351. sc := &scenarioContext{
  352. url: url,
  353. }
  354. viewsPath, _ := filepath.Abs("../../public/views")
  355. sc.m = macaron.New()
  356. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  357. Directory: viewsPath,
  358. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  359. }))
  360. sc.m.Use(middleware.GetContextHandler())
  361. sc.m.Use(middleware.Sessioner(&session.Options{}))
  362. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  363. sc.context = c
  364. sc.context.UserId = TestUserID
  365. sc.context.OrgId = TestOrgID
  366. sc.context.OrgRole = role
  367. return UpdateFolder(c, cmd)
  368. })
  369. fakeRepo = &fakeDashboardRepo{}
  370. dashboards.SetRepository(fakeRepo)
  371. sc.m.Put(routePattern, sc.defaultHandler)
  372. fn(sc)
  373. })
  374. }