From 127473f4a49c1aeeafa82fa8d70ba06843472112 Mon Sep 17 00:00:00 2001 From: Karl Persson <kalle.persson@grafana.com> Date: Tue, 29 Aug 2023 15:09:09 +0200 Subject: [PATCH] API: Remove usage of legacy dashboard guardian in tests (#73937) --- pkg/api/dashboard_test.go | 701 +++++++++++++------------------------- 1 file changed, 245 insertions(+), 456 deletions(-) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index ecf71818572..45f305a545f 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -27,6 +27,7 @@ import ( "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/plugins/manager/fakes" "github.com/grafana/grafana/pkg/registry/corekind" + "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" @@ -54,11 +55,11 @@ import ( "github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/star/startest" "github.com/grafana/grafana/pkg/services/tag/tagimpl" - "github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/web" + "github.com/grafana/grafana/pkg/web/webtest" ) func TestGetHomeDashboard(t *testing.T) { @@ -131,424 +132,247 @@ func newTestLive(t *testing.T, store db.DB) *live.GrafanaLive { return gLive } -// 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 TestHTTPServer_GetDashboard_AccessControl(t *testing.T) { + setup := func() *webtest.Server { + return SetupAPITestServer(t, func(hs *HTTPServer) { + dash := dashboards.NewDashboard("some dash") + dash.ID = 1 + dash.UID = "1" + + dashSvc := dashboards.NewFakeDashboardService(t) + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dash, nil).Maybe() + hs.DashboardService = dashSvc + + hs.Cfg = setting.NewCfg() + hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg) + hs.starService = startest.NewStarServiceFake() + hs.dashboardProvisioningService = mockDashboardProvisioningService{} + + guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) + }) + } + + getDashboard := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) { + return server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1"), userWithPermissions(1, permissions))) + } + + t.Run("Should not be able to get dashboard without correct permission", func(t *testing.T) { + server := setup() + + res, err := getDashboard(server, nil) + require.NoError(t, err) + + assert.Equal(t, http.StatusForbidden, res.StatusCode) + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should be able to get when user has permission to read dashboard", func(t *testing.T) { + server := setup() + + permissions := []accesscontrol.Permission{{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"}} + res, err := getDashboard(server, permissions) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode) + var data dtos.DashboardFullWithMeta + require.NoError(t, json.NewDecoder(res.Body).Decode(&data)) + + assert.Equal(t, data.Meta.CanSave, false) + assert.Equal(t, data.Meta.CanEdit, false) + assert.Equal(t, data.Meta.CanDelete, false) + assert.Equal(t, data.Meta.CanAdmin, false) + + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should set CanSave and CanEdit with correct permissions", func(t *testing.T) { + server := setup() + + res, err := getDashboard(server, []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"}, + {Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:1"}, + }) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode) + var data dtos.DashboardFullWithMeta + require.NoError(t, json.NewDecoder(res.Body).Decode(&data)) + + assert.Equal(t, data.Meta.CanSave, true) + assert.Equal(t, data.Meta.CanEdit, true) + assert.Equal(t, data.Meta.CanDelete, false) + assert.Equal(t, data.Meta.CanAdmin, false) + + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should set canDelete with correct permissions", func(t *testing.T) { + server := setup() + + res, err := getDashboard(server, []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"}, + {Action: dashboards.ActionDashboardsDelete, Scope: "dashboards:uid:1"}, + }) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode) + var data dtos.DashboardFullWithMeta + require.NoError(t, json.NewDecoder(res.Body).Decode(&data)) + + assert.Equal(t, data.Meta.CanSave, false) + assert.Equal(t, data.Meta.CanEdit, false) + assert.Equal(t, data.Meta.CanDelete, true) + assert.Equal(t, data.Meta.CanAdmin, false) + + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should set canAdmin with correct permissions", func(t *testing.T) { + server := setup() + + res, err := getDashboard(server, []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"}, + {Action: dashboards.ActionDashboardsPermissionsRead, Scope: "dashboards:uid:1"}, + {Action: dashboards.ActionDashboardsPermissionsWrite, Scope: "dashboards:uid:1"}, + }) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode) + var data dtos.DashboardFullWithMeta + require.NoError(t, json.NewDecoder(res.Body).Decode(&data)) + + assert.Equal(t, data.Meta.CanSave, false) + assert.Equal(t, data.Meta.CanEdit, false) + assert.Equal(t, data.Meta.CanDelete, false) + assert.Equal(t, data.Meta.CanAdmin, true) + + require.NoError(t, res.Body.Close()) + }) +} + +func TestHTTPServer_DeleteDashboardByUID_AccessControl(t *testing.T) { + setup := func() *webtest.Server { + return SetupAPITestServer(t, func(hs *HTTPServer) { + dash := dashboards.NewDashboard("some dash") + dash.ID = 1 + dash.UID = "1" + + dashSvc := dashboards.NewFakeDashboardService(t) + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dash, nil).Maybe() + dashSvc.On("DeleteDashboard", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + hs.DashboardService = dashSvc + + hs.Cfg = setting.NewCfg() + hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg) + hs.starService = startest.NewStarServiceFake() + + hs.LibraryPanelService = &mockLibraryPanelService{} + hs.LibraryElementService = &mockLibraryElementService{} + + pubDashService := publicdashboards.NewFakePublicDashboardService(t) + pubDashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil).Maybe() + hs.PublicDashboardsApi = api.ProvideApi(pubDashService, nil, hs.AccessControl, featuremgmt.WithFeatures()) + + guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) + }) + } + deleteDashboard := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) { + return server.Send(webtest.RequestWithSignedInUser(server.NewRequest(http.MethodDelete, "/api/dashboards/uid/1", nil), userWithPermissions(1, permissions))) + } + + t.Run("Should not be able to delete dashboard without correct permission", func(t *testing.T) { + server := setup() + res, err := deleteDashboard(server, []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsDelete, Scope: "dashboards:uid:2"}, + }) + require.NoError(t, err) + + assert.Equal(t, http.StatusForbidden, res.StatusCode) + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should be able to delete dashboard with correct permission", func(t *testing.T) { + server := setup() + res, err := deleteDashboard(server, []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsDelete, Scope: "dashboards:uid:1"}, + }) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode) + require.NoError(t, res.Body.Close()) + }) +} + +func TestHTTPServer_GetDashboardVersions_AccessControl(t *testing.T) { + setup := func() *webtest.Server { + return SetupAPITestServer(t, func(hs *HTTPServer) { + dash := dashboards.NewDashboard("some dash") + dash.ID = 1 + dash.UID = "1" + + dashSvc := dashboards.NewFakeDashboardService(t) + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dash, nil).Maybe() + dashSvc.On("DeleteDashboard", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + hs.DashboardService = dashSvc + + hs.Cfg = setting.NewCfg() + hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg) + hs.starService = startest.NewStarServiceFake() + + hs.dashboardVersionService = &dashvertest.FakeDashboardVersionService{ + ExpectedListDashboarVersions: []*dashver.DashboardVersionDTO{}, + ExpectedDashboardVersion: &dashver.DashboardVersionDTO{}, + } + + guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) + }) + } + + getVersion := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) { + return server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/versions/1"), userWithPermissions(1, permissions))) + } + + getVersions := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) { + return server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/versions"), userWithPermissions(1, permissions))) + } + + t.Run("Should not be able to list dashboard versions without correct permission", func(t *testing.T) { + server := setup() + + res, err := getVersions(server, []accesscontrol.Permission{}) + require.NoError(t, err) + assert.Equal(t, http.StatusForbidden, res.StatusCode) + require.NoError(t, res.Body.Close()) + + res, err = getVersion(server, []accesscontrol.Permission{}) + require.NoError(t, err) + assert.Equal(t, http.StatusForbidden, res.StatusCode) + + require.NoError(t, res.Body.Close()) + }) + + t.Run("Should be able to list dashboard versions with correct permission", func(t *testing.T) { + server := setup() + + permissions := []accesscontrol.Permission{ + {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"}, + {Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:1"}, + } + + res, err := getVersions(server, permissions) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + require.NoError(t, res.Body.Close()) + + res, err = getVersion(server, permissions) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + + require.NoError(t, res.Body.Close()) + }) +} func TestDashboardAPIEndpoint(t *testing.T) { - t.Run("Given a dashboard with a parent folder which does not have an ACL", func(t *testing.T) { - fakeDash := dashboards.NewDashboard("Child dash") - fakeDash.SetID(1) - fakeDash.SetUID("test-uid-one") - fakeDash.FolderID = 1 - fakeDash.HasACL = false - fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake() - fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersionDTO{CreatedBy: 1} - teamService := &teamtest.FakeService{} - dashboardService := dashboards.NewFakeDashboardService(t) - dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(fakeDash, nil) - mockSQLStore := dbtest.NewFakeDB() - - hs := &HTTPServer{ - Cfg: setting.NewCfg(), - pluginStore: &fakes.FakePluginStore{}, - SQLStore: mockSQLStore, - AccessControl: accesscontrolmock.New(), - Features: featuremgmt.WithFeatures(), - DashboardService: dashboardService, - dashboardVersionService: fakeDashboardVersionService, - Kinds: corekind.NewBase(nil), - QuotaService: quotatest.New(false, nil), - starService: startest.NewStarServiceFake(), - userService: &usertest.FakeUserService{ - ExpectedUser: &user.User{ID: 1, Login: "test-user"}, - }, - } - - setUp := func() { - viewerRole := org.RoleViewer - editorRole := org.RoleEditor - qResult := []*dashboards.DashboardACLInfoDTO{ - {Role: &viewerRole, Permission: dashboards.PERMISSION_VIEW}, - {Role: &editorRole, Permission: dashboards.PERMISSION_EDIT}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(hs.Cfg, mockSQLStore, dashboardService, teamService) - } - - // This tests two scenarios: - // 1. user is an org viewer - // 2. user is an org editor - - t.Run("When user is an Org Viewer", func(t *testing.T) { - role := org.RoleViewer - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.Equal(t, fakeDash.UID, dash.Dashboard.Get("uid").MustString()) - assert.False(t, dash.Meta.CanEdit) - assert.False(t, dash.Meta.CanSave) - assert.False(t, dash.Meta.CanAdmin) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", - "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - - hs.callGetDashboardVersion(sc) - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", - "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - - hs.callGetDashboardVersions(sc) - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Editor", func(t *testing.T) { - role := org.RoleEditor - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.True(t, dash.Meta.CanEdit) - assert.True(t, dash.Meta.CanSave) - assert.False(t, dash.Meta.CanAdmin) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", - "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - hs.callGetDashboardVersion(sc) - - assert.Equal(t, 200, sc.resp.Code) - var version *dashver.DashboardVersionMeta - err := json.NewDecoder(sc.resp.Body).Decode(&version) - require.NoError(t, err) - assert.NotEqual(t, anonString, version.CreatedBy) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", - "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUp() - hs.callGetDashboardVersions(sc) - - assert.Equal(t, 200, sc.resp.Code) - }, mockSQLStore) - }) - }) - - t.Run("Given a dashboard with a parent folder which has an ACL", func(t *testing.T) { - fakeDash := dashboards.NewDashboard("Child dash") - fakeDash.ID = 1 - fakeDash.FolderID = 1 - fakeDash.HasACL = true - fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake() - fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersionDTO{} - teamService := &teamtest.FakeService{} - dashboardService := dashboards.NewFakeDashboardService(t) - - dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(fakeDash, nil) - qResult := []*dashboards.DashboardACLInfoDTO{ - { - DashboardID: 1, - Permission: dashboards.PERMISSION_EDIT, - UserID: 200, - }, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - - mockSQLStore := dbtest.NewFakeDB() - cfg := setting.NewCfg() - sql := db.InitTestDB(t) - - hs := &HTTPServer{ - Cfg: cfg, - Live: newTestLive(t, sql), - LibraryPanelService: &mockLibraryPanelService{}, - LibraryElementService: &mockLibraryElementService{}, - SQLStore: mockSQLStore, - AccessControl: accesscontrolmock.New(), - DashboardService: dashboardService, - dashboardVersionService: fakeDashboardVersionService, - Features: featuremgmt.WithFeatures(), - Kinds: corekind.NewBase(nil), - } - - setUp := func() { - cfg.ViewersCanEdit = false - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) - } - - // 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 - - t.Run("When user is an Org Viewer and has no permissions for this dashboard", func(t *testing.T) { - role := org.RoleViewer - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - sc.handlerFunc = hs.GetDashboard - sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - hs.callDeleteDashboardByUID(t, sc, dashboardService, nil) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", - "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - hs.callGetDashboardVersion(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", - "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUp() - hs.callGetDashboardVersions(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Editor and has no permissions for this dashboard", func(t *testing.T) { - role := org.RoleEditor - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - sc.sqlStore = mockSQLStore - sc.handlerFunc = hs.GetDashboard - sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUp() - hs.callDeleteDashboardByUID(t, sc, dashboardService, nil) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", - "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUp() - hs.callGetDashboardVersion(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", - "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUp() - hs.callGetDashboardVersions(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Viewer but has an edit permission", func(t *testing.T) { - role := org.RoleViewer - - setUpInner := func() { - cfg.ViewersCanEdit = false - - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := []*dashboards.DashboardACLInfoDTO{ - {OrgID: 1, DashboardID: 2, UserID: 1, Permission: dashboards.PERMISSION_EDIT}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) - } - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", - "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.True(t, dash.Meta.CanEdit) - assert.True(t, dash.Meta.CanSave) - assert.False(t, dash.Meta.CanAdmin) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := dashboards.NewDashboard("test") - dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil) - dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil) - - pubdashService := publicdashboards.NewFakePublicDashboardService(t) - pubdashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil) - hs.callDeleteDashboardByUID(t, sc, dashboardService, pubdashService) - - assert.Equal(t, 200, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Viewer and viewers can edit", func(t *testing.T) { - role := org.RoleViewer - - setUpInner := func() { - cfg.ViewersCanEdit = true - - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := []*dashboards.DashboardACLInfoDTO{ - {OrgID: 1, DashboardID: 2, UserID: 1, Permission: dashboards.PERMISSION_VIEW}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) - } - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - - require.True(t, cfg.ViewersCanEdit) - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.True(t, dash.Meta.CanEdit) - assert.False(t, dash.Meta.CanSave) - assert.False(t, dash.Meta.CanAdmin) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - - hs.callDeleteDashboardByUID(t, sc, dashboardService, nil) - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Viewer but has an admin permission", func(t *testing.T) { - role := org.RoleViewer - - setUpInner := func() { - cfg.ViewersCanEdit = true - - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := []*dashboards.DashboardACLInfoDTO{ - {OrgID: 1, DashboardID: 2, UserID: 1, Permission: dashboards.PERMISSION_ADMIN}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) - } - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.True(t, dash.Meta.CanEdit) - assert.True(t, dash.Meta.CanSave) - assert.True(t, dash.Meta.CanAdmin) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - sc.sqlStore = mockSQLStore - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := dashboards.NewDashboard("test") - dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil) - dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil) - pubdashService := publicdashboards.NewFakePublicDashboardService(t) - pubdashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil) - hs.callDeleteDashboardByUID(t, sc, dashboardService, pubdashService) - - assert.Equal(t, 200, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUpInner() - - hs.callGetDashboardVersion(sc) - assert.Equal(t, 200, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUpInner() - - hs.callGetDashboardVersions(sc) - assert.Equal(t, 200, sc.resp.Code) - }, mockSQLStore) - }) - - t.Run("When user is an Org Editor but has a view permission", func(t *testing.T) { - role := org.RoleEditor - - setUpInner := func() { - cfg := setting.NewCfg() - dashboardService := dashboards.NewFakeDashboardService(t) - qResult := []*dashboards.DashboardACLInfoDTO{ - {OrgID: 1, DashboardID: 2, UserID: 1, Permission: dashboards.PERMISSION_VIEW}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) - } - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - sc.sqlStore = mockSQLStore - dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) - - assert.False(t, dash.Meta.CanEdit) - assert.False(t, dash.Meta.CanSave) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { - setUpInner() - hs.callDeleteDashboardByUID(t, sc, dashboardService, nil) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) { - setUpInner() - hs.callGetDashboardVersion(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - - loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) { - setUpInner() - hs.callGetDashboardVersions(sc) - - assert.Equal(t, 403, sc.resp.Code) - }, mockSQLStore) - }) - }) - t.Run("Given two dashboards with the same title in different folders", func(t *testing.T) { dashOne := dashboards.NewDashboard("dash") dashOne.ID = 2 @@ -776,15 +600,6 @@ func TestDashboardAPIEndpoint(t *testing.T) { }, } sqlmock := dbtest.NewFakeDB() - setUp := func() { - teamSvc := &teamtest.FakeService{} - dashSvc := dashboards.NewFakeDashboardService(t) - dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(nil, nil) - qResult := &dashboards.Dashboard{} - dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(setting.NewCfg(), sqlmock, dashSvc, teamSvc) - } - cmd := dtos.CalculateDiffOptions{ Base: dtos.CalculateDiffTarget{ DashboardId: 1, @@ -800,7 +615,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { t.Run("when user does not have permission", func(t *testing.T) { role := org.RoleViewer postDiffScenario(t, "When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) { - setUp() + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: false}) callPostDashboard(sc) assert.Equal(t, 403, sc.resp.Code) @@ -810,6 +625,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { t.Run("when user does have permission", func(t *testing.T) { role := org.RoleAdmin postDiffScenario(t, "When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) { + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) // This test shouldn't hit GetDashboardACLInfoList, so no setup needed sc.dashboardVersionService = fakeDashboardVersionService callPostDashboard(sc) @@ -892,16 +708,13 @@ func TestDashboardAPIEndpoint(t *testing.T) { dashboardStore := dashboards.NewFakeDashboardStore(t) dashboardStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{ExternalID: "/dashboard1.json"}, nil).Once() - teamService := &teamtest.FakeService{} dashboardService := dashboards.NewFakeDashboardService(t) dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`)) require.NoError(t, err) qResult := &dashboards.Dashboard{ID: 1, Data: dataValue} dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil) - qResult2 := []*dashboards.DashboardACLInfoDTO{{OrgID: testOrgID, DashboardID: 1, UserID: testUserID, Permission: dashboards.PERMISSION_EDIT}} - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult2, nil) - guardian.InitLegacyGuardian(setting.NewCfg(), mockSQLStore, dashboardService, teamService) + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true}) loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) { fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background()) @@ -954,14 +767,11 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) { fakeDash := dashboards.NewDashboard("Child dash") fakeDash.ID = 1 fakeDash.FolderID = 1 - fakeDash.HasACL = false fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake() dashboardService := dashboards.NewFakeDashboardService(t) dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(fakeDash, nil) - teamService := &teamtest.FakeService{} - mockSQLStore := dbtest.NewFakeDB() cfg := setting.NewCfg() @@ -984,14 +794,7 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) { } setUp := func() { - viewerRole := org.RoleViewer - editorRole := org.RoleEditor - qResult := []*dashboards.DashboardACLInfoDTO{ - {Role: &viewerRole, Permission: dashboards.PERMISSION_VIEW}, - {Role: &editorRole, Permission: dashboards.PERMISSION_EDIT}, - } - dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) } loggedInUserScenarioWithRole(t, "When user exists and calling GET on", "GET", "/api/dashboards/id/2/versions", @@ -1143,25 +946,11 @@ func (hs *HTTPServer) callGetDashboard(sc *scenarioContext) { sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() } -func (hs *HTTPServer) callGetDashboardVersion(sc *scenarioContext) { - sc.handlerFunc = hs.GetDashboardVersion - sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() -} - func (hs *HTTPServer) callGetDashboardVersions(sc *scenarioContext) { sc.handlerFunc = hs.GetDashboardVersions sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() } -func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T, - sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService, mockPubdashService *publicdashboards.FakePublicDashboardService) { - hs.DashboardService = mockDashboard - pubdashApi := api.ProvideApi(mockPubdashService, nil, nil, featuremgmt.WithFeatures()) - hs.PublicDashboardsApi = pubdashApi - sc.handlerFunc = hs.DeleteDashboardByUID - sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec() -} - func callPostDashboard(sc *scenarioContext) { sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() }