diff --git a/pkg/api/accesscontrol.go b/pkg/api/accesscontrol.go index 55945b28a4d..53c6032c3f3 100644 --- a/pkg/api/accesscontrol.go +++ b/pkg/api/accesscontrol.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/org" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/grafanads" ) @@ -68,7 +67,7 @@ func (hs *HTTPServer) declareFixedRoles() error { Grants: []string{string(org.RoleEditor)}, } - if setting.ViewersCanEdit { + if hs.Cfg.ViewersCanEdit { datasourcesExplorerRole.Grants = append(datasourcesExplorerRole.Grants, string(org.RoleViewer)) } diff --git a/pkg/api/annotations_test.go b/pkg/api/annotations_test.go index 23c7a24a2b6..0ea84fd4eba 100644 --- a/pkg/api/annotations_test.go +++ b/pkg/api/annotations_test.go @@ -697,7 +697,7 @@ func setUpACL() { } }).Return(result, nil) - guardian.InitLegacyGuardian(store, dashSvc, teamSvc) + guardian.InitLegacyGuardian(setting.NewCfg(), store, dashSvc, teamSvc) } func setUpRBACGuardian(t *testing.T) { diff --git a/pkg/api/api.go b/pkg/api/api.go index 0e558c0cd65..76f8cc4223d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -65,6 +65,7 @@ func (hs *HTTPServer) registerRoutes() { reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin) reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg) redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg) + ensureEditorOrViewerCanEdit := middleware.EnsureEditorOrViewerCanEdit(hs.Cfg) authorize := ac.Middleware(hs.AccessControl) authorizeInOrg := ac.AuthorizeInOrgMiddleware(hs.AccessControl, hs.accesscontrolService, hs.userService) quota := middleware.Quota(hs.QuotaService) @@ -177,7 +178,7 @@ func (hs *HTTPServer) registerRoutes() { if f, ok := reqSignedIn.(func(c *contextmodel.ReqContext)); ok { f(c) } - middleware.EnsureEditorOrViewerCanEdit(c) + ensureEditorOrViewerCanEdit(c) }, ac.EvalPermission(ac.ActionDatasourcesExplore)), hs.Index) r.Get("/playlists/", reqSignedIn, hs.Index) diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index f9887d07c1c..63fd69a80bd 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -340,6 +340,6 @@ func setUp(confs ...setUpConf) *HTTPServer { dashSvc := &dashboards.FakeDashboardService{} qResult := aclMockResp dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(store, dashSvc, teamSvc) + guardian.InitLegacyGuardian(setting.NewCfg(), store, dashSvc, teamSvc) return hs } diff --git a/pkg/api/dashboard_snapshot_test.go b/pkg/api/dashboard_snapshot_test.go index 5cede0b7c58..4028904cbc9 100644 --- a/pkg/api/dashboard_snapshot_test.go +++ b/pkg/api/dashboard_snapshot_test.go @@ -82,7 +82,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) { dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(nil, nil).Maybe() hs.DashboardService = dashSvc - guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc) + guardian.InitLegacyGuardian(setting.NewCfg(), sc.sqlStore, dashSvc, teamSvc) sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec() assert.Equal(t, 403, sc.resp.Code) @@ -133,7 +133,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) { dashSvc := dashboards.NewFakeDashboardService(t) dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(nil, errutil.Error{PublicMessage: "some error"}).Maybe() - guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc) + guardian.InitLegacyGuardian(sc.cfg, sc.sqlStore, dashSvc, teamSvc) d := setUpSnapshotTest(t, 0, ts.URL) hs := buildHttpServer(d, true) hs.DashboardService = dashSvc @@ -153,7 +153,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) { dashSvc := dashboards.NewFakeDashboardService(t) dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(nil, dashboards.ErrDashboardNotFound).Maybe() - guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc) + guardian.InitLegacyGuardian(sc.cfg, sc.sqlStore, dashSvc, teamSvc) d := setUpSnapshotTest(t, 0, ts.URL) hs := buildHttpServer(d, true) hs.DashboardService = dashSvc @@ -185,7 +185,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) { {Role: &editorRole, Permission: dashboards.PERMISSION_EDIT}, } dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResultACL, nil) - guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc) + guardian.InitLegacyGuardian(sc.cfg, sc.sqlStore, dashSvc, teamSvc) d := setUpSnapshotTest(t, 0, ts.URL) hs := buildHttpServer(d, true) hs.DashboardService = dashSvc diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index a53b25cbce5..a5931e67793 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -9,12 +9,13 @@ import ( "os" "testing" - "github.com/grafana/grafana/pkg/services/publicdashboards" - "github.com/grafana/grafana/pkg/services/publicdashboards/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/services/publicdashboards" + "github.com/grafana/grafana/pkg/services/publicdashboards/api" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" @@ -165,7 +166,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { {Role: &editorRole, Permission: dashboards.PERMISSION_EDIT}, } dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil) - guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService) + guardian.InitLegacyGuardian(hs.Cfg, mockSQLStore, dashboardService, teamService) } // This tests two scenarios: @@ -274,12 +275,8 @@ func TestDashboardAPIEndpoint(t *testing.T) { } setUp := func() { - origCanEdit := setting.ViewersCanEdit - t.Cleanup(func() { - setting.ViewersCanEdit = origCanEdit - }) - setting.ViewersCanEdit = false - guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService) + cfg.ViewersCanEdit = false + guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) } // This tests six scenarios: @@ -370,18 +367,14 @@ func TestDashboardAPIEndpoint(t *testing.T) { role := org.RoleViewer setUpInner := func() { - origCanEdit := setting.ViewersCanEdit - t.Cleanup(func() { - setting.ViewersCanEdit = origCanEdit - }) - setting.ViewersCanEdit = false + 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(mockSQLStore, dashboardService, teamService) + guardian.InitLegacyGuardian(cfg, mockSQLStore, dashboardService, teamService) } loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", @@ -414,24 +407,20 @@ func TestDashboardAPIEndpoint(t *testing.T) { role := org.RoleViewer setUpInner := func() { - origCanEdit := setting.ViewersCanEdit - t.Cleanup(func() { - setting.ViewersCanEdit = origCanEdit - }) - setting.ViewersCanEdit = true + 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(mockSQLStore, dashboardService, teamService) + 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, setting.ViewersCanEdit) + require.True(t, cfg.ViewersCanEdit) sc.sqlStore = mockSQLStore dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil) @@ -452,18 +441,14 @@ func TestDashboardAPIEndpoint(t *testing.T) { role := org.RoleViewer setUpInner := func() { - origCanEdit := setting.ViewersCanEdit - t.Cleanup(func() { - setting.ViewersCanEdit = origCanEdit - }) - setting.ViewersCanEdit = true + 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(mockSQLStore, dashboardService, teamService) + 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) { @@ -509,12 +494,13 @@ func TestDashboardAPIEndpoint(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(mockSQLStore, dashboardService, teamService) + 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) { @@ -782,7 +768,7 @@ func TestDashboardAPIEndpoint(t *testing.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(sqlmock, dashSvc, teamSvc) + guardian.InitLegacyGuardian(setting.NewCfg(), sqlmock, dashSvc, teamSvc) } cmd := dtos.CalculateDiffOptions{ @@ -901,7 +887,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { 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(mockSQLStore, dashboardService, teamService) + guardian.InitLegacyGuardian(setting.NewCfg(), mockSQLStore, dashboardService, teamService) 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()) diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index d9c8c0127bb..d6e34ac4819 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -245,7 +245,7 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st qResult := &dashboards.Dashboard{} dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil) store := dbtest.NewFakeDB() - guardian.InitLegacyGuardian(store, dashSvc, teamSvc) + guardian.InitLegacyGuardian(setting.NewCfg(), store, dashSvc, teamSvc) folderPermissions := acmock.NewMockedPermissionsService() folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) hs := HTTPServer{ diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index d93715b483a..ee87d065349 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -133,7 +133,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro ExternalUserMngInfo: setting.ExternalUserMngInfo, ExternalUserMngLinkUrl: setting.ExternalUserMngLinkUrl, ExternalUserMngLinkName: setting.ExternalUserMngLinkName, - ViewersCanEdit: setting.ViewersCanEdit, + ViewersCanEdit: hs.Cfg.ViewersCanEdit, AngularSupportEnabled: hs.Cfg.AngularSupportEnabled, EditorsCanAdmin: hs.Cfg.EditorsCanAdmin, DisableSanitizeHtml: hs.Cfg.DisableSanitizeHtml, diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 8ac2e457c7d..1d3284898ad 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -83,9 +83,11 @@ func removeForceLoginParams(str string) string { return forceLoginParamsRegexp.ReplaceAllString(str, "") } -func EnsureEditorOrViewerCanEdit(c *contextmodel.ReqContext) { - if !c.SignedInUser.HasRole(org.RoleEditor) && !setting.ViewersCanEdit { - accessForbidden(c) +func EnsureEditorOrViewerCanEdit(cfg *setting.Cfg) func(c *contextmodel.ReqContext) { + return func(c *contextmodel.ReqContext) { + if !c.SignedInUser.HasRole(org.RoleEditor) && !cfg.ViewersCanEdit { + accessForbidden(c) + } } } diff --git a/pkg/services/authn/authnimpl/usage_stats.go b/pkg/services/authn/authnimpl/usage_stats.go index 6ca1086a259..d945602e4c1 100644 --- a/pkg/services/authn/authnimpl/usage_stats.go +++ b/pkg/services/authn/authnimpl/usage_stats.go @@ -4,7 +4,6 @@ import ( "context" "github.com/grafana/grafana/pkg/services/authn" - "github.com/grafana/grafana/pkg/setting" ) func (s *Service) getUsageStats(ctx context.Context) (map[string]interface{}, error) { @@ -30,7 +29,7 @@ func (s *Service) getUsageStats(ctx context.Context) (map[string]interface{}, er // FIXME: Move this to accesscontrol OSS. // FIXME: Access Control OSS usage stats is currently disabled if Enterprise is enabled. m["stats.authz.viewers_can_edit.count"] = 0 - if setting.ViewersCanEdit { + if s.cfg.ViewersCanEdit { m["stats.authz.viewers_can_edit.count"] = 1 } diff --git a/pkg/services/dashboards/service/dashboard_service_integration_test.go b/pkg/services/dashboards/service/dashboard_service_integration_test.go index f6f1f4a5020..682278dbeb6 100644 --- a/pkg/services/dashboards/service/dashboard_service_integration_test.go +++ b/pkg/services/dashboards/service/dashboard_service_integration_test.go @@ -836,7 +836,7 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc accesscontrolmock.New(), foldertest.NewFakeService(), ) - guardian.InitLegacyGuardian(sqlStore, service, &teamtest.FakeService{}) + guardian.InitLegacyGuardian(cfg, sqlStore, service, &teamtest.FakeService{}) savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore) savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.ID, sqlStore) diff --git a/pkg/services/guardian/accesscontrol_guardian.go b/pkg/services/guardian/accesscontrol_guardian.go index 462b9fcbcbb..2b3162d4546 100644 --- a/pkg/services/guardian/accesscontrol_guardian.go +++ b/pkg/services/guardian/accesscontrol_guardian.go @@ -23,7 +23,7 @@ var _ DashboardGuardian = new(AccessControlDashboardGuardian) // NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardId. func NewAccessControlDashboardGuardian( - ctx context.Context, dashboardId int64, user *user.SignedInUser, + ctx context.Context, cfg *setting.Cfg, dashboardId int64, user *user.SignedInUser, store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, @@ -48,6 +48,7 @@ func NewAccessControlDashboardGuardian( return &AccessControlDashboardGuardian{ ctx: ctx, + cfg: cfg, log: log.New("dashboard.permissions"), dashboard: dashboard, user: user, @@ -61,7 +62,7 @@ func NewAccessControlDashboardGuardian( // NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardUID. func NewAccessControlDashboardGuardianByUID( - ctx context.Context, dashboardUID string, user *user.SignedInUser, + ctx context.Context, cfg *setting.Cfg, dashboardUID string, user *user.SignedInUser, store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, @@ -85,6 +86,7 @@ func NewAccessControlDashboardGuardianByUID( } return &AccessControlDashboardGuardian{ + cfg: cfg, ctx: ctx, log: log.New("dashboard.permissions"), dashboard: dashboard, @@ -101,13 +103,14 @@ func NewAccessControlDashboardGuardianByUID( // This constructor should be preferred over the other two if the dashboard in available // since it avoids querying the database for fetching the dashboard. func NewAccessControlDashboardGuardianByDashboard( - ctx context.Context, dashboard *dashboards.Dashboard, user *user.SignedInUser, + ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user *user.SignedInUser, store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardService dashboards.DashboardService, ) (*AccessControlDashboardGuardian, error) { return &AccessControlDashboardGuardian{ + cfg: cfg, ctx: ctx, log: log.New("dashboard.permissions"), dashboard: dashboard, @@ -121,6 +124,7 @@ func NewAccessControlDashboardGuardianByDashboard( } type AccessControlDashboardGuardian struct { + cfg *setting.Cfg ctx context.Context log log.Logger dashboard *dashboards.Dashboard @@ -151,7 +155,7 @@ func (a *AccessControlDashboardGuardian) CanEdit() (bool, error) { return false, ErrGuardianDashboardNotFound } - if setting.ViewersCanEdit { + if a.cfg.ViewersCanEdit { return a.CanView() } diff --git a/pkg/services/guardian/accesscontrol_guardian_test.go b/pkg/services/guardian/accesscontrol_guardian_test.go index b5ddc437648..bb4f914c3bb 100644 --- a/pkg/services/guardian/accesscontrol_guardian_test.go +++ b/pkg/services/guardian/accesscontrol_guardian_test.go @@ -108,13 +108,14 @@ func TestAccessControlDashboardGuardian_CanSave(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t)) + guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, nil, testDashSvc(t)) can, err := guardian.CanSave() require.NoError(t, err) assert.Equal(t, tt.expected, can) }) } } + func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) { tests := []accessControlGuardianTestCase{ { @@ -199,12 +200,11 @@ func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t)) + cfg := setting.NewCfg() + cfg.ViewersCanEdit = tt.viewersCanEdit + dashSvc := testDashSvc(t) + guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, cfg, dashSvc) - if tt.viewersCanEdit { - setting.ViewersCanEdit = true - defer func() { setting.ViewersCanEdit = false }() - } can, err := guardian.CanEdit() require.NoError(t, err) assert.Equal(t, tt.expected, can) @@ -283,7 +283,7 @@ func TestAccessControlDashboardGuardian_CanView(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t)) + guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, nil, testDashSvc(t)) can, err := guardian.CanView() require.NoError(t, err) @@ -387,7 +387,7 @@ func TestAccessControlDashboardGuardian_CanAdmin(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t)) + guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, nil, testDashSvc(t)) can, err := guardian.CanAdmin() require.NoError(t, err) @@ -467,7 +467,7 @@ func TestAccessControlDashboardGuardian_CanDelete(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t)) + guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, nil, testDashSvc(t)) can, err := guardian.CanDelete() require.NoError(t, err) @@ -531,7 +531,7 @@ func TestAccessControlDashboardGuardian_CanCreate(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, "0", tt.permissions, nil) + guardian, _ := setupAccessControlGuardianTest(t, "0", tt.permissions, nil, nil) can, err := guardian.CanCreate(tt.folderID, tt.isFolder) require.NoError(t, err) @@ -563,7 +563,7 @@ func TestAccessControlDashboardGuardian_GetHiddenACL(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - guardian, _ := setupAccessControlGuardianTest(t, "1", nil, testDashSvc(t)) + guardian, _ := setupAccessControlGuardianTest(t, "1", nil, nil, testDashSvc(t)) mocked := accesscontrolmock.NewMockedPermissionsService() guardian.dashboardPermissionsService = mocked @@ -585,7 +585,10 @@ func TestAccessControlDashboardGuardian_GetHiddenACL(t *testing.T) { } } -func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []accesscontrol.Permission, dashboardSvc dashboards.DashboardService) (*AccessControlDashboardGuardian, *dashboards.Dashboard) { +func setupAccessControlGuardianTest(t *testing.T, uid string, + permissions []accesscontrol.Permission, + cfg *setting.Cfg, + dashboardSvc dashboards.DashboardService) (*AccessControlDashboardGuardian, *dashboards.Dashboard) { t.Helper() store := db.InitTestDB(t) @@ -626,13 +629,13 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce require.NoError(t, err) folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( - setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, foldertest.NewFakeService(), ac, teamSvc, userSvc) + cfg, routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, foldertest.NewFakeService(), ac, teamSvc, userSvc) require.NoError(t, err) dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions( - setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, foldertest.NewFakeService(), ac, teamSvc, userSvc) + cfg, routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, foldertest.NewFakeService(), ac, teamSvc, userSvc) require.NoError(t, err) - g, err := NewAccessControlDashboardGuardian(context.Background(), dash.ID, &user.SignedInUser{OrgID: 1}, store, ac, folderPermissions, dashboardPermissions, dashboardSvc) + g, err := NewAccessControlDashboardGuardian(context.Background(), cfg, dash.ID, &user.SignedInUser{OrgID: 1}, store, ac, folderPermissions, dashboardPermissions, dashboardSvc) require.NoError(t, err) g.dashboard = dash return g, dash diff --git a/pkg/services/guardian/guardian.go b/pkg/services/guardian/guardian.go index f47fe6398e1..614702bebe4 100644 --- a/pkg/services/guardian/guardian.go +++ b/pkg/services/guardian/guardian.go @@ -42,6 +42,7 @@ type DashboardGuardian interface { } type dashboardGuardianImpl struct { + cfg *setting.Cfg user *user.SignedInUser dashId int64 orgId int64 @@ -73,7 +74,7 @@ var NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId } // newDashboardGuardian creates a dashboard guardian by the provided dashId. -func newDashboardGuardian(ctx context.Context, dashId int64, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { +func newDashboardGuardian(ctx context.Context, cfg *setting.Cfg, dashId int64, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { if dashId != 0 { q := &dashboards.GetDashboardQuery{ ID: dashId, @@ -89,6 +90,7 @@ func newDashboardGuardian(ctx context.Context, dashId int64, orgId int64, user * } return &dashboardGuardianImpl{ + cfg: cfg, user: user, dashId: dashId, orgId: orgId, @@ -101,7 +103,7 @@ func newDashboardGuardian(ctx context.Context, dashId int64, orgId int64, user * } // newDashboardGuardianByUID creates a dashboard guardian by the provided dashUID. -func newDashboardGuardianByUID(ctx context.Context, dashUID string, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { +func newDashboardGuardianByUID(ctx context.Context, cfg *setting.Cfg, dashUID string, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { dashID := int64(0) if dashUID != "" { q := &dashboards.GetDashboardQuery{ @@ -120,6 +122,7 @@ func newDashboardGuardianByUID(ctx context.Context, dashUID string, orgId int64, } return &dashboardGuardianImpl{ + cfg: cfg, user: user, dashId: dashID, orgId: orgId, @@ -134,8 +137,9 @@ func newDashboardGuardianByUID(ctx context.Context, dashUID string, orgId int64, // newDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard. // This constructor should be preferred over the other two if the dashboard in available // since it avoids querying the database for fetching the dashboard. -func newDashboardGuardianByDashboard(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { +func newDashboardGuardianByDashboard(ctx context.Context, cfg *setting.Cfg, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) { return &dashboardGuardianImpl{ + cfg: cfg, user: user, dashId: dash.ID, orgId: orgId, @@ -152,7 +156,7 @@ func (g *dashboardGuardianImpl) CanSave() (bool, error) { } func (g *dashboardGuardianImpl) CanEdit() (bool, error) { - if setting.ViewersCanEdit { + if g.cfg.ViewersCanEdit { return g.HasPermission(dashboards.PERMISSION_VIEW) } diff --git a/pkg/services/guardian/guardian_test.go b/pkg/services/guardian/guardian_test.go index d7d3a8b5a9d..022eb93905f 100644 --- a/pkg/services/guardian/guardian_test.go +++ b/pkg/services/guardian/guardian_test.go @@ -714,7 +714,7 @@ func TestGuardianGetHiddenACL(t *testing.T) { UserID: 1, Login: "user1", } - g, err := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) + g, err := newDashboardGuardian(context.Background(), cfg, dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) require.NoError(t, err) hiddenACL, err := g.GetHiddenACL(cfg) @@ -735,7 +735,7 @@ func TestGuardianGetHiddenACL(t *testing.T) { qResult := &dashboards.Dashboard{} dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) { }).Return(qResult, nil) - g, err := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) + g, err := newDashboardGuardian(context.Background(), cfg, dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) require.NoError(t, err) hiddenACL, err := g.GetHiddenACL(cfg) @@ -777,7 +777,7 @@ func TestGuardianGetACLWithoutDuplicates(t *testing.T) { UserID: 1, Login: "user1", } - g, err := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) + g, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) require.NoError(t, err) acl, err := g.GetACLWithoutDuplicates() diff --git a/pkg/services/guardian/guardian_util_test.go b/pkg/services/guardian/guardian_util_test.go index ba84307fe48..3f3374fc401 100644 --- a/pkg/services/guardian/guardian_util_test.go +++ b/pkg/services/guardian/guardian_util_test.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/setting" ) type scenarioContext struct { @@ -54,7 +55,7 @@ func orgRoleScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFu UID: q.UID, } }).Return(qResult, nil) - guard, err := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, fakeDashboardService, &teamtest.FakeService{}) + guard, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, fakeDashboardService, &teamtest.FakeService{}) require.NoError(t, err) sc := &scenarioContext{ @@ -86,7 +87,7 @@ func apiKeyScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFun UID: q.UID, } }).Return(qResult, nil) - guard, err := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) + guard, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{}) require.NoError(t, err) sc := &scenarioContext{ @@ -128,7 +129,7 @@ func permissionScenario(desc string, dashboardID int64, sc *scenarioContext, }).Return(qResultDash, nil) sc.permissionScenario = desc - g, err := newDashboardGuardian(context.Background(), dashboardID, sc.givenUser.OrgID, sc.givenUser, store, dashSvc, teamSvc) + g, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, sc.givenUser.OrgID, sc.givenUser, store, dashSvc, teamSvc) require.NoError(t, err) sc.g = g diff --git a/pkg/services/guardian/provider.go b/pkg/services/guardian/provider.go index 82d839db7b0..454db8d39ae 100644 --- a/pkg/services/guardian/provider.go +++ b/pkg/services/guardian/provider.go @@ -8,51 +8,52 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/setting" ) type Provider struct{} func ProvideService( - store db.DB, ac accesscontrol.AccessControl, + cfg *setting.Cfg, store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardService dashboards.DashboardService, teamService team.Service, ) *Provider { if !ac.IsDisabled() { // TODO: Fix this hack, see https://github.com/grafana/grafana-enterprise/issues/2935 - InitAccessControlGuardian(store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) + InitAccessControlGuardian(cfg, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) } else { - InitLegacyGuardian(store, dashboardService, teamService) + InitLegacyGuardian(cfg, store, dashboardService, teamService) } return &Provider{} } -func InitLegacyGuardian(store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) { +func InitLegacyGuardian(cfg *setting.Cfg, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) { New = func(ctx context.Context, dashId int64, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return newDashboardGuardian(ctx, dashId, orgId, user, store, dashSvc, teamSvc) + return newDashboardGuardian(ctx, cfg, dashId, orgId, user, store, dashSvc, teamSvc) } NewByUID = func(ctx context.Context, dashUID string, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return newDashboardGuardianByUID(ctx, dashUID, orgId, user, store, dashSvc, teamSvc) + return newDashboardGuardianByUID(ctx, cfg, dashUID, orgId, user, store, dashSvc, teamSvc) } NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return newDashboardGuardianByDashboard(ctx, dash, orgId, user, store, dashSvc, teamSvc) + return newDashboardGuardianByDashboard(ctx, cfg, dash, orgId, user, store, dashSvc, teamSvc) } } func InitAccessControlGuardian( - store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, + cfg *setting.Cfg, store db.DB, ac accesscontrol.AccessControl, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardService dashboards.DashboardService, ) { New = func(ctx context.Context, dashId int64, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return NewAccessControlDashboardGuardian(ctx, dashId, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) + return NewAccessControlDashboardGuardian(ctx, cfg, dashId, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) } NewByUID = func(ctx context.Context, dashUID string, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return NewAccessControlDashboardGuardianByUID(ctx, dashUID, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) + return NewAccessControlDashboardGuardianByUID(ctx, cfg, dashUID, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) } NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) { - return NewAccessControlDashboardGuardianByDashboard(ctx, dash, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) + return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService) } } diff --git a/pkg/services/libraryelements/libraryelements_test.go b/pkg/services/libraryelements/libraryelements_test.go index 366e5ec98e3..2da4b10263a 100644 --- a/pkg/services/libraryelements/libraryelements_test.go +++ b/pkg/services/libraryelements/libraryelements_test.go @@ -392,7 +392,7 @@ func validateAndUnMarshalArrayResponse(t *testing.T, resp response.Response) lib func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) { t.Helper() store := dbtest.NewFakeDB() - guardian.InitLegacyGuardian(store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{}) + guardian.InitLegacyGuardian(setting.NewCfg(), store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{}) testScenario(t, desc, func(t *testing.T, sc scenarioContext) { command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel") @@ -446,7 +446,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo features, folderPermissions, dashboardPermissions, ac, foldertest.NewFakeService(), ) - guardian.InitLegacyGuardian(sqlStore, dashboardService, &teamtest.FakeService{}) + guardian.InitLegacyGuardian(sqlStore.Cfg, sqlStore, dashboardService, &teamtest.FakeService{}) service := LibraryElementService{ Cfg: sqlStore.Cfg, SQLStore: sqlStore, diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index 8570e20fef1..4c5c07e9f37 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -779,7 +779,7 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s UID: q.UID, } }).Return(result, nil) - guardian.InitLegacyGuardian(store, dashSvc, &teamtest.FakeService{}) + guardian.InitLegacyGuardian(setting.NewCfg(), store, dashSvc, &teamtest.FakeService{}) t.Helper() testScenario(t, desc, func(t *testing.T, sc scenarioContext) { diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 7888632b0f8..66ae88f7a11 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -114,7 +114,7 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, hasEditPerm bool, p } canExplore := func(context *contextmodel.ReqContext) bool { - return c.OrgRole == org.RoleAdmin || c.OrgRole == org.RoleEditor || setting.ViewersCanEdit + return c.OrgRole == org.RoleAdmin || c.OrgRole == org.RoleEditor || s.cfg.ViewersCanEdit } if setting.ExploreEnabled && hasAccess(canExplore, ac.EvalPermission(ac.ActionDatasourcesExplore)) { diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 4cbb5863d3e..d2b36676d19 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -102,7 +102,6 @@ var ( ExternalUserMngLinkUrl string ExternalUserMngLinkName string ExternalUserMngInfo string - ViewersCanEdit bool // HTTP auth SigV4AuthEnabled bool @@ -330,6 +329,7 @@ type Cfg struct { // DistributedCache RemoteCacheOptions *RemoteCacheOptions + ViewersCanEdit bool EditorsCanAdmin bool ApiKeyMaxSecondsToLive int64 @@ -1567,7 +1567,7 @@ func readUserSettings(iniFile *ini.File, cfg *Cfg) error { ExternalUserMngLinkName = valueAsString(users, "external_manage_link_name", "") ExternalUserMngInfo = valueAsString(users, "external_manage_info", "") - ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false) + cfg.ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false) cfg.EditorsCanAdmin = users.Key("editors_can_admin").MustBool(false) userInviteMaxLifetimeVal := valueAsString(users, "user_invite_max_lifetime_duration", "24h")