| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106 |
- package api
- import (
- "encoding/json"
- "fmt"
- "testing"
- "github.com/grafana/grafana/pkg/api/dtos"
- "github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/services/alerting"
- "github.com/grafana/grafana/pkg/services/dashboards"
- "github.com/grafana/grafana/pkg/setting"
- . "github.com/smartystreets/goconvey/convey"
- )
- // This tests three main scenarios.
- // If a user has access to execute an action on a dashboard:
- // 1. and the dashboard is in a folder which does not have an acl
- // 2. and the dashboard is in a folder which does have an acl
- // 3. Post dashboard response tests
- func TestDashboardApiEndpoint(t *testing.T) {
- Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
- fakeDash := m.NewDashboard("Child dash")
- fakeDash.Id = 1
- fakeDash.FolderId = 1
- fakeDash.HasAcl = false
- bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
- dashboards := []*m.Dashboard{fakeDash}
- query.Result = dashboards
- return nil
- })
- var getDashboardQueries []*m.GetDashboardQuery
- bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
- query.Result = fakeDash
- getDashboardQueries = append(getDashboardQueries, query)
- return nil
- })
- bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
- query.Result = false
- return nil
- })
- viewerRole := m.ROLE_VIEWER
- editorRole := m.ROLE_EDITOR
- aclMockResp := []*m.DashboardAclInfoDTO{
- {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
- {Role: &editorRole, Permission: m.PERMISSION_EDIT},
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = aclMockResp
- return nil
- })
- bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
- query.Result = []*m.TeamDTO{}
- return nil
- })
- // This tests two scenarios:
- // 1. user is an org viewer
- // 2. user is an org editor
- Convey("When user is an Org Viewer", func() {
- role := m.ROLE_VIEWER
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should not be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeFalse)
- So(dash.Meta.CanSave, ShouldBeFalse)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should not be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeFalse)
- So(dash.Meta.CanSave, ShouldBeFalse)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- Convey("When user is an Org Editor", func() {
- role := m.ROLE_EDITOR
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- })
- })
- Convey("Given a dashboard with a parent folder which has an acl", t, func() {
- fakeDash := m.NewDashboard("Child dash")
- fakeDash.Id = 1
- fakeDash.FolderId = 1
- fakeDash.HasAcl = true
- setting.ViewersCanEdit = false
- bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
- query.Result = false
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
- dashboards := []*m.Dashboard{fakeDash}
- query.Result = dashboards
- return nil
- })
- aclMockResp := []*m.DashboardAclInfoDTO{
- {
- DashboardId: 1,
- Permission: m.PERMISSION_EDIT,
- UserId: 200,
- },
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = aclMockResp
- return nil
- })
- var getDashboardQueries []*m.GetDashboardQuery
- bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
- query.Result = fakeDash
- getDashboardQueries = append(getDashboardQueries, query)
- return nil
- })
- bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
- query.Result = []*m.TeamDTO{}
- return nil
- })
- // This tests six scenarios:
- // 1. user is an org viewer AND has no permissions for this dashboard
- // 2. user is an org editor AND has no permissions for this dashboard
- // 3. user is an org viewer AND has been granted edit permission for the dashboard
- // 4. user is an org viewer AND all viewers have edit permission for this dashboard
- // 5. user is an org viewer AND has been granted an admin permission
- // 6. user is an org editor AND has been granted a view permission
- Convey("When user is an Org Viewer and has no permissions for this dashboard", func() {
- role := m.ROLE_VIEWER
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- sc.handlerFunc = GetDashboard
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be denied access", func() {
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- sc.handlerFunc = GetDashboard
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be denied access", func() {
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- Convey("When user is an Org Editor and has no permissions for this dashboard", func() {
- role := m.ROLE_EDITOR
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- sc.handlerFunc = GetDashboard
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be denied access", func() {
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- sc.handlerFunc = GetDashboard
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be denied access", func() {
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- Convey("When user is an Org Viewer but has an edit permission", func() {
- role := m.ROLE_VIEWER
- mockResult := []*m.DashboardAclInfoDTO{
- {OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_EDIT},
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = mockResult
- return nil
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be able to get dashboard with edit rights", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be able to get dashboard with edit rights", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- })
- Convey("When user is an Org Viewer and viewers can edit", func() {
- role := m.ROLE_VIEWER
- setting.ViewersCanEdit = true
- mockResult := []*m.DashboardAclInfoDTO{
- {OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = mockResult
- return nil
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeFalse)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be able to get dashboard with edit rights but can save should be false", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeFalse)
- So(dash.Meta.CanAdmin, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- })
- Convey("When user is an Org Viewer but has an admin permission", func() {
- role := m.ROLE_VIEWER
- mockResult := []*m.DashboardAclInfoDTO{
- {OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_ADMIN},
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = mockResult
- return nil
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should be able to get dashboard with edit rights", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeTrue)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should be able to get dashboard with edit rights", func() {
- So(dash.Meta.CanEdit, ShouldBeTrue)
- So(dash.Meta.CanSave, ShouldBeTrue)
- So(dash.Meta.CanAdmin, ShouldBeTrue)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- })
- Convey("When user is an Org Editor but has a view permission", func() {
- role := m.ROLE_EDITOR
- mockResult := []*m.DashboardAclInfoDTO{
- {OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
- }
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = mockResult
- return nil
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- Convey("Should not be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeFalse)
- So(dash.Meta.CanSave, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- dash := GetDashboardShouldReturn200(sc)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- Convey("Should not be able to edit or save dashboard", func() {
- So(dash.Meta.CanEdit, ShouldBeFalse)
- So(dash.Meta.CanSave, ShouldBeFalse)
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by slug", func() {
- So(getDashboardQueries[0].Slug, ShouldEqual, "child-dash")
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- Convey("Should lookup dashboard by uid", func() {
- So(getDashboardQueries[0].Uid, ShouldEqual, "abcdefghi")
- })
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
- CallGetDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
- CallGetDashboardVersions(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- })
- Convey("Given two dashboards with the same title in different folders", t, func() {
- dashOne := m.NewDashboard("dash")
- dashOne.Id = 2
- dashOne.FolderId = 1
- dashOne.HasAcl = false
- dashTwo := m.NewDashboard("dash")
- dashTwo.Id = 4
- dashTwo.FolderId = 3
- dashTwo.HasAcl = false
- bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
- query.Result = false
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
- dashboards := []*m.Dashboard{dashOne, dashTwo}
- query.Result = dashboards
- return nil
- })
- role := m.ROLE_EDITOR
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- Convey("Should result in 412 Precondition failed", func() {
- So(sc.resp.Code, ShouldEqual, 412)
- result := sc.ToJSON()
- So(result.Get("status").MustString(), ShouldEqual, "multiple-slugs-exists")
- So(result.Get("message").MustString(), ShouldEqual, m.ErrDashboardsWithSameSlugExists.Error())
- })
- })
- })
- Convey("Post dashboard response tests", t, func() {
- // This tests that a valid request returns correct response
- Convey("Given a correct request for creating a dashboard", func() {
- cmd := m.SaveDashboardCommand{
- OrgId: 1,
- UserId: 5,
- Dashboard: simplejson.NewFromAny(map[string]interface{}{
- "title": "Dash",
- }),
- Overwrite: true,
- FolderId: 3,
- IsFolder: false,
- Message: "msg",
- }
- mock := &dashboards.FakeDashboardService{
- SaveDashboardResult: &m.Dashboard{
- Id: 2,
- Uid: "uid",
- Title: "Dash",
- Slug: "dash",
- Version: 2,
- },
- }
- postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", mock, cmd, func(sc *scenarioContext) {
- CallPostDashboardShouldReturnSuccess(sc)
- Convey("It should call dashboard service with correct data", func() {
- dto := mock.SavedDashboards[0]
- So(dto.OrgId, ShouldEqual, cmd.OrgId)
- So(dto.User.UserId, ShouldEqual, cmd.UserId)
- So(dto.Dashboard.FolderId, ShouldEqual, 3)
- So(dto.Dashboard.Title, ShouldEqual, "Dash")
- So(dto.Overwrite, ShouldBeTrue)
- So(dto.Message, ShouldEqual, "msg")
- })
- Convey("It should return correct response data", func() {
- result := sc.ToJSON()
- So(result.Get("status").MustString(), ShouldEqual, "success")
- So(result.Get("id").MustInt64(), ShouldEqual, 2)
- So(result.Get("uid").MustString(), ShouldEqual, "uid")
- So(result.Get("slug").MustString(), ShouldEqual, "dash")
- So(result.Get("url").MustString(), ShouldEqual, "/d/uid/dash")
- })
- })
- })
- // This tests that invalid requests returns expected error responses
- Convey("Given incorrect requests for creating a dashboard", func() {
- testCases := []struct {
- SaveError error
- ExpectedStatusCode int
- }{
- {SaveError: m.ErrDashboardNotFound, ExpectedStatusCode: 404},
- {SaveError: m.ErrFolderNotFound, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
- {SaveError: m.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
- {SaveError: m.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
- {SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
- {SaveError: m.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
- {SaveError: m.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
- {SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400},
- {SaveError: m.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
- {SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
- }
- cmd := m.SaveDashboardCommand{
- OrgId: 1,
- Dashboard: simplejson.NewFromAny(map[string]interface{}{
- "title": "",
- }),
- }
- for _, tc := range testCases {
- mock := &dashboards.FakeDashboardService{
- SaveDashboardError: tc.SaveError,
- }
- postDashboardScenario(fmt.Sprintf("Expect '%s' error when calling POST on", tc.SaveError.Error()), "/api/dashboards", "/api/dashboards", mock, cmd, func(sc *scenarioContext) {
- CallPostDashboard(sc)
- So(sc.resp.Code, ShouldEqual, tc.ExpectedStatusCode)
- })
- }
- })
- })
- Convey("Given two dashboards being compared", t, func() {
- mockResult := []*m.DashboardAclInfoDTO{}
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = mockResult
- return nil
- })
- bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
- query.Result = false
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
- query.Result = &m.DashboardVersion{
- Data: simplejson.NewFromAny(map[string]interface{}{
- "title": "Dash" + string(query.DashboardId),
- }),
- }
- return nil
- })
- cmd := dtos.CalculateDiffOptions{
- Base: dtos.CalculateDiffTarget{
- DashboardId: 1,
- Version: 1,
- },
- New: dtos.CalculateDiffTarget{
- DashboardId: 2,
- Version: 2,
- },
- DiffType: "basic",
- }
- Convey("when user does not have permission", func() {
- role := m.ROLE_VIEWER
- postDiffScenario("When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) {
- CallPostDashboard(sc)
- So(sc.resp.Code, ShouldEqual, 403)
- })
- })
- Convey("when user does have permission", func() {
- role := m.ROLE_ADMIN
- postDiffScenario("When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) {
- CallPostDashboard(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- })
- })
- })
- Convey("Given dashboard in folder being restored should restore to folder", t, func() {
- fakeDash := m.NewDashboard("Child dash")
- fakeDash.Id = 2
- fakeDash.FolderId = 1
- fakeDash.HasAcl = false
- bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
- query.Result = fakeDash
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
- query.Result = &m.DashboardVersion{
- DashboardId: 2,
- Version: 1,
- Data: fakeDash.Data,
- }
- return nil
- })
- mock := &dashboards.FakeDashboardService{
- SaveDashboardResult: &m.Dashboard{
- Id: 2,
- Uid: "uid",
- Title: "Dash",
- Slug: "dash",
- Version: 1,
- },
- }
- cmd := dtos.RestoreDashboardVersionCommand{
- Version: 1,
- }
- restoreDashboardVersionScenario("When calling POST on", "/api/dashboards/id/1/restore", "/api/dashboards/id/:dashboardId/restore", mock, cmd, func(sc *scenarioContext) {
- CallRestoreDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- dto := mock.SavedDashboards[0]
- So(dto.Dashboard.FolderId, ShouldEqual, 1)
- So(dto.Dashboard.Title, ShouldEqual, "Child dash")
- So(dto.Message, ShouldEqual, "Restored from version 1")
- })
- })
- Convey("Given dashboard in general folder being restored should restore to general folder", t, func() {
- fakeDash := m.NewDashboard("Child dash")
- fakeDash.Id = 2
- fakeDash.HasAcl = false
- bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
- query.Result = fakeDash
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
- query.Result = &m.DashboardVersion{
- DashboardId: 2,
- Version: 1,
- Data: fakeDash.Data,
- }
- return nil
- })
- mock := &dashboards.FakeDashboardService{
- SaveDashboardResult: &m.Dashboard{
- Id: 2,
- Uid: "uid",
- Title: "Dash",
- Slug: "dash",
- Version: 1,
- },
- }
- cmd := dtos.RestoreDashboardVersionCommand{
- Version: 1,
- }
- restoreDashboardVersionScenario("When calling POST on", "/api/dashboards/id/1/restore", "/api/dashboards/id/:dashboardId/restore", mock, cmd, func(sc *scenarioContext) {
- CallRestoreDashboardVersion(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- dto := mock.SavedDashboards[0]
- So(dto.Dashboard.FolderId, ShouldEqual, 0)
- So(dto.Dashboard.Title, ShouldEqual, "Child dash")
- So(dto.Message, ShouldEqual, "Restored from version 1")
- })
- })
- Convey("Given provisioned dashboard", t, func() {
- bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
- query.Result = []*m.Dashboard{{}}
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
- query.Result = &m.Dashboard{Id: 1}
- return nil
- })
- bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
- query.Result = true
- return nil
- })
- bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
- query.Result = []*m.DashboardAclInfoDTO{
- {OrgId: TestOrgID, DashboardId: 1, UserId: TestUserID, Permission: m.PERMISSION_EDIT},
- }
- return nil
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", m.ROLE_EDITOR, func(sc *scenarioContext) {
- CallDeleteDashboardBySlug(sc)
- Convey("Should result in 400", func() {
- So(sc.resp.Code, ShouldEqual, 400)
- result := sc.ToJSON()
- So(result.Get("error").MustString(), ShouldEqual, m.ErrDashboardCannotDeleteProvisionedDashboard.Error())
- })
- })
- loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/abcdefghi", "/api/dashboards/db/:uid", m.ROLE_EDITOR, func(sc *scenarioContext) {
- CallDeleteDashboardByUID(sc)
- Convey("Should result in 400", func() {
- So(sc.resp.Code, ShouldEqual, 400)
- result := sc.ToJSON()
- So(result.Get("error").MustString(), ShouldEqual, m.ErrDashboardCannotDeleteProvisionedDashboard.Error())
- })
- })
- })
- }
- func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
- CallGetDashboard(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- dash := dtos.DashboardFullWithMeta{}
- err := json.NewDecoder(sc.resp.Body).Decode(&dash)
- So(err, ShouldBeNil)
- return dash
- }
- func CallGetDashboard(sc *scenarioContext) {
- sc.handlerFunc = GetDashboard
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- }
- func CallGetDashboardVersion(sc *scenarioContext) {
- bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
- query.Result = &m.DashboardVersion{}
- return nil
- })
- sc.handlerFunc = GetDashboardVersion
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- }
- func CallGetDashboardVersions(sc *scenarioContext) {
- bus.AddHandler("test", func(query *m.GetDashboardVersionsQuery) error {
- query.Result = []*m.DashboardVersionDTO{}
- return nil
- })
- sc.handlerFunc = GetDashboardVersions
- sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
- }
- func CallDeleteDashboardBySlug(sc *scenarioContext) {
- bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
- return nil
- })
- sc.handlerFunc = DeleteDashboardBySlug
- sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
- }
- func CallDeleteDashboardByUID(sc *scenarioContext) {
- bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
- return nil
- })
- sc.handlerFunc = DeleteDashboardByUID
- sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
- }
- func CallPostDashboard(sc *scenarioContext) {
- sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
- }
- func CallRestoreDashboardVersion(sc *scenarioContext) {
- sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
- }
- func CallPostDashboardShouldReturnSuccess(sc *scenarioContext) {
- CallPostDashboard(sc)
- So(sc.resp.Code, ShouldEqual, 200)
- }
- func postDashboardScenario(desc string, url string, routePattern string, mock *dashboards.FakeDashboardService, cmd m.SaveDashboardCommand, fn scenarioFunc) {
- Convey(desc+" "+url, func() {
- defer bus.ClearBusHandlers()
- hs := HTTPServer{
- Bus: bus.GetBus(),
- Cfg: setting.NewCfg(),
- }
- sc := setupScenarioContext(url)
- sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
- sc.context = c
- sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
- return hs.PostDashboard(c, cmd)
- })
- origNewDashboardService := dashboards.NewService
- dashboards.MockDashboardService(mock)
- sc.m.Post(routePattern, sc.defaultHandler)
- defer func() {
- dashboards.NewService = origNewDashboardService
- }()
- fn(sc)
- })
- }
- func postDiffScenario(desc string, url string, routePattern string, cmd dtos.CalculateDiffOptions, role m.RoleType, fn scenarioFunc) {
- Convey(desc+" "+url, func() {
- defer bus.ClearBusHandlers()
- sc := setupScenarioContext(url)
- sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
- sc.context = c
- sc.context.SignedInUser = &m.SignedInUser{
- OrgId: TestOrgID,
- UserId: TestUserID,
- }
- sc.context.OrgRole = role
- return CalculateDashboardDiff(c, cmd)
- })
- sc.m.Post(routePattern, sc.defaultHandler)
- fn(sc)
- })
- }
- func restoreDashboardVersionScenario(desc string, url string, routePattern string, mock *dashboards.FakeDashboardService, cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc) {
- Convey(desc+" "+url, func() {
- defer bus.ClearBusHandlers()
- hs := HTTPServer{
- Cfg: setting.NewCfg(),
- Bus: bus.GetBus(),
- }
- sc := setupScenarioContext(url)
- sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
- sc.context = c
- sc.context.SignedInUser = &m.SignedInUser{
- OrgId: TestOrgID,
- UserId: TestUserID,
- }
- sc.context.OrgRole = m.ROLE_ADMIN
- return hs.RestoreDashboardVersion(c, cmd)
- })
- origNewDashboardService := dashboards.NewService
- dashboards.MockDashboardService(mock)
- sc.m.Post(routePattern, sc.defaultHandler)
- defer func() {
- dashboards.NewService = origNewDashboardService
- }()
- fn(sc)
- })
- }
- func (sc *scenarioContext) ToJSON() *simplejson.Json {
- var result *simplejson.Json
- err := json.NewDecoder(sc.resp.Body).Decode(&result)
- So(err, ShouldBeNil)
- return result
- }
|