dashboard_test.go 28 KB

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