dashboard_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  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. m "github.com/grafana/grafana/pkg/models"
  13. "github.com/grafana/grafana/pkg/services/alerting"
  14. "github.com/grafana/grafana/pkg/services/dashboards"
  15. "github.com/grafana/grafana/pkg/setting"
  16. . "github.com/smartystreets/goconvey/convey"
  17. )
  18. type fakeDashboardRepo struct {
  19. inserted []*dashboards.SaveDashboardItem
  20. getDashboard []*m.Dashboard
  21. }
  22. func (repo *fakeDashboardRepo) SaveDashboard(json *dashboards.SaveDashboardItem) (*m.Dashboard, error) {
  23. repo.inserted = append(repo.inserted, json)
  24. return json.Dashboard, nil
  25. }
  26. var fakeRepo *fakeDashboardRepo
  27. func TestDashboardApiEndpoint(t *testing.T) {
  28. Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
  29. fakeDash := m.NewDashboard("Child dash")
  30. fakeDash.Id = 1
  31. fakeDash.FolderId = 1
  32. fakeDash.HasAcl = false
  33. var getDashboardQueries []*m.GetDashboardQuery
  34. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  35. query.Result = fakeDash
  36. getDashboardQueries = append(getDashboardQueries, query)
  37. return nil
  38. })
  39. viewerRole := m.ROLE_VIEWER
  40. editorRole := m.ROLE_EDITOR
  41. aclMockResp := []*m.DashboardAclInfoDTO{
  42. {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
  43. {Role: &editorRole, Permission: m.PERMISSION_EDIT},
  44. }
  45. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  46. query.Result = aclMockResp
  47. return nil
  48. })
  49. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  50. query.Result = []*m.Team{}
  51. return nil
  52. })
  53. cmd := m.SaveDashboardCommand{
  54. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  55. "folderId": fakeDash.FolderId,
  56. "title": fakeDash.Title,
  57. "id": fakeDash.Id,
  58. }),
  59. }
  60. Convey("When user is an Org Viewer", func() {
  61. role := m.ROLE_VIEWER
  62. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  63. dash := GetDashboardShouldReturn200(sc)
  64. Convey("Should lookup dashboard by slug", func() {
  65. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  66. })
  67. Convey("Should not be able to edit or save dashboard", func() {
  68. So(dash.Meta.CanEdit, ShouldBeFalse)
  69. So(dash.Meta.CanSave, ShouldBeFalse)
  70. So(dash.Meta.CanAdmin, ShouldBeFalse)
  71. })
  72. })
  73. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  74. dash := GetDashboardShouldReturn200(sc)
  75. Convey("Should lookup dashboard by uid", func() {
  76. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  77. })
  78. Convey("Should not be able to edit or save dashboard", func() {
  79. So(dash.Meta.CanEdit, ShouldBeFalse)
  80. So(dash.Meta.CanSave, ShouldBeFalse)
  81. So(dash.Meta.CanAdmin, ShouldBeFalse)
  82. })
  83. })
  84. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  85. CallDeleteDashboard(sc)
  86. So(sc.resp.Code, ShouldEqual, 403)
  87. Convey("Should lookup dashboard by slug", func() {
  88. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  89. })
  90. })
  91. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  92. CallGetDashboardVersion(sc)
  93. So(sc.resp.Code, ShouldEqual, 403)
  94. })
  95. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  96. CallGetDashboardVersions(sc)
  97. So(sc.resp.Code, ShouldEqual, 403)
  98. })
  99. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  100. CallPostDashboard(sc)
  101. So(sc.resp.Code, ShouldEqual, 403)
  102. })
  103. })
  104. Convey("When user is an Org Editor", func() {
  105. role := m.ROLE_EDITOR
  106. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  107. dash := GetDashboardShouldReturn200(sc)
  108. Convey("Should lookup dashboard by slug", func() {
  109. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  110. })
  111. Convey("Should be able to edit or save dashboard", func() {
  112. So(dash.Meta.CanEdit, ShouldBeTrue)
  113. So(dash.Meta.CanSave, ShouldBeTrue)
  114. So(dash.Meta.CanAdmin, ShouldBeFalse)
  115. })
  116. })
  117. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  118. dash := GetDashboardShouldReturn200(sc)
  119. Convey("Should lookup dashboard by uid", func() {
  120. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  121. })
  122. Convey("Should be able to edit or save dashboard", func() {
  123. So(dash.Meta.CanEdit, ShouldBeTrue)
  124. So(dash.Meta.CanSave, ShouldBeTrue)
  125. So(dash.Meta.CanAdmin, ShouldBeFalse)
  126. })
  127. })
  128. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  129. CallDeleteDashboard(sc)
  130. So(sc.resp.Code, ShouldEqual, 200)
  131. Convey("Should lookup dashboard by slug", func() {
  132. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  133. })
  134. })
  135. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  136. CallGetDashboardVersion(sc)
  137. So(sc.resp.Code, ShouldEqual, 200)
  138. })
  139. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  140. CallGetDashboardVersions(sc)
  141. So(sc.resp.Code, ShouldEqual, 200)
  142. })
  143. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  144. CallPostDashboardShouldReturnSuccess(sc)
  145. })
  146. Convey("When saving a dashboard folder in another folder", func() {
  147. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  148. query.Result = fakeDash
  149. query.Result.IsFolder = true
  150. return nil
  151. })
  152. invalidCmd := m.SaveDashboardCommand{
  153. FolderId: fakeDash.FolderId,
  154. IsFolder: true,
  155. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  156. "folderId": fakeDash.FolderId,
  157. "title": fakeDash.Title,
  158. }),
  159. }
  160. Convey("Should return an error", func() {
  161. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, invalidCmd, func(sc *scenarioContext) {
  162. CallPostDashboard(sc)
  163. So(sc.resp.Code, ShouldEqual, 400)
  164. })
  165. })
  166. })
  167. })
  168. })
  169. Convey("Given a dashboard with a parent folder which has an acl", t, func() {
  170. fakeDash := m.NewDashboard("Child dash")
  171. fakeDash.Id = 1
  172. fakeDash.FolderId = 1
  173. fakeDash.HasAcl = true
  174. setting.ViewersCanEdit = false
  175. aclMockResp := []*m.DashboardAclInfoDTO{
  176. {
  177. DashboardId: 1,
  178. Permission: m.PERMISSION_EDIT,
  179. UserId: 200,
  180. },
  181. }
  182. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  183. query.Result = aclMockResp
  184. return nil
  185. })
  186. var getDashboardQueries []*m.GetDashboardQuery
  187. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  188. query.Result = fakeDash
  189. getDashboardQueries = append(getDashboardQueries, query)
  190. return nil
  191. })
  192. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  193. query.Result = []*m.Team{}
  194. return nil
  195. })
  196. cmd := m.SaveDashboardCommand{
  197. FolderId: fakeDash.FolderId,
  198. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  199. "id": fakeDash.Id,
  200. "folderId": fakeDash.FolderId,
  201. "title": fakeDash.Title,
  202. }),
  203. }
  204. Convey("When user is an Org Viewer and has no permissions for this dashboard", func() {
  205. role := m.ROLE_VIEWER
  206. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  207. sc.handlerFunc = GetDashboard
  208. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  209. Convey("Should lookup dashboard by slug", func() {
  210. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  211. })
  212. Convey("Should be denied access", func() {
  213. So(sc.resp.Code, ShouldEqual, 403)
  214. })
  215. })
  216. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  217. sc.handlerFunc = GetDashboard
  218. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  219. Convey("Should lookup dashboard by uid", func() {
  220. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  221. })
  222. Convey("Should be denied access", func() {
  223. So(sc.resp.Code, ShouldEqual, 403)
  224. })
  225. })
  226. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  227. CallDeleteDashboard(sc)
  228. So(sc.resp.Code, ShouldEqual, 403)
  229. Convey("Should lookup dashboard by slug", func() {
  230. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  231. })
  232. })
  233. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  234. CallGetDashboardVersion(sc)
  235. So(sc.resp.Code, ShouldEqual, 403)
  236. })
  237. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  238. CallGetDashboardVersions(sc)
  239. So(sc.resp.Code, ShouldEqual, 403)
  240. })
  241. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  242. CallPostDashboard(sc)
  243. So(sc.resp.Code, ShouldEqual, 403)
  244. })
  245. })
  246. Convey("When user is an Org Editor and has no permissions for this dashboard", func() {
  247. role := m.ROLE_EDITOR
  248. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  249. sc.handlerFunc = GetDashboard
  250. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  251. Convey("Should lookup dashboard by slug", func() {
  252. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  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/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  259. sc.handlerFunc = GetDashboard
  260. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  261. Convey("Should lookup dashboard by uid", func() {
  262. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  263. })
  264. Convey("Should be denied access", func() {
  265. So(sc.resp.Code, ShouldEqual, 403)
  266. })
  267. })
  268. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  269. CallDeleteDashboard(sc)
  270. So(sc.resp.Code, ShouldEqual, 403)
  271. Convey("Should lookup dashboard by slug", func() {
  272. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  273. })
  274. })
  275. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  276. CallGetDashboardVersion(sc)
  277. So(sc.resp.Code, ShouldEqual, 403)
  278. })
  279. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  280. CallGetDashboardVersions(sc)
  281. So(sc.resp.Code, ShouldEqual, 403)
  282. })
  283. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  284. CallPostDashboard(sc)
  285. So(sc.resp.Code, ShouldEqual, 403)
  286. })
  287. })
  288. Convey("When user is an Org Viewer but has an edit permission", func() {
  289. role := m.ROLE_VIEWER
  290. mockResult := []*m.DashboardAclInfoDTO{
  291. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_EDIT},
  292. }
  293. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  294. query.Result = mockResult
  295. return nil
  296. })
  297. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  298. dash := GetDashboardShouldReturn200(sc)
  299. Convey("Should lookup dashboard by slug", func() {
  300. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  301. })
  302. Convey("Should be able to get dashboard with edit rights", func() {
  303. So(dash.Meta.CanEdit, ShouldBeTrue)
  304. So(dash.Meta.CanSave, ShouldBeTrue)
  305. So(dash.Meta.CanAdmin, ShouldBeFalse)
  306. })
  307. })
  308. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  309. dash := GetDashboardShouldReturn200(sc)
  310. Convey("Should lookup dashboard by uid", func() {
  311. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  312. })
  313. Convey("Should be able to get dashboard with edit rights", func() {
  314. So(dash.Meta.CanEdit, ShouldBeTrue)
  315. So(dash.Meta.CanSave, ShouldBeTrue)
  316. So(dash.Meta.CanAdmin, ShouldBeFalse)
  317. })
  318. })
  319. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  320. CallDeleteDashboard(sc)
  321. So(sc.resp.Code, ShouldEqual, 200)
  322. Convey("Should lookup dashboard by slug", func() {
  323. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  324. })
  325. })
  326. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  327. CallGetDashboardVersion(sc)
  328. So(sc.resp.Code, ShouldEqual, 200)
  329. })
  330. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  331. CallGetDashboardVersions(sc)
  332. So(sc.resp.Code, ShouldEqual, 200)
  333. })
  334. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  335. CallPostDashboardShouldReturnSuccess(sc)
  336. })
  337. })
  338. Convey("When user is an Org Viewer and viewers can edit", func() {
  339. role := m.ROLE_VIEWER
  340. setting.ViewersCanEdit = true
  341. mockResult := []*m.DashboardAclInfoDTO{
  342. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
  343. }
  344. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  345. query.Result = mockResult
  346. return nil
  347. })
  348. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  349. dash := GetDashboardShouldReturn200(sc)
  350. Convey("Should lookup dashboard by slug", func() {
  351. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  352. })
  353. Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
  354. So(dash.Meta.CanEdit, ShouldBeTrue)
  355. So(dash.Meta.CanSave, ShouldBeFalse)
  356. So(dash.Meta.CanAdmin, ShouldBeFalse)
  357. })
  358. })
  359. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  360. dash := GetDashboardShouldReturn200(sc)
  361. Convey("Should lookup dashboard by uid", func() {
  362. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  363. })
  364. Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
  365. So(dash.Meta.CanEdit, ShouldBeTrue)
  366. So(dash.Meta.CanSave, ShouldBeFalse)
  367. So(dash.Meta.CanAdmin, ShouldBeFalse)
  368. })
  369. })
  370. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  371. CallDeleteDashboard(sc)
  372. So(sc.resp.Code, ShouldEqual, 403)
  373. Convey("Should lookup dashboard by slug", func() {
  374. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  375. })
  376. })
  377. })
  378. Convey("When user is an Org Viewer but has an admin permission", func() {
  379. role := m.ROLE_VIEWER
  380. mockResult := []*m.DashboardAclInfoDTO{
  381. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_ADMIN},
  382. }
  383. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  384. query.Result = mockResult
  385. return nil
  386. })
  387. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  388. dash := GetDashboardShouldReturn200(sc)
  389. Convey("Should lookup dashboard by slug", func() {
  390. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  391. })
  392. Convey("Should be able to get dashboard with edit rights", func() {
  393. So(dash.Meta.CanEdit, ShouldBeTrue)
  394. So(dash.Meta.CanSave, ShouldBeTrue)
  395. So(dash.Meta.CanAdmin, ShouldBeTrue)
  396. })
  397. })
  398. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  399. dash := GetDashboardShouldReturn200(sc)
  400. Convey("Should lookup dashboard by uid", func() {
  401. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  402. })
  403. Convey("Should be able to get dashboard with edit rights", func() {
  404. So(dash.Meta.CanEdit, ShouldBeTrue)
  405. So(dash.Meta.CanSave, ShouldBeTrue)
  406. So(dash.Meta.CanAdmin, ShouldBeTrue)
  407. })
  408. })
  409. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  410. CallDeleteDashboard(sc)
  411. So(sc.resp.Code, ShouldEqual, 200)
  412. Convey("Should lookup dashboard by slug", func() {
  413. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  414. })
  415. })
  416. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  417. CallGetDashboardVersion(sc)
  418. So(sc.resp.Code, ShouldEqual, 200)
  419. })
  420. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  421. CallGetDashboardVersions(sc)
  422. So(sc.resp.Code, ShouldEqual, 200)
  423. })
  424. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  425. CallPostDashboardShouldReturnSuccess(sc)
  426. })
  427. })
  428. Convey("When user is an Org Editor but has a view permission", func() {
  429. role := m.ROLE_EDITOR
  430. mockResult := []*m.DashboardAclInfoDTO{
  431. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
  432. }
  433. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  434. query.Result = mockResult
  435. return nil
  436. })
  437. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  438. dash := GetDashboardShouldReturn200(sc)
  439. Convey("Should lookup dashboard by slug", func() {
  440. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  441. })
  442. Convey("Should not be able to edit or save dashboard", func() {
  443. So(dash.Meta.CanEdit, ShouldBeFalse)
  444. So(dash.Meta.CanSave, ShouldBeFalse)
  445. })
  446. })
  447. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  448. dash := GetDashboardShouldReturn200(sc)
  449. Convey("Should lookup dashboard by uid", func() {
  450. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  451. })
  452. Convey("Should not be able to edit or save dashboard", func() {
  453. So(dash.Meta.CanEdit, ShouldBeFalse)
  454. So(dash.Meta.CanSave, ShouldBeFalse)
  455. })
  456. })
  457. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  458. CallDeleteDashboard(sc)
  459. So(sc.resp.Code, ShouldEqual, 403)
  460. Convey("Should lookup dashboard by slug", func() {
  461. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  462. })
  463. })
  464. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  465. CallGetDashboardVersion(sc)
  466. So(sc.resp.Code, ShouldEqual, 403)
  467. })
  468. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  469. CallGetDashboardVersions(sc)
  470. So(sc.resp.Code, ShouldEqual, 403)
  471. })
  472. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  473. CallPostDashboard(sc)
  474. So(sc.resp.Code, ShouldEqual, 403)
  475. })
  476. })
  477. })
  478. }
  479. func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
  480. sc.handlerFunc = GetDashboard
  481. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  482. So(sc.resp.Code, ShouldEqual, 200)
  483. dash := dtos.DashboardFullWithMeta{}
  484. err := json.NewDecoder(sc.resp.Body).Decode(&dash)
  485. So(err, ShouldBeNil)
  486. return dash
  487. }
  488. func CallGetDashboardVersion(sc *scenarioContext) {
  489. bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
  490. query.Result = &m.DashboardVersion{}
  491. return nil
  492. })
  493. sc.handlerFunc = GetDashboardVersion
  494. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  495. }
  496. func CallGetDashboardVersions(sc *scenarioContext) {
  497. bus.AddHandler("test", func(query *m.GetDashboardVersionsQuery) error {
  498. query.Result = []*m.DashboardVersionDTO{}
  499. return nil
  500. })
  501. sc.handlerFunc = GetDashboardVersions
  502. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  503. }
  504. func CallDeleteDashboard(sc *scenarioContext) {
  505. bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
  506. return nil
  507. })
  508. sc.handlerFunc = DeleteDashboard
  509. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  510. }
  511. func CallPostDashboard(sc *scenarioContext) {
  512. bus.AddHandler("test", func(cmd *alerting.ValidateDashboardAlertsCommand) error {
  513. return nil
  514. })
  515. bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
  516. cmd.Result = &m.Dashboard{Id: 2, Slug: "Dash", Version: 2}
  517. return nil
  518. })
  519. bus.AddHandler("test", func(cmd *alerting.UpdateDashboardAlertsCommand) error {
  520. return nil
  521. })
  522. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  523. }
  524. func CallPostDashboardShouldReturnSuccess(sc *scenarioContext) {
  525. CallPostDashboard(sc)
  526. So(sc.resp.Code, ShouldEqual, 200)
  527. result := sc.ToJson()
  528. So(result.Get("status").MustString(), ShouldEqual, "success")
  529. So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0)
  530. So(result.Get("uid").MustString(), ShouldNotBeNil)
  531. So(result.Get("slug").MustString(), ShouldNotBeNil)
  532. So(result.Get("url").MustString(), ShouldNotBeNil)
  533. }
  534. func postDashboardScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.SaveDashboardCommand, fn scenarioFunc) {
  535. Convey(desc+" "+url, func() {
  536. defer bus.ClearBusHandlers()
  537. sc := &scenarioContext{
  538. url: url,
  539. }
  540. viewsPath, _ := filepath.Abs("../../public/views")
  541. sc.m = macaron.New()
  542. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  543. Directory: viewsPath,
  544. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  545. }))
  546. sc.m.Use(middleware.GetContextHandler())
  547. sc.m.Use(middleware.Sessioner(&session.Options{}))
  548. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  549. sc.context = c
  550. sc.context.UserId = TestUserID
  551. sc.context.OrgId = TestOrgID
  552. sc.context.OrgRole = role
  553. return PostDashboard(c, cmd)
  554. })
  555. fakeRepo = &fakeDashboardRepo{}
  556. dashboards.SetRepository(fakeRepo)
  557. sc.m.Post(routePattern, sc.defaultHandler)
  558. fn(sc)
  559. })
  560. }
  561. func (sc *scenarioContext) ToJson() *simplejson.Json {
  562. var result *simplejson.Json
  563. err := json.NewDecoder(sc.resp.Body).Decode(&result)
  564. So(err, ShouldBeNil)
  565. return result
  566. }