diff --git a/pkg/api/accesscontrol.go b/pkg/api/accesscontrol.go index 1855531c0ce..0f1c4efe772 100644 --- a/pkg/api/accesscontrol.go +++ b/pkg/api/accesscontrol.go @@ -74,10 +74,6 @@ func (hs *HTTPServer) declareFixedRoles() error { Grants: []string{string(org.RoleEditor)}, } - if hs.Cfg.ViewersCanEdit { - datasourcesExplorerRole.Grants = append(datasourcesExplorerRole.Grants, string(org.RoleViewer)) - } - datasourcesReaderRole := ac.RoleRegistration{ Role: ac.RoleDTO{ Name: "fixed:datasources:reader", diff --git a/pkg/services/guardian/accesscontrol_guardian.go b/pkg/services/guardian/accesscontrol_guardian.go index 4282ac4e29e..88d29ec72b1 100644 --- a/pkg/services/guardian/accesscontrol_guardian.go +++ b/pkg/services/guardian/accesscontrol_guardian.go @@ -221,10 +221,6 @@ func (a *accessControlDashboardGuardian) CanEdit() (bool, error) { return false, ErrGuardianDashboardNotFound.Errorf("failed to check edit permissions for dashboard") } - if a.cfg.ViewersCanEdit { - return a.CanView() - } - return a.evaluate( accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)), ) @@ -235,10 +231,6 @@ func (a *accessControlFolderGuardian) CanEdit() (bool, error) { return false, ErrGuardianFolderNotFound.Errorf("failed to check edit permissions for folder") } - if a.cfg.ViewersCanEdit { - return a.CanView() - } - return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID))) } diff --git a/pkg/services/guardian/accesscontrol_guardian_test.go b/pkg/services/guardian/accesscontrol_guardian_test.go index 5de4e91da24..2171d2bbbac 100644 --- a/pkg/services/guardian/accesscontrol_guardian_test.go +++ b/pkg/services/guardian/accesscontrol_guardian_test.go @@ -36,11 +36,10 @@ var ( ) type accessControlGuardianTestCase struct { - desc string - dashboard *dashboards.Dashboard - permissions []accesscontrol.Permission - viewersCanEdit bool - expected bool + desc string + dashboard *dashboards.Dashboard + permissions []accesscontrol.Permission + expected bool } func TestAccessControlDashboardGuardian_CanSave(t *testing.T) { @@ -257,18 +256,6 @@ func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) { }, expected: false, }, - { - desc: "should be able to edit dashboard with read action when viewer_can_edit is true", - dashboard: dashboard, - permissions: []accesscontrol.Permission{ - { - Action: dashboards.ActionDashboardsRead, - Scope: "dashboards:uid:1", - }, - }, - viewersCanEdit: true, - expected: true, - }, { desc: "should not be able to edit folder with folder write and dashboard wildcard scope", dashboard: fldr, @@ -324,24 +311,11 @@ func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) { }, expected: false, }, - { - desc: "should be able to edit folder with folder read action when viewer_can_edit is true", - dashboard: fldr, - permissions: []accesscontrol.Permission{ - { - Action: dashboards.ActionFoldersRead, - Scope: folderUIDScope, - }, - }, - viewersCanEdit: true, - expected: true, - }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { cfg := setting.NewCfg() - cfg.ViewersCanEdit = tt.viewersCanEdit guardian := setupAccessControlGuardianTest(t, tt.dashboard, tt.permissions, cfg) can, err := guardian.CanEdit() diff --git a/pkg/services/queryhistory/api.go b/pkg/services/queryhistory/api.go index ef8ca98ad4d..0b39e4c9f30 100644 --- a/pkg/services/queryhistory/api.go +++ b/pkg/services/queryhistory/api.go @@ -30,7 +30,7 @@ type CallbackHandler func(c *contextmodel.ReqContext) response.Response func (s *QueryHistoryService) permissionsMiddleware(handler CallbackHandler, errorMessage string) CallbackHandler { return func(c *contextmodel.ReqContext) response.Response { hasAccess := ac.HasAccess(s.accessControl, c) - if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit && !hasAccess(ac.EvalPermission(ac.ActionDatasourcesExplore)) { + if c.GetOrgRole() == org.RoleViewer && !hasAccess(ac.EvalPermission(ac.ActionDatasourcesExplore)) { return response.Error(http.StatusUnauthorized, errorMessage, nil) } return handler(c) diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index d5564aa2814..b3c4c75afe8 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -20,6 +20,7 @@ import { } from '@grafana/ui'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/NavToolbarSeparator'; +import grafanaConfig from 'app/core/config'; import { LS_PANEL_COPY_KEY } from 'app/core/constants'; import { contextSrv } from 'app/core/core'; import { Trans, t } from 'app/core/internationalization'; @@ -78,6 +79,11 @@ export function ToolbarActions({ dashboard }: Props) { const showScopesSelector = config.featureToggles.scopeFilters && !isEditing; const dashboardNewLayouts = config.featureToggles.dashboardNewLayouts; + // Internal only; + // allows viewer editing without ability to save + // used for grafana play + const canEdit = grafanaConfig.viewersCanEdit; + if (!isEditingPanel) { // This adds the presence indicators in enterprise addDynamicActions(toolbarActions, dynamicDashNavActions.left, 'left-actions'); @@ -354,7 +360,7 @@ export function ToolbarActions({ dashboard }: Props) { toolbarActions.push({ group: 'main-buttons', - condition: !isEditing && dashboard.canEditDashboard() && !isViewingPanel && !isPlaying && editable, + condition: !isEditing && (dashboard.canEditDashboard() || canEdit) && !isViewingPanel && !isPlaying && editable, render: () => (