@@ -456,3 +448,16 @@ export const getStyles = (theme: GrafanaTheme2) => ({
display: none;
`,
});
+
+export function UnifiedAlertListPanel(props: PanelProps
) {
+ const [, gmaReadAllowed] = useAlertingAbility(AlertingAction.ViewAlertRule);
+ const [, externalReadAllowed] = useAlertingAbility(AlertingAction.ViewExternalAlertRule);
+
+ if (!gmaReadAllowed && !externalReadAllowed) {
+ return (
+ Sorry, you do not have the required permissions to read alert rules
+ );
+ }
+
+ return ;
+}
diff --git a/public/app/plugins/panel/alertlist/UnifiedalertList.test.tsx b/public/app/plugins/panel/alertlist/UnifiedalertList.test.tsx
index 22d963ddf4c..d4847dd8289 100644
--- a/public/app/plugins/panel/alertlist/UnifiedalertList.test.tsx
+++ b/public/app/plugins/panel/alertlist/UnifiedalertList.test.tsx
@@ -2,7 +2,6 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
-import { act } from 'react-test-renderer';
import { byRole, byText } from 'testing-library-selector';
import { FieldConfigSource, getDefaultTimeRange, LoadingState, PanelProps, PluginExtensionTypes } from '@grafana/data';
@@ -25,7 +24,7 @@ import {
} from '../../../features/alerting/unified/mocks';
import { GRAFANA_RULES_SOURCE_NAME } from '../../../features/alerting/unified/utils/datasource';
-import { UnifiedAlertList } from './UnifiedAlertList';
+import { UnifiedAlertListPanel } from './UnifiedAlertList';
import { GroupMode, SortOrder, UnifiedAlertListOptions, ViewMode } from './types';
import * as utils from './util';
@@ -159,20 +158,20 @@ const renderPanel = (options: Partial = defaultOptions)
return render(
-
+
);
};
describe('UnifiedAlertList', () => {
+ jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
+
it('subscribes to the dashboard refresh interval', async () => {
jest.spyOn(defaultProps, 'replaceVariables').mockReturnValue('severity=critical');
- await act(async () => {
- renderPanel();
- });
+ renderPanel();
- expect(dashboard.events.subscribe).toHaveBeenCalledTimes(1);
+ await waitFor(() => expect(dashboard.events.subscribe).toHaveBeenCalledTimes(1));
expect(dashboard.events.subscribe.mock.calls[0][0]).toEqual(TimeRangeUpdatedEvent);
});
@@ -180,21 +179,18 @@ describe('UnifiedAlertList', () => {
await waitFor(() => {
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
- jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
const filterAlertsSpy = jest.spyOn(utils, 'filterAlerts');
const replaceVarsSpy = jest.spyOn(defaultProps, 'replaceVariables').mockReturnValue('severity=critical');
const user = userEvent.setup();
- await act(async () => {
- renderPanel({
- alertInstanceLabelFilter: '$label',
- dashboardAlerts: false,
- alertName: '',
- datasource: GRAFANA_RULES_SOURCE_NAME,
- folder: undefined,
- });
+ renderPanel({
+ alertInstanceLabelFilter: '$label',
+ dashboardAlerts: false,
+ alertName: '',
+ datasource: GRAFANA_RULES_SOURCE_NAME,
+ folder: undefined,
});
await waitFor(() => {
@@ -222,4 +218,12 @@ describe('UnifiedAlertList', () => {
expect.anything()
);
});
+
+ it('should render authorization error when user has no permission', async () => {
+ jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
+
+ renderPanel();
+
+ expect(screen.getByRole('alert', { name: 'Permission required' })).toBeInTheDocument();
+ });
});
diff --git a/public/app/plugins/panel/alertlist/module.tsx b/public/app/plugins/panel/alertlist/module.tsx
index 8250c7fd4d4..0bb5d10eeed 100644
--- a/public/app/plugins/panel/alertlist/module.tsx
+++ b/public/app/plugins/panel/alertlist/module.tsx
@@ -17,7 +17,7 @@ import { GRAFANA_DATASOURCE_NAME } from '../../../features/alerting/unified/util
import { AlertList } from './AlertList';
import { alertListPanelMigrationHandler } from './AlertListMigrationHandler';
import { GroupBy } from './GroupByWithLoading';
-import { UnifiedAlertList } from './UnifiedAlertList';
+import { UnifiedAlertListPanel } from './UnifiedAlertList';
import { AlertListSuggestionsSupplier } from './suggestions';
import { AlertListOptions, GroupMode, ShowOption, SortOrder, UnifiedAlertListOptions, ViewMode } from './types';
@@ -156,7 +156,7 @@ const alertList = new PanelPlugin(AlertList)
.setMigrationHandler(alertListPanelMigrationHandler)
.setSuggestionsSupplier(new AlertListSuggestionsSupplier());
-const unifiedAlertList = new PanelPlugin(UnifiedAlertList).setPanelOptions((builder) => {
+const unifiedAlertList = new PanelPlugin(UnifiedAlertListPanel).setPanelOptions((builder) => {
builder
.addRadio({
path: 'viewMode',