dashboard_test.go 29 KB

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