Restore dashboards: Update permissions

This commit is contained in:
Clarity-89
2026-01-05 13:46:43 +02:00
parent c05e1bd43a
commit eba4c8ecc2
8 changed files with 35 additions and 12 deletions
@@ -29,5 +29,6 @@ export async function initOpenFeature() {
}
export function evaluateBooleanFlag(flagName: FeatureFlagName, defaultValue: boolean): boolean {
console.log('deets', OpenFeature.getClient().getBooleanDetails('recentlyViewedDashboards', false));
return OpenFeature.getClient().getBooleanValue(flagName, defaultValue);
}
+1 -1
View File
@@ -408,7 +408,7 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
}
//nolint:staticcheck // not yet migrated to OpenFeature
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagRestoreDashboards) && (c.GetOrgRole() == org.RoleAdmin || c.IsGrafanaAdmin) {
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagRestoreDashboards) && hasAccess(ac.EvalPermission(dashboards.ActionDashboardsDelete)) {
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
Text: "Recently deleted",
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
@@ -113,6 +113,29 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
expect(await screen.findByRole('button', { name: 'New' })).toBeInTheDocument();
});
it('shows the "Recently deleted" button when restore is enabled and user can delete dashboards', async () => {
const previousFlag = config.featureToggles.restoreDashboards;
config.featureToggles.restoreDashboards = true;
render(<BrowseDashboardsPage queryParams={{}} />);
await screen.findByPlaceholderText('Search for dashboards and folders');
expect(await screen.findByRole('link', { name: 'Recently deleted' })).toBeInTheDocument();
config.featureToggles.restoreDashboards = previousFlag;
});
it('does not show the "Recently deleted" button when user cannot delete dashboards', async () => {
const previousFlag = config.featureToggles.restoreDashboards;
config.featureToggles.restoreDashboards = true;
mockPermissions.canDeleteDashboards = false;
render(<BrowseDashboardsPage queryParams={{}} />);
await screen.findByPlaceholderText('Search for dashboards and folders');
expect(screen.queryByRole('link', { name: 'Recently deleted' })).not.toBeInTheDocument();
config.featureToggles.restoreDashboards = previousFlag;
});
it('does not show the "New" button if the user does not have permissions', async () => {
mockPermissions.canCreateDashboards = false;
mockPermissions.canCreateFolders = false;
@@ -13,7 +13,6 @@ import { getConfig } from 'app/core/config';
import { useDispatch } from 'app/types/store';
import { FolderRepo } from '../../core/components/NestedFolderPicker/FolderRepo';
import { contextSrv } from '../../core/services/context_srv';
import { ManagerKind } from '../apiserver/types';
import { TemplateDashboardModal } from '../dashboard/dashgrid/DashboardLibrary/TemplateDashboardModal';
import { buildNavModel, getDashboardsTabID } from '../folders/state/navModel';
@@ -104,7 +103,6 @@ const BrowseDashboardsPage = memo(({ queryParams }: { queryParams: Record<string
canCreateDashboards,
canCreateFolders,
} = getFolderPermissions(folder);
const hasAdminRights = contextSrv.hasRole('Admin') || contextSrv.isGrafanaAdmin;
const isProvisionedFolder = folder?.managedBy === ManagerKind.Repo;
const showEditTitle = canEditFolders && folderUID && !isProvisionedFolder;
const permissions = {
@@ -155,7 +153,7 @@ const BrowseDashboardsPage = memo(({ queryParams }: { queryParams: Record<string
renderTitle={renderTitle}
actions={
<>
{config.featureToggles.restoreDashboards && hasAdminRights && (
{config.featureToggles.restoreDashboards && canDeleteDashboards && (
<LinkButton
variant="secondary"
href={getConfig().appSubUrl + '/dashboard/recently-deleted'}
@@ -59,9 +59,9 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
<Text element="p">
<Trans i18nKey="browse-dashboards.action.delete-modal-restore-dashboards-text">
This action will delete the selected folders immediately. Deleted dashboards will be kept in the
history for up to 12 months and can be restored by your organization administrator during that time.
The history is limited to 1000 dashboards older ones may be removed sooner if the limit is reached.
Folders cannot be restored.
history for up to 12 months. Users with delete permissions can restore the dashboards they deleted,
and admins can restore any user's deleted dashboards. The history is limited to 1000 dashboards — older
ones may be removed sooner if the limit is reached. Folders cannot be restored.
</Trans>
</Text>
<Space v={2} />
@@ -19,8 +19,8 @@ export const RecentlyDeletedEmptyState = ({ searchState }: RecentlyDeletedEmptyS
role="alert"
>
<Trans i18nKey={'recently-deleted.page.no-deleted-dashboards-text'}>
When you delete a dashboard, it will appear here for 30 days before being permanently deleted. Your organization
administrator can restore recently-deleted dashboards.
When you delete a dashboard, it will appear here for 30 days before being permanently deleted. Users with delete
permissions can restore the dashboards they deleted, and admins can restore any user's deleted dashboards.
</Trans>
</EmptyState>
);
@@ -81,8 +81,9 @@ export function DeleteDashboardModal({ dashboardTitle, onConfirm, onClose }: Del
<Text element="p">
<Trans i18nKey="dashboard-settings.delete-modal-restore-dashboards-text">
This action will delete the dashboard. Deleted dashboards will be kept in the history for up to 12
months and can be restored by your organization administrator during that time. The history is limited
to 1000 dashboardsolder ones will be removed sooner if the limit is reached.
months. Users with delete permissions can restore the dashboards they deleted, and admins can restore
any user's deleted dashboards. The history is limited to 1000 dashboards—older ones will be removed
sooner if the limit is reached.
</Trans>
</Text>
<Space v={1} />
+1 -1
View File
@@ -534,7 +534,7 @@ export function getAppRoutes(): RouteDescriptor[] {
},
config.featureToggles.restoreDashboards && {
path: '/dashboard/recently-deleted',
roles: () => ['Admin', 'ServerAdmin'],
roles: () => contextSrv.evaluatePermission([AccessControlAction.DashboardsDelete]),
component: SafeDynamicImport(
() => import(/* webpackChunkName: "RecentlyDeletedPage" */ 'app/features/browse-dashboards/RecentlyDeletedPage')
),