Browse Dashboards: Use correct permissions checks (#74811)

* use correct permissions checks + update unit tests

* fix rest of the unit tests
This commit is contained in:
Ashley Harrison 2023-09-14 11:23:12 +01:00 committed by GitHub
parent 5e9f252962
commit 8874a8d398
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 146 additions and 64 deletions

View File

@ -99,6 +99,14 @@ jest.mock('app/features/browse-dashboards/api/services', () => {
describe('browse-dashboards BrowseDashboardsPage', () => { describe('browse-dashboards BrowseDashboardsPage', () => {
let props: Props; let props: Props;
let server: SetupServer; let server: SetupServer;
const mockPermissions = {
canCreateDashboards: true,
canCreateFolder: true,
canDeleteFolder: true,
canEditFolder: true,
canViewPermissions: true,
canSetPermissions: true,
};
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
@ -135,13 +143,7 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
...getRouteComponentProps(), ...getRouteComponentProps(),
}; };
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => { jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
return {
canEditInFolder: true,
canCreateDashboards: true,
canCreateFolder: true,
};
});
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); 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 () => { it('does not show the "New" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => { jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return { return {
canEditInFolder: false, ...mockPermissions,
canCreateDashboards: false, canCreateDashboards: false,
canCreateFolder: 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 () => { it('does not show the "New" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => { jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return { return {
canEditInFolder: false, ...mockPermissions,
canCreateDashboards: false, canCreateDashboards: false,
canCreateFolder: 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 () => { 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(<BrowseDashboardsPage {...props} />); render(<BrowseDashboardsPage {...props} />);
expect(await screen.findByRole('heading', { name: folderA.item.title })).toBeInTheDocument(); expect(await screen.findByRole('heading', { name: folderA.item.title })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.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 () => { it('does not show the "Edit title" button if the user does not have permissions', async () => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => { jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
return { return {
canEditInFolder: false, ...mockPermissions,
canCreateDashboards: false, canEditFolder: false,
canCreateFolder: false,
}; };
}); });
render(<BrowseDashboardsPage {...props} />); render(<BrowseDashboardsPage {...props} />);

View File

@ -78,9 +78,9 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
const hasSelection = useHasSelection(); 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) => { const onEditTitle = async (newValue: string) => {
if (folderDTO) { if (folderDTO) {
const result = await saveFolder({ const result = await saveFolder({
@ -125,9 +125,9 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
<AutoSizer> <AutoSizer>
{({ width, height }) => {({ width, height }) =>
isSearching ? ( isSearching ? (
<SearchView canSelect={canEditInFolder} width={width} height={height} /> <SearchView canSelect={canEditFolder} width={width} height={height} />
) : ( ) : (
<BrowseView canSelect={canEditInFolder} width={width} height={height} folderUID={folderUID} /> <BrowseView canSelect={canEditFolder} width={width} height={height} folderUID={folderUID} />
) )
} }
</AutoSizer> </AutoSizer>

View File

@ -11,6 +11,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
import BrowseFolderAlertingPage, { OwnProps } from './BrowseFolderAlertingPage'; import BrowseFolderAlertingPage, { OwnProps } from './BrowseFolderAlertingPage';
import { getPrometheusRulesResponse, getRulerRulesResponse } from './fixtures/alertRules.fixture'; import { getPrometheusRulesResponse, getRulerRulesResponse } from './fixtures/alertRules.fixture';
import * as permissions from './permissions';
function render(...[ui, options]: Parameters<typeof rtlRender>) { function render(...[ui, options]: Parameters<typeof rtlRender>) {
rtlRender(<TestProvider>{ui}</TestProvider>, options); rtlRender(<TestProvider>{ui}</TestProvider>, options);
@ -34,6 +35,14 @@ const mockPrometheusRulesResponse = getPrometheusRulesResponse(mockFolderName);
describe('browse-dashboards BrowseFolderAlertingPage', () => { describe('browse-dashboards BrowseFolderAlertingPage', () => {
let props: OwnProps; let props: OwnProps;
let server: SetupServer; let server: SetupServer;
const mockPermissions = {
canCreateDashboards: true,
canCreateFolder: true,
canDeleteFolder: true,
canEditFolder: true,
canViewPermissions: true,
canSetPermissions: true,
};
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
@ -61,6 +70,7 @@ describe('browse-dashboards BrowseFolderAlertingPage', () => {
}); });
beforeEach(() => { beforeEach(() => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
props = { props = {
...getRouteComponentProps({ ...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 () => { 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(<BrowseFolderAlertingPage {...props} />); render(<BrowseFolderAlertingPage {...props} />);
expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument(); expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();

View File

@ -11,6 +11,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
import BrowseFolderLibraryPanelsPage, { OwnProps } from './BrowseFolderLibraryPanelsPage'; import BrowseFolderLibraryPanelsPage, { OwnProps } from './BrowseFolderLibraryPanelsPage';
import { getLibraryElementsResponse } from './fixtures/libraryElements.fixture'; import { getLibraryElementsResponse } from './fixtures/libraryElements.fixture';
import * as permissions from './permissions';
function render(...[ui, options]: Parameters<typeof rtlRender>) { function render(...[ui, options]: Parameters<typeof rtlRender>) {
rtlRender(<TestProvider>{ui}</TestProvider>, options); rtlRender(<TestProvider>{ui}</TestProvider>, options);
@ -34,6 +35,14 @@ const mockLibraryElementsResponse = getLibraryElementsResponse(1, {
describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => { describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
let props: OwnProps; let props: OwnProps;
let server: SetupServer; let server: SetupServer;
const mockPermissions = {
canCreateDashboards: true,
canCreateFolder: true,
canDeleteFolder: true,
canEditFolder: true,
canViewPermissions: true,
canSetPermissions: true,
};
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
@ -66,6 +75,7 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
}); });
beforeEach(() => { beforeEach(() => {
jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
props = { props = {
...getRouteComponentProps({ ...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 () => { 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(<BrowseFolderLibraryPanelsPage {...props} />); render(<BrowseFolderLibraryPanelsPage {...props} />);
expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument(); expect(await screen.findByRole('heading', { name: mockFolderName })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument();

View File

@ -4,11 +4,11 @@ import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
import { config } from '@grafana/runtime'; import { config } from '@grafana/runtime';
import { appEvents, contextSrv } from 'app/core/core'; import { appEvents } from 'app/core/core';
import { AccessControlAction } from 'app/types';
import { ShowModalReactEvent } from 'app/types/events'; import { ShowModalReactEvent } from 'app/types/events';
import { mockFolderDTO } from '../fixtures/folder.fixture'; import { mockFolderDTO } from '../fixtures/folder.fixture';
import * as permissions from '../permissions';
import { DeleteModal } from './BrowseActions/DeleteModal'; import { DeleteModal } from './BrowseActions/DeleteModal';
import { MoveModal } from './BrowseActions/MoveModal'; import { MoveModal } from './BrowseActions/MoveModal';
@ -25,9 +25,17 @@ jest.mock('app/core/components/AccessControl', () => ({
describe('browse-dashboards FolderActionsButton', () => { describe('browse-dashboards FolderActionsButton', () => {
const mockFolder = mockFolderDTO(); const mockFolder = mockFolderDTO();
const mockPermissions = {
canCreateDashboards: true,
canCreateFolder: true,
canDeleteFolder: true,
canEditFolder: true,
canViewPermissions: true,
canSetPermissions: true,
};
beforeEach(() => { beforeEach(() => {
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => mockPermissions);
}); });
afterEach(() => { afterEach(() => {
@ -44,7 +52,15 @@ describe('browse-dashboards FolderActionsButton', () => {
}); });
it('does not render anything when the user has no permissions to do anything', () => { 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(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument(); 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 () => { it('does not render the "Manage permissions" option if the user does not have permission to view permissions', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersPermissionsRead); ...mockPermissions,
canViewPermissions: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); 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 () => { it('does not render the "Move" option if the user does not have permission to edit', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersWrite); ...mockPermissions,
canEditFolder: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); 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 () => { it('does not render the "Delete" option if the user does not have permission to delete', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersDelete); ...mockPermissions,
canDeleteFolder: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));
@ -140,7 +165,15 @@ describe('browse-dashboards FolderActionsButton', () => {
describe('with nestedFolders disabled', () => { describe('with nestedFolders disabled', () => {
it('does not render anything when the user has no permissions to do anything', () => { 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(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
expect(screen.queryByRole('button', { name: 'Folder actions' })).not.toBeInTheDocument(); 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 () => { it('does not render the "Manage permissions" option if the user does not have permission to view permissions', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersPermissionsRead); ...mockPermissions,
canViewPermissions: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); 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 () => { it('does not render the "Move" option if the user does not have permission to edit', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersWrite); ...mockPermissions,
canEditFolder: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); 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 () => { it('does not render the "Delete" option if the user does not have permission to delete', async () => {
jest jest.spyOn(permissions, 'getFolderPermissions').mockImplementation(() => {
.spyOn(contextSrv, 'hasPermission') return {
.mockImplementation((permission: string) => permission !== AccessControlAction.FoldersDelete); ...mockPermissions,
canDeleteFolder: false,
};
});
render(<FolderActionsButton folder={mockFolder} />); render(<FolderActionsButton folder={mockFolder} />);
await userEvent.click(screen.getByRole('button', { name: 'Folder actions' })); await userEvent.click(screen.getByRole('button', { name: 'Folder actions' }));

View File

@ -3,12 +3,13 @@ import React, { useState } from 'react';
import { config, locationService, reportInteraction } from '@grafana/runtime'; import { config, locationService, reportInteraction } from '@grafana/runtime';
import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui'; import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui';
import { Permissions } from 'app/core/components/AccessControl'; 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 { t, Trans } from 'app/core/internationalization';
import { AccessControlAction, FolderDTO } from 'app/types'; import { FolderDTO } from 'app/types';
import { ShowModalReactEvent } from 'app/types/events'; import { ShowModalReactEvent } from 'app/types/events';
import { useDeleteFolderMutation, useMoveFolderMutation } from '../api/browseDashboardsAPI'; import { useDeleteFolderMutation, useMoveFolderMutation } from '../api/browseDashboardsAPI';
import { getFolderPermissions } from '../permissions';
import { DeleteModal } from './BrowseActions/DeleteModal'; import { DeleteModal } from './BrowseActions/DeleteModal';
import { MoveModal } from './BrowseActions/MoveModal'; import { MoveModal } from './BrowseActions/MoveModal';
@ -22,12 +23,9 @@ export function FolderActionsButton({ folder }: Props) {
const [showPermissionsDrawer, setShowPermissionsDrawer] = useState(false); const [showPermissionsDrawer, setShowPermissionsDrawer] = useState(false);
const [moveFolder] = useMoveFolderMutation(); const [moveFolder] = useMoveFolderMutation();
const [deleteFolder] = useDeleteFolderMutation(); const [deleteFolder] = useDeleteFolderMutation();
const canViewPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsRead); const { canEditFolder, canDeleteFolder, canViewPermissions, canSetPermissions } = getFolderPermissions(folder);
const canSetPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsWrite);
// Can only move folders when nestedFolders is enabled // Can only move folders when nestedFolders is enabled
const canMoveFolder = const canMoveFolder = config.featureToggles.nestedFolders && canEditFolder;
config.featureToggles.nestedFolders && contextSrv.hasPermission(AccessControlAction.FoldersWrite);
const canDeleteFolder = contextSrv.hasPermission(AccessControlAction.FoldersDelete);
const onMove = async (destinationUID: string) => { const onMove = async (destinationUID: string) => {
await moveFolder({ folder, destinationUID }); await moveFolder({ folder, destinationUID });

View File

@ -2,30 +2,27 @@ import { config } from '@grafana/runtime';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { AccessControlAction, FolderDTO } from 'app/types'; import { AccessControlAction, FolderDTO } from 'app/types';
function checkFolderPermission(action: AccessControlAction, fallback: boolean, folderDTO?: FolderDTO) { function checkFolderPermission(action: AccessControlAction, folderDTO?: FolderDTO) {
return folderDTO return folderDTO ? contextSrv.hasPermissionInMetadata(action, folderDTO) : contextSrv.hasPermission(action);
? contextSrv.hasAccessInMetadata(action, folderDTO, fallback)
: contextSrv.hasAccess(action, fallback);
} }
export function getFolderPermissions(folderDTO?: FolderDTO) { export function getFolderPermissions(folderDTO?: FolderDTO) {
// It is possible to have edit permissions for folders and dashboards, without being able to save, hence 'canSave' const canEditFolder = checkFolderPermission(AccessControlAction.FoldersWrite, folderDTO);
const canEditInFolderFallback = folderDTO ? folderDTO.canSave : contextSrv.hasEditPermissionInFolders;
const canEditInFolder = checkFolderPermission(AccessControlAction.FoldersWrite, canEditInFolderFallback, folderDTO);
// Can only create a folder if we have permissions and either we're at root or nestedFolders is enabled // Can only create a folder if we have permissions and either we're at root or nestedFolders is enabled
const canCreateFolder = Boolean( const canCreateFolder = Boolean(
(!folderDTO || config.featureToggles.nestedFolders) && (!folderDTO || config.featureToggles.nestedFolders) && checkFolderPermission(AccessControlAction.FoldersCreate)
checkFolderPermission(AccessControlAction.FoldersCreate, contextSrv.isEditor)
);
const canCreateDashboards = checkFolderPermission(
AccessControlAction.DashboardsCreate,
canEditInFolderFallback || !!folderDTO?.canSave
); );
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 { return {
canEditInFolder,
canCreateDashboards, canCreateDashboards,
canCreateFolder, canCreateFolder,
canDeleteFolder,
canEditFolder,
canSetPermissions,
canViewPermissions,
}; };
} }