diff --git a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx
index 22e331ae3c8..f7c1897fcfd 100644
--- a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx
+++ b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx
@@ -99,6 +99,14 @@ jest.mock('app/features/browse-dashboards/api/services', () => {
describe('browse-dashboards BrowseDashboardsPage', () => {
let props: Props;
let server: SetupServer;
+ const mockPermissions = {
+ canCreateDashboards: true,
+ canCreateFolder: true,
+ canDeleteFolder: true,
+ canEditFolder: true,
+ canViewPermissions: true,
+ canSetPermissions: true,
+ };
beforeAll(() => {
server = setupServer(
@@ -135,13 +143,7 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
...getRouteComponentProps(),
};
- jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
- return {
- canEditInFolder: true,
- canCreateDashboards: true,
- canCreateFolder: true,
- };
- });
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
});
@@ -169,7 +171,7 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
it('does not show the "New" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return {
- canEditInFolder: false,
+ ...mockPermissions,
canCreateDashboards: false,
canCreateFolder: false,
};
@@ -273,7 +275,7 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
it('does not show the "New" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return {
- canEditInFolder: false,
+ ...mockPermissions,
canCreateDashboards: false,
canCreateFolder: false,
};
@@ -289,7 +291,15 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
});
it('does not show the "Folder actions" button if the user does not have permissions', async () => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ canEditFolder: false,
+ canSetPermissions: false,
+ canViewPermissions: false,
+ };
+ });
render();
expect(await screen.findByRole('heading', { name: folderA.item.title })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();
@@ -303,9 +313,8 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
it('does not show the "Edit title" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return {
- canEditInFolder: false,
- canCreateDashboards: false,
- canCreateFolder: false,
+ ...mockPermissions,
+ canEditFolder: false,
};
});
render();
diff --git a/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx b/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx
index 909b95153a8..251bb7a99e7 100644
--- a/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx
+++ b/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx
@@ -78,9 +78,9 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
const hasSelection = useHasSelection();
- const { canEditInFolder, canCreateDashboards, canCreateFolder } = getFolderPermissions(folderDTO);
+ const { canEditFolder, canCreateDashboards, canCreateFolder } = getFolderPermissions(folderDTO);
- const showEditTitle = canEditInFolder && folderUID;
+ const showEditTitle = canEditFolder && folderUID;
const onEditTitle = async (newValue: string) => {
if (folderDTO) {
const result = await saveFolder({
@@ -125,9 +125,9 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
{({ width, height }) =>
isSearching ? (
-
+
) : (
-
+
)
}
diff --git a/public/app/features/browse-dashboards/BrowseFolderAlertingPage.test.tsx b/public/app/features/browse-dashboards/BrowseFolderAlertingPage.test.tsx
index 964c4aec837..14f057725d1 100644
--- a/public/app/features/browse-dashboards/BrowseFolderAlertingPage.test.tsx
+++ b/public/app/features/browse-dashboards/BrowseFolderAlertingPage.test.tsx
@@ -11,6 +11,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
import BrowseFolderAlertingPage, { OwnProps } from './BrowseFolderAlertingPage';
import { getPrometheusRulesResponse, getRulerRulesResponse } from './fixtures/alertRules.fixture';
+import * as permissions from './permissions';
function render(...[ui, options]: Parameters) {
rtlRender({ui}, options);
@@ -34,6 +35,14 @@ const mockPrometheusRulesResponse = getPrometheusRulesResponse(mockFolderName);
describe('browse-dashboards BrowseFolderAlertingPage', () => {
let props: OwnProps;
let server: SetupServer;
+ const mockPermissions = {
+ canCreateDashboards: true,
+ canCreateFolder: true,
+ canDeleteFolder: true,
+ canEditFolder: true,
+ canViewPermissions: true,
+ canSetPermissions: true,
+ };
beforeAll(() => {
server = setupServer(
@@ -61,6 +70,7 @@ describe('browse-dashboards BrowseFolderAlertingPage', () => {
});
beforeEach(() => {
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
props = {
...getRouteComponentProps({
@@ -92,7 +102,15 @@ describe('browse-dashboards BrowseFolderAlertingPage', () => {
});
it('does not display the "Folder actions" button if the user does not have permissions', async () => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ canEditFolder: false,
+ canViewPermissions: false,
+ canSetPermissions: false,
+ };
+ });
render();
expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();
diff --git a/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx b/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx
index 10b9708cb38..27424333759 100644
--- a/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx
+++ b/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx
@@ -11,6 +11,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
import BrowseFolderLibraryPanelsPage, { OwnProps } from './BrowseFolderLibraryPanelsPage';
import { getLibraryElementsResponse } from './fixtures/libraryElements.fixture';
+import * as permissions from './permissions';
function render(...[ui, options]: Parameters) {
rtlRender({ui}, options);
@@ -34,6 +35,14 @@ const mockLibraryElementsResponse = getLibraryElementsResponse(1, {
describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
let props: OwnProps;
let server: SetupServer;
+ const mockPermissions = {
+ canCreateDashboards: true,
+ canCreateFolder: true,
+ canDeleteFolder: true,
+ canEditFolder: true,
+ canViewPermissions: true,
+ canSetPermissions: true,
+ };
beforeAll(() => {
server = setupServer(
@@ -66,6 +75,7 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
});
beforeEach(() => {
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
props = {
...getRouteComponentProps({
@@ -97,7 +107,15 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
});
it('does not display the "Folder actions" button if the user does not have permissions', async () => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ canEditFolder: false,
+ canViewPermissions: false,
+ canSetPermissions: false,
+ };
+ });
render();
expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();
diff --git a/public/app/features/browse-dashboards/components/FolderActionsButton.test.tsx b/public/app/features/browse-dashboards/components/FolderActionsButton.test.tsx
index bd7ce560cb2..0c50ae35ec0 100644
--- a/public/app/features/browse-dashboards/components/FolderActionsButton.test.tsx
+++ b/public/app/features/browse-dashboards/components/FolderActionsButton.test.tsx
@@ -4,11 +4,11 @@ import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider';
import { config } from '@grafana/runtime';
-import { appEvents, contextSrv } from 'app/core/core';
-import { AccessControlAction } from 'app/types';
+import { appEvents } from 'app/core/core';
import { ShowModalReactEvent } from 'app/types/events';
import { mockFolderDTO } from '../fixtures/folder.fixture';
+import * as permissions from '../permissions';
import { DeleteModal } from './BrowseActions/DeleteModal';
import { MoveModal } from './BrowseActions/MoveModal';
@@ -25,9 +25,17 @@ jest.mock('app/core/components/AccessControl', () => ({
describe('browse-dashboards FolderActionsButton', () => {
const mockFolder = mockFolderDTO();
+ const mockPermissions = {
+ canCreateDashboards: true,
+ canCreateFolder: true,
+ canDeleteFolder: true,
+ canEditFolder: true,
+ canViewPermissions: true,
+ canSetPermissions: true,
+ };
beforeEach(() => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
});
afterEach(() => {
@@ -44,7 +52,15 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render anything when the user has no permissions to do anything', () => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ canEditFolder: false,
+ canViewPermissions: false,
+ canSetPermissions: false,
+ };
+ });
render();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();
});
@@ -64,9 +80,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Manage permissions" option if the user does not have permission to view permissions', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersPermissionsRead);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canViewPermissions: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@@ -76,9 +95,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Move" option if the user does not have permission to edit', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersWrite);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canEditFolder: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@@ -88,9 +110,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Delete" option if the user does not have permission to delete', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersDelete);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@@ -140,7 +165,15 @@ describe('browse-dashboards FolderActionsButton', () => {
describe('with nestedFolders disabled', () => {
it('does not render anything when the user has no permissions to do anything', () => {
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ canEditFolder: false,
+ canViewPermissions: false,
+ canSetPermissions: false,
+ };
+ });
render();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();
});
@@ -166,9 +199,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Manage permissions" option if the user does not have permission to view permissions', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersPermissionsRead);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canViewPermissions: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@@ -177,9 +213,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Move" option if the user does not have permission to edit', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersWrite);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canEditFolder: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@@ -188,9 +227,12 @@ describe('browse-dashboards FolderActionsButton', () => {
});
it('does not render the "Delete" option if the user does not have permission to delete', async () => {
- jest
- .spyOn(contextSrv, 'hasPermission')
- .mockImplementation((permission: string) => permission !== AccessControlAction.FoldersDelete);
+ jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
+ return {
+ ...mockPermissions,
+ canDeleteFolder: false,
+ };
+ });
render();
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
diff --git a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx
index c3c3e645b35..0f2ed031cdc 100644
--- a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx
+++ b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx
@@ -3,12 +3,13 @@ import React, { useState } from 'react';
import { config, locationService, reportInteraction } from '@grafana/runtime';
import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui';
import { Permissions } from 'app/core/components/AccessControl';
-import { appEvents, contextSrv } from 'app/core/core';
+import { appEvents } from 'app/core/core';
import { t, Trans } from 'app/core/internationalization';
-import { AccessControlAction, FolderDTO } from 'app/types';
+import { FolderDTO } from 'app/types';
import { ShowModalReactEvent } from 'app/types/events';
import { useDeleteFolderMutation, useMoveFolderMutation } from '../api/browseDashboardsAPI';
+import { getFolderPermissions } from '../permissions';
import { DeleteModal } from './BrowseActions/DeleteModal';
import { MoveModal } from './BrowseActions/MoveModal';
@@ -22,12 +23,9 @@ export function FolderActionsButton({ folder }: Props) {
const [showPermissionsDrawer, setShowPermissionsDrawer] = useState(false);
const [moveFolder] = useMoveFolderMutation();
const [deleteFolder] = useDeleteFolderMutation();
- const canViewPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsRead);
- const canSetPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsWrite);
+ const { canEditFolder, canDeleteFolder, canViewPermissions, canSetPermissions } = getFolderPermissions(folder);
// Can only move folders when nestedFolders is enabled
- const canMoveFolder =
- config.featureToggles.nestedFolders && contextSrv.hasPermission(AccessControlAction.FoldersWrite);
- const canDeleteFolder = contextSrv.hasPermission(AccessControlAction.FoldersDelete);
+ const canMoveFolder = config.featureToggles.nestedFolders && canEditFolder;
const onMove = async (destinationUID: string) => {
await moveFolder({ folder, destinationUID });
diff --git a/public/app/features/browse-dashboards/permissions.ts b/public/app/features/browse-dashboards/permissions.ts
index d0486102965..4236cdeb750 100644
--- a/public/app/features/browse-dashboards/permissions.ts
+++ b/public/app/features/browse-dashboards/permissions.ts
@@ -2,30 +2,27 @@ import { config } from '@grafana/runtime';
import { contextSrv } from 'app/core/core';
import { AccessControlAction, FolderDTO } from 'app/types';
-function checkFolderPermission(action: AccessControlAction, fallback: boolean, folderDTO?: FolderDTO) {
- return folderDTO
- ? contextSrv.hasAccessInMetadata(action, folderDTO, fallback)
- : contextSrv.hasAccess(action, fallback);
+function checkFolderPermission(action: AccessControlAction, folderDTO?: FolderDTO) {
+ return folderDTO ? contextSrv.hasPermissionInMetadata(action, folderDTO) : contextSrv.hasPermission(action);
}
export function getFolderPermissions(folderDTO?: FolderDTO) {
- // It is possible to have edit permissions for folders and dashboards, without being able to save, hence 'canSave'
- const canEditInFolderFallback = folderDTO ? folderDTO.canSave : contextSrv.hasEditPermissionInFolders;
-
- const canEditInFolder = checkFolderPermission(AccessControlAction.FoldersWrite, canEditInFolderFallback, folderDTO);
+ const canEditFolder = checkFolderPermission(AccessControlAction.FoldersWrite, folderDTO);
// Can only create a folder if we have permissions and either we're at root or nestedFolders is enabled
const canCreateFolder = Boolean(
- (!folderDTO || config.featureToggles.nestedFolders) &&
- checkFolderPermission(AccessControlAction.FoldersCreate, contextSrv.isEditor)
- );
- const canCreateDashboards = checkFolderPermission(
- AccessControlAction.DashboardsCreate,
- canEditInFolderFallback || !!folderDTO?.canSave
+ (!folderDTO || config.featureToggles.nestedFolders) && checkFolderPermission(AccessControlAction.FoldersCreate)
);
+ const canCreateDashboards = checkFolderPermission(AccessControlAction.DashboardsCreate, folderDTO);
+ const canDeleteFolder = checkFolderPermission(AccessControlAction.FoldersDelete, folderDTO);
+ const canViewPermissions = checkFolderPermission(AccessControlAction.FoldersPermissionsRead, folderDTO);
+ const canSetPermissions = checkFolderPermission(AccessControlAction.FoldersPermissionsWrite, folderDTO);
return {
- canEditInFolder,
canCreateDashboards,
canCreateFolder,
+ canDeleteFolder,
+ canEditFolder,
+ canSetPermissions,
+ canViewPermissions,
};
}