dashboard_test.go 28 KB

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