dashboard_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  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. CallPostDashboard(sc)
  145. So(sc.resp.Code, ShouldEqual, 200)
  146. result := sc.ToJson()
  147. So(result.Get("status").MustString(), ShouldEqual, "success")
  148. So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0)
  149. So(result.Get("uid").MustString(), ShouldNotBeNil)
  150. So(result.Get("slug").MustString(), ShouldNotBeNil)
  151. })
  152. Convey("When saving a dashboard folder in another folder", func() {
  153. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  154. query.Result = fakeDash
  155. query.Result.IsFolder = true
  156. return nil
  157. })
  158. invalidCmd := m.SaveDashboardCommand{
  159. FolderId: fakeDash.FolderId,
  160. IsFolder: true,
  161. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  162. "folderId": fakeDash.FolderId,
  163. "title": fakeDash.Title,
  164. }),
  165. }
  166. Convey("Should return an error", func() {
  167. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, invalidCmd, func(sc *scenarioContext) {
  168. CallPostDashboard(sc)
  169. So(sc.resp.Code, ShouldEqual, 400)
  170. })
  171. })
  172. })
  173. })
  174. })
  175. Convey("Given a dashboard with a parent folder which has an acl", t, func() {
  176. fakeDash := m.NewDashboard("Child dash")
  177. fakeDash.Id = 1
  178. fakeDash.FolderId = 1
  179. fakeDash.HasAcl = true
  180. setting.ViewersCanEdit = false
  181. aclMockResp := []*m.DashboardAclInfoDTO{
  182. {
  183. DashboardId: 1,
  184. Permission: m.PERMISSION_EDIT,
  185. UserId: 200,
  186. },
  187. }
  188. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  189. query.Result = aclMockResp
  190. return nil
  191. })
  192. var getDashboardQueries []*m.GetDashboardQuery
  193. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  194. query.Result = fakeDash
  195. getDashboardQueries = append(getDashboardQueries, query)
  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.SaveDashboardCommand{
  203. FolderId: fakeDash.FolderId,
  204. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  205. "id": fakeDash.Id,
  206. "folderId": fakeDash.FolderId,
  207. "title": fakeDash.Title,
  208. }),
  209. }
  210. Convey("When user is an Org Viewer and has no permissions for this dashboard", func() {
  211. role := m.ROLE_VIEWER
  212. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  213. sc.handlerFunc = GetDashboard
  214. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  215. Convey("Should lookup dashboard by slug", func() {
  216. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  217. })
  218. Convey("Should be denied access", func() {
  219. So(sc.resp.Code, ShouldEqual, 403)
  220. })
  221. })
  222. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  223. sc.handlerFunc = GetDashboard
  224. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  225. Convey("Should lookup dashboard by uid", func() {
  226. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  227. })
  228. Convey("Should be denied access", func() {
  229. So(sc.resp.Code, ShouldEqual, 403)
  230. })
  231. })
  232. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  233. CallDeleteDashboard(sc)
  234. So(sc.resp.Code, ShouldEqual, 403)
  235. Convey("Should lookup dashboard by slug", func() {
  236. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  237. })
  238. })
  239. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  240. CallGetDashboardVersion(sc)
  241. So(sc.resp.Code, ShouldEqual, 403)
  242. })
  243. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  244. CallGetDashboardVersions(sc)
  245. So(sc.resp.Code, ShouldEqual, 403)
  246. })
  247. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  248. CallPostDashboard(sc)
  249. So(sc.resp.Code, ShouldEqual, 403)
  250. })
  251. })
  252. Convey("When user is an Org Editor and has no permissions for this dashboard", func() {
  253. role := m.ROLE_EDITOR
  254. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  255. sc.handlerFunc = GetDashboard
  256. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  257. Convey("Should lookup dashboard by slug", func() {
  258. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  259. })
  260. Convey("Should be denied access", func() {
  261. So(sc.resp.Code, ShouldEqual, 403)
  262. })
  263. })
  264. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  265. sc.handlerFunc = GetDashboard
  266. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  267. Convey("Should lookup dashboard by uid", func() {
  268. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  269. })
  270. Convey("Should be denied access", func() {
  271. So(sc.resp.Code, ShouldEqual, 403)
  272. })
  273. })
  274. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  275. CallDeleteDashboard(sc)
  276. So(sc.resp.Code, ShouldEqual, 403)
  277. Convey("Should lookup dashboard by slug", func() {
  278. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  279. })
  280. })
  281. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  282. CallGetDashboardVersion(sc)
  283. So(sc.resp.Code, ShouldEqual, 403)
  284. })
  285. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  286. CallGetDashboardVersions(sc)
  287. So(sc.resp.Code, ShouldEqual, 403)
  288. })
  289. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  290. CallPostDashboard(sc)
  291. So(sc.resp.Code, ShouldEqual, 403)
  292. })
  293. })
  294. Convey("When user is an Org Viewer but has an edit permission", func() {
  295. role := m.ROLE_VIEWER
  296. mockResult := []*m.DashboardAclInfoDTO{
  297. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_EDIT},
  298. }
  299. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  300. query.Result = mockResult
  301. return nil
  302. })
  303. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  304. dash := GetDashboardShouldReturn200(sc)
  305. Convey("Should lookup dashboard by slug", func() {
  306. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  307. })
  308. Convey("Should be able to get dashboard with edit rights", func() {
  309. So(dash.Meta.CanEdit, ShouldBeTrue)
  310. So(dash.Meta.CanSave, ShouldBeTrue)
  311. So(dash.Meta.CanAdmin, ShouldBeFalse)
  312. })
  313. })
  314. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  315. dash := GetDashboardShouldReturn200(sc)
  316. Convey("Should lookup dashboard by uid", func() {
  317. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  318. })
  319. Convey("Should be able to get dashboard with edit rights", func() {
  320. So(dash.Meta.CanEdit, ShouldBeTrue)
  321. So(dash.Meta.CanSave, ShouldBeTrue)
  322. So(dash.Meta.CanAdmin, ShouldBeFalse)
  323. })
  324. })
  325. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  326. CallDeleteDashboard(sc)
  327. So(sc.resp.Code, ShouldEqual, 200)
  328. Convey("Should lookup dashboard by slug", func() {
  329. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  330. })
  331. })
  332. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  333. CallGetDashboardVersion(sc)
  334. So(sc.resp.Code, ShouldEqual, 200)
  335. })
  336. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  337. CallGetDashboardVersions(sc)
  338. So(sc.resp.Code, ShouldEqual, 200)
  339. })
  340. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  341. CallPostDashboard(sc)
  342. So(sc.resp.Code, ShouldEqual, 200)
  343. result := sc.ToJson()
  344. So(result.Get("status").MustString(), ShouldEqual, "success")
  345. So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0)
  346. So(result.Get("uid").MustString(), ShouldNotBeNil)
  347. So(result.Get("slug").MustString(), ShouldNotBeNil)
  348. })
  349. })
  350. Convey("When user is an Org Viewer and viewers can edit", func() {
  351. role := m.ROLE_VIEWER
  352. setting.ViewersCanEdit = true
  353. mockResult := []*m.DashboardAclInfoDTO{
  354. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
  355. }
  356. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  357. query.Result = mockResult
  358. return nil
  359. })
  360. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  361. dash := GetDashboardShouldReturn200(sc)
  362. Convey("Should lookup dashboard by slug", func() {
  363. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  364. })
  365. Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
  366. So(dash.Meta.CanEdit, ShouldBeTrue)
  367. So(dash.Meta.CanSave, ShouldBeFalse)
  368. So(dash.Meta.CanAdmin, ShouldBeFalse)
  369. })
  370. })
  371. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  372. dash := GetDashboardShouldReturn200(sc)
  373. Convey("Should lookup dashboard by uid", func() {
  374. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  375. })
  376. Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
  377. So(dash.Meta.CanEdit, ShouldBeTrue)
  378. So(dash.Meta.CanSave, ShouldBeFalse)
  379. So(dash.Meta.CanAdmin, ShouldBeFalse)
  380. })
  381. })
  382. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  383. CallDeleteDashboard(sc)
  384. So(sc.resp.Code, ShouldEqual, 403)
  385. Convey("Should lookup dashboard by slug", func() {
  386. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  387. })
  388. })
  389. })
  390. Convey("When user is an Org Viewer but has an admin permission", func() {
  391. role := m.ROLE_VIEWER
  392. mockResult := []*m.DashboardAclInfoDTO{
  393. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_ADMIN},
  394. }
  395. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  396. query.Result = mockResult
  397. return nil
  398. })
  399. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  400. dash := GetDashboardShouldReturn200(sc)
  401. Convey("Should lookup dashboard by slug", func() {
  402. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  403. })
  404. Convey("Should be able to get dashboard with edit rights", func() {
  405. So(dash.Meta.CanEdit, ShouldBeTrue)
  406. So(dash.Meta.CanSave, ShouldBeTrue)
  407. So(dash.Meta.CanAdmin, ShouldBeTrue)
  408. })
  409. })
  410. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  411. dash := GetDashboardShouldReturn200(sc)
  412. Convey("Should lookup dashboard by uid", func() {
  413. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  414. })
  415. Convey("Should be able to get dashboard with edit rights", func() {
  416. So(dash.Meta.CanEdit, ShouldBeTrue)
  417. So(dash.Meta.CanSave, ShouldBeTrue)
  418. So(dash.Meta.CanAdmin, ShouldBeTrue)
  419. })
  420. })
  421. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  422. CallDeleteDashboard(sc)
  423. So(sc.resp.Code, ShouldEqual, 200)
  424. Convey("Should lookup dashboard by slug", func() {
  425. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  426. })
  427. })
  428. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  429. CallGetDashboardVersion(sc)
  430. So(sc.resp.Code, ShouldEqual, 200)
  431. })
  432. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  433. CallGetDashboardVersions(sc)
  434. So(sc.resp.Code, ShouldEqual, 200)
  435. })
  436. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  437. CallPostDashboard(sc)
  438. So(sc.resp.Code, ShouldEqual, 200)
  439. result := sc.ToJson()
  440. So(result.Get("status").MustString(), ShouldEqual, "success")
  441. So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0)
  442. So(result.Get("uid").MustString(), ShouldNotBeNil)
  443. So(result.Get("slug").MustString(), ShouldNotBeNil)
  444. })
  445. })
  446. Convey("When user is an Org Editor but has a view permission", func() {
  447. role := m.ROLE_EDITOR
  448. mockResult := []*m.DashboardAclInfoDTO{
  449. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
  450. }
  451. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  452. query.Result = mockResult
  453. return nil
  454. })
  455. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  456. dash := GetDashboardShouldReturn200(sc)
  457. Convey("Should lookup dashboard by slug", func() {
  458. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  459. })
  460. Convey("Should not be able to edit or save dashboard", func() {
  461. So(dash.Meta.CanEdit, ShouldBeFalse)
  462. So(dash.Meta.CanSave, ShouldBeFalse)
  463. })
  464. })
  465. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
  466. dash := GetDashboardShouldReturn200(sc)
  467. Convey("Should lookup dashboard by uid", func() {
  468. So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
  469. })
  470. Convey("Should not be able to edit or save dashboard", func() {
  471. So(dash.Meta.CanEdit, ShouldBeFalse)
  472. So(dash.Meta.CanSave, ShouldBeFalse)
  473. })
  474. })
  475. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
  476. CallDeleteDashboard(sc)
  477. So(sc.resp.Code, ShouldEqual, 403)
  478. Convey("Should lookup dashboard by slug", func() {
  479. So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
  480. })
  481. })
  482. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  483. CallGetDashboardVersion(sc)
  484. So(sc.resp.Code, ShouldEqual, 403)
  485. })
  486. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  487. CallGetDashboardVersions(sc)
  488. So(sc.resp.Code, ShouldEqual, 403)
  489. })
  490. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  491. CallPostDashboard(sc)
  492. So(sc.resp.Code, ShouldEqual, 403)
  493. })
  494. })
  495. })
  496. }
  497. func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
  498. sc.handlerFunc = GetDashboard
  499. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  500. So(sc.resp.Code, ShouldEqual, 200)
  501. dash := dtos.DashboardFullWithMeta{}
  502. err := json.NewDecoder(sc.resp.Body).Decode(&dash)
  503. So(err, ShouldBeNil)
  504. return dash
  505. }
  506. func CallGetDashboardVersion(sc *scenarioContext) {
  507. bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
  508. query.Result = &m.DashboardVersion{}
  509. return nil
  510. })
  511. sc.handlerFunc = GetDashboardVersion
  512. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  513. }
  514. func CallGetDashboardVersions(sc *scenarioContext) {
  515. bus.AddHandler("test", func(query *m.GetDashboardVersionsQuery) error {
  516. query.Result = []*m.DashboardVersionDTO{}
  517. return nil
  518. })
  519. sc.handlerFunc = GetDashboardVersions
  520. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  521. }
  522. func CallDeleteDashboard(sc *scenarioContext) {
  523. bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
  524. return nil
  525. })
  526. sc.handlerFunc = DeleteDashboard
  527. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  528. }
  529. func CallPostDashboard(sc *scenarioContext) {
  530. bus.AddHandler("test", func(cmd *alerting.ValidateDashboardAlertsCommand) error {
  531. return nil
  532. })
  533. bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
  534. cmd.Result = &m.Dashboard{Id: 2, Slug: "Dash", Version: 2}
  535. return nil
  536. })
  537. bus.AddHandler("test", func(cmd *alerting.UpdateDashboardAlertsCommand) error {
  538. return nil
  539. })
  540. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  541. }
  542. func postDashboardScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.SaveDashboardCommand, fn scenarioFunc) {
  543. Convey(desc+" "+url, func() {
  544. defer bus.ClearBusHandlers()
  545. sc := &scenarioContext{
  546. url: url,
  547. }
  548. viewsPath, _ := filepath.Abs("../../public/views")
  549. sc.m = macaron.New()
  550. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  551. Directory: viewsPath,
  552. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  553. }))
  554. sc.m.Use(middleware.GetContextHandler())
  555. sc.m.Use(middleware.Sessioner(&session.Options{}))
  556. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  557. sc.context = c
  558. sc.context.UserId = TestUserID
  559. sc.context.OrgId = TestOrgID
  560. sc.context.OrgRole = role
  561. return PostDashboard(c, cmd)
  562. })
  563. fakeRepo = &fakeDashboardRepo{}
  564. dashboards.SetRepository(fakeRepo)
  565. sc.m.Post(routePattern, sc.defaultHandler)
  566. fn(sc)
  567. })
  568. }
  569. func (sc *scenarioContext) ToJson() *simplejson.Json {
  570. var result *simplejson.Json
  571. err := json.NewDecoder(sc.resp.Body).Decode(&result)
  572. So(err, ShouldBeNil)
  573. return result
  574. }