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 { export function evaluateBooleanFlag(flagName: FeatureFlagName, defaultValue: boolean): boolean {
console.log('deets', OpenFeature.getClient().getBooleanDetails('recentlyViewedDashboards', false));
return OpenFeature.getClient().getBooleanValue(flagName, defaultValue); 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 //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{ dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
Text: "Recently deleted", Text: "Recently deleted",
SubTitle: "Any items listed here for more than 30 days will be automatically 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(); 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 () => { it('does not show the "New" button if the user does not have permissions', async () => {
mockPermissions.canCreateDashboards = false; mockPermissions.canCreateDashboards = false;
mockPermissions.canCreateFolders = false; mockPermissions.canCreateFolders = false;
@@ -13,7 +13,6 @@ import { getConfig } from 'app/core/config';
import { useDispatch } from 'app/types/store'; import { useDispatch } from 'app/types/store';
import { FolderRepo } from '../../core/components/NestedFolderPicker/FolderRepo'; import { FolderRepo } from '../../core/components/NestedFolderPicker/FolderRepo';
import { contextSrv } from '../../core/services/context_srv';
import { ManagerKind } from '../apiserver/types'; import { ManagerKind } from '../apiserver/types';
import { TemplateDashboardModal } from '../dashboard/dashgrid/DashboardLibrary/TemplateDashboardModal'; import { TemplateDashboardModal } from '../dashboard/dashgrid/DashboardLibrary/TemplateDashboardModal';
import { buildNavModel, getDashboardsTabID } from '../folders/state/navModel'; import { buildNavModel, getDashboardsTabID } from '../folders/state/navModel';
@@ -104,7 +103,6 @@ const BrowseDashboardsPage = memo(({ queryParams }: { queryParams: Record<string
canCreateDashboards, canCreateDashboards,
canCreateFolders, canCreateFolders,
} = getFolderPermissions(folder); } = getFolderPermissions(folder);
const hasAdminRights = contextSrv.hasRole('Admin') || contextSrv.isGrafanaAdmin;
const isProvisionedFolder = folder?.managedBy === ManagerKind.Repo; const isProvisionedFolder = folder?.managedBy === ManagerKind.Repo;
const showEditTitle = canEditFolders && folderUID && !isProvisionedFolder; const showEditTitle = canEditFolders && folderUID && !isProvisionedFolder;
const permissions = { const permissions = {
@@ -155,7 +153,7 @@ const BrowseDashboardsPage = memo(({ queryParams }: { queryParams: Record<string
renderTitle={renderTitle} renderTitle={renderTitle}
actions={ actions={
<> <>
{config.featureToggles.restoreDashboards && hasAdminRights && ( {config.featureToggles.restoreDashboards && canDeleteDashboards && (
<LinkButton <LinkButton
variant="secondary" variant="secondary"
href={getConfig().appSubUrl + '/dashboard/recently-deleted'} href={getConfig().appSubUrl + '/dashboard/recently-deleted'}
@@ -59,9 +59,9 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
<Text element="p"> <Text element="p">
<Trans i18nKey="browse-dashboards.action.delete-modal-restore-dashboards-text"> <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 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. history for up to 12 months. Users with delete permissions can restore the dashboards they deleted,
The history is limited to 1000 dashboards — older ones may be removed sooner if the limit is reached. and admins can restore any user's deleted dashboards. The history is limited to 1000 dashboards — older
Folders cannot be restored. ones may be removed sooner if the limit is reached. Folders cannot be restored.
</Trans> </Trans>
</Text> </Text>
<Space v={2} /> <Space v={2} />
@@ -19,8 +19,8 @@ export const RecentlyDeletedEmptyState = ({ searchState }: RecentlyDeletedEmptyS
role="alert" role="alert"
> >
<Trans i18nKey={'recently-deleted.page.no-deleted-dashboards-text'}> <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 When you delete a dashboard, it will appear here for 30 days before being permanently deleted. Users with delete
administrator can restore recently-deleted dashboards. permissions can restore the dashboards they deleted, and admins can restore any user's deleted dashboards.
</Trans> </Trans>
</EmptyState> </EmptyState>
); );
@@ -81,8 +81,9 @@ export function DeleteDashboardModal({ dashboardTitle, onConfirm, onClose }: Del
<Text element="p"> <Text element="p">
<Trans i18nKey="dashboard-settings.delete-modal-restore-dashboards-text"> <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 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 months. Users with delete permissions can restore the dashboards they deleted, and admins can restore
to 1000 dashboards—older ones will be removed sooner if the limit is reached. any user's deleted dashboards. The history is limited to 1000 dashboards—older ones will be removed
sooner if the limit is reached.
</Trans> </Trans>
</Text> </Text>
<Space v={1} /> <Space v={1} />
+1 -1
View File
@@ -534,7 +534,7 @@ export function getAppRoutes(): RouteDescriptor[] {
}, },
config.featureToggles.restoreDashboards && { config.featureToggles.restoreDashboards && {
path: '/dashboard/recently-deleted', path: '/dashboard/recently-deleted',
roles: () => ['Admin', 'ServerAdmin'], roles: () => contextSrv.evaluatePermission([AccessControlAction.DashboardsDelete]),
component: SafeDynamicImport( component: SafeDynamicImport(
() => import(/* webpackChunkName: "RecentlyDeletedPage" */ 'app/features/browse-dashboards/RecentlyDeletedPage') () => import(/* webpackChunkName: "RecentlyDeletedPage" */ 'app/features/browse-dashboards/RecentlyDeletedPage')
), ),