mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix permissions for silences list view (#88908)
This commit is contained in:
@@ -396,8 +396,15 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
|||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Notification policies", SubTitle: "Determine how alerts are routed to contact points", Id: "am-routes", Url: s.cfg.AppSubURL + "/alerting/routes", Icon: "sitemap"})
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Notification policies", SubTitle: "Determine how alerts are routed to contact points", Id: "am-routes", Url: s.cfg.AppSubURL + "/alerting/routes", Icon: "sitemap"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingInstanceRead), ac.EvalPermission(ac.ActionAlertingInstancesExternalRead))) {
|
if hasAccess(ac.EvalAny(
|
||||||
|
ac.EvalPermission(ac.ActionAlertingInstanceRead),
|
||||||
|
ac.EvalPermission(ac.ActionAlertingInstancesExternalRead),
|
||||||
|
ac.EvalPermission(ac.ActionAlertingSilencesRead),
|
||||||
|
)) {
|
||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Silences", SubTitle: "Stop notifications from one or more alerting rules", Id: "silences", Url: s.cfg.AppSubURL + "/alerting/silences", Icon: "bell-slash"})
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Silences", SubTitle: "Stop notifications from one or more alerting rules", Id: "silences", Url: s.cfg.AppSubURL + "/alerting/silences", Icon: "bell-slash"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingInstanceRead), ac.EvalPermission(ac.ActionAlertingInstancesExternalRead))) {
|
||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
|
|||||||
roles: evaluateAccess([
|
roles: evaluateAccess([
|
||||||
AccessControlAction.AlertingInstanceRead,
|
AccessControlAction.AlertingInstanceRead,
|
||||||
AccessControlAction.AlertingInstancesExternalRead,
|
AccessControlAction.AlertingInstancesExternalRead,
|
||||||
|
AccessControlAction.AlertingSilenceRead,
|
||||||
]),
|
]),
|
||||||
component: importAlertingComponent(
|
component: importAlertingComponent(
|
||||||
() => import(/* webpackChunkName: "AlertSilences" */ 'app/features/alerting/unified/Silences')
|
() => import(/* webpackChunkName: "AlertSilences" */ 'app/features/alerting/unified/Silences')
|
||||||
|
|||||||
@@ -81,18 +81,6 @@ const ui = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetMocks = () => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
|
||||||
AccessControlAction.AlertingInstanceRead,
|
|
||||||
AccessControlAction.AlertingInstanceCreate,
|
|
||||||
AccessControlAction.AlertingInstanceUpdate,
|
|
||||||
AccessControlAction.AlertingInstancesExternalRead,
|
|
||||||
AccessControlAction.AlertingInstancesExternalWrite,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setUserLogged = (isLogged: boolean) => {
|
const setUserLogged = (isLogged: boolean) => {
|
||||||
config.bootData.user.isSignedIn = isLogged;
|
config.bootData.user.isSignedIn = isLogged;
|
||||||
config.bootData.user.name = isLogged ? 'admin' : '';
|
config.bootData.user.name = isLogged ? 'admin' : '';
|
||||||
@@ -115,12 +103,18 @@ const server = setupMswServer();
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setupDataSources(dataSources.am, dataSources[MOCK_DATASOURCE_NAME_BROKEN_ALERTMANAGER]);
|
setupDataSources(dataSources.am, dataSources[MOCK_DATASOURCE_NAME_BROKEN_ALERTMANAGER]);
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingInstanceRead,
|
||||||
|
AccessControlAction.AlertingInstanceCreate,
|
||||||
|
AccessControlAction.AlertingInstanceUpdate,
|
||||||
|
AccessControlAction.AlertingInstancesExternalRead,
|
||||||
|
AccessControlAction.AlertingInstancesExternalWrite,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Silences', () => {
|
afterEach(() => jest.resetAllMocks());
|
||||||
beforeAll(resetMocks);
|
|
||||||
afterEach(resetMocks);
|
|
||||||
|
|
||||||
|
describe('Silences', () => {
|
||||||
it(
|
it(
|
||||||
'loads and shows silences',
|
'loads and shows silences',
|
||||||
async () => {
|
async () => {
|
||||||
@@ -211,8 +205,6 @@ describe('Silences', () => {
|
|||||||
|
|
||||||
describe('Silence create/edit', () => {
|
describe('Silence create/edit', () => {
|
||||||
const baseUrlPath = '/alerting/silence/new';
|
const baseUrlPath = '/alerting/silence/new';
|
||||||
beforeAll(resetMocks);
|
|
||||||
afterEach(resetMocks);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockAlertRuleApi(server).getAlertRule(MOCK_SILENCE_ID_EXISTING_ALERT_RULE_UID, grafanaRulerRule);
|
mockAlertRuleApi(server).getAlertRule(MOCK_SILENCE_ID_EXISTING_ALERT_RULE_UID, grafanaRulerRule);
|
||||||
|
|||||||
@@ -5,14 +5,20 @@ import { Provider } from 'react-redux';
|
|||||||
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||||
import { setAlertmanagerChoices } from 'app/features/alerting/unified/mocks/server/configure';
|
import { setAlertmanagerChoices } from 'app/features/alerting/unified/mocks/server/configure';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
import { AccessControlAction } from 'app/types/accessControl';
|
||||||
|
|
||||||
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
||||||
|
import { grantUserPermissions } from '../mocks';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
|
||||||
import { GrafanaAlertmanagerDeliveryWarning } from './GrafanaAlertmanagerDeliveryWarning';
|
import { GrafanaAlertmanagerDeliveryWarning } from './GrafanaAlertmanagerDeliveryWarning';
|
||||||
setupMswServer();
|
setupMswServer();
|
||||||
|
|
||||||
describe('GrafanaAlertmanagerDeliveryWarning', () => {
|
describe('GrafanaAlertmanagerDeliveryWarning', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
grantUserPermissions([AccessControlAction.AlertingNotificationsRead]);
|
||||||
|
});
|
||||||
|
|
||||||
it('Should not render when the datasource is not Grafana', () => {
|
it('Should not render when the datasource is not Grafana', () => {
|
||||||
setAlertmanagerChoices(AlertmanagerChoice.External, 0);
|
setAlertmanagerChoices(AlertmanagerChoice.External, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Alert, useStyles2 } from '@grafana/ui/src';
|
|||||||
|
|
||||||
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
||||||
import { alertmanagerApi } from '../api/alertmanagerApi';
|
import { alertmanagerApi } from '../api/alertmanagerApi';
|
||||||
|
import { AlertingAction, useAlertingAbility } from '../hooks/useAbilities';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
|
||||||
interface GrafanaAlertmanagerDeliveryWarningProps {
|
interface GrafanaAlertmanagerDeliveryWarningProps {
|
||||||
@@ -14,12 +15,17 @@ interface GrafanaAlertmanagerDeliveryWarningProps {
|
|||||||
|
|
||||||
export function GrafanaAlertmanagerDeliveryWarning({ currentAlertmanager }: GrafanaAlertmanagerDeliveryWarningProps) {
|
export function GrafanaAlertmanagerDeliveryWarning({ currentAlertmanager }: GrafanaAlertmanagerDeliveryWarningProps) {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const viewingInternalAM = currentAlertmanager === GRAFANA_RULES_SOURCE_NAME;
|
const externalAlertmanager = currentAlertmanager !== GRAFANA_RULES_SOURCE_NAME;
|
||||||
|
|
||||||
|
const [readConfigurationStatusSupported, readConfigurationStatusAllowed] = useAlertingAbility(
|
||||||
|
AlertingAction.ReadConfigurationStatus
|
||||||
|
);
|
||||||
|
const canReadConfigurationStatus = readConfigurationStatusSupported && readConfigurationStatusAllowed;
|
||||||
|
|
||||||
const { currentData: amChoiceStatus } = alertmanagerApi.endpoints.getGrafanaAlertingConfigurationStatus.useQuery(
|
const { currentData: amChoiceStatus } = alertmanagerApi.endpoints.getGrafanaAlertingConfigurationStatus.useQuery(
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
skip: !viewingInternalAM,
|
skip: externalAlertmanager || !canReadConfigurationStatus,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -27,7 +33,7 @@ export function GrafanaAlertmanagerDeliveryWarning({ currentAlertmanager }: Graf
|
|||||||
amChoiceStatus?.alertmanagersChoice &&
|
amChoiceStatus?.alertmanagersChoice &&
|
||||||
[AlertmanagerChoice.External, AlertmanagerChoice.All].includes(amChoiceStatus?.alertmanagersChoice);
|
[AlertmanagerChoice.External, AlertmanagerChoice.All].includes(amChoiceStatus?.alertmanagersChoice);
|
||||||
|
|
||||||
if (!interactsWithExternalAMs || !viewingInternalAM) {
|
if (!interactsWithExternalAMs || externalAlertmanager) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const SilenceDetails = ({ silence }: Props) => {
|
|||||||
<div>{duration}</div>
|
<div>{duration}</div>
|
||||||
<div className={styles.title}>Created by</div>
|
<div className={styles.title}>Created by</div>
|
||||||
<div>{createdBy}</div>
|
<div>{createdBy}</div>
|
||||||
{silencedAlerts.length > 0 && (
|
{Array.isArray(silencedAlerts) && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.title}>Affected alerts</div>
|
<div className={styles.title}>Affected alerts</div>
|
||||||
<SilencedAlertsTable silencedAlerts={silencedAlerts} />
|
<SilencedAlertsTable silencedAlerts={silencedAlerts} />
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { MATCHER_ALERT_RULE_UID } from 'app/features/alerting/unified/utils/cons
|
|||||||
import { getDatasourceAPIUid, GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
import { getDatasourceAPIUid, GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import { MatcherOperator, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types';
|
import { MatcherOperator, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
|
||||||
|
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
||||||
import { SilenceFormFields } from '../../types/silence-form';
|
import { SilenceFormFields } from '../../types/silence-form';
|
||||||
import { matcherFieldToMatcher } from '../../utils/alertmanager';
|
import { matcherFieldToMatcher } from '../../utils/alertmanager';
|
||||||
import { makeAMLink } from '../../utils/misc';
|
import { makeAMLink } from '../../utils/misc';
|
||||||
@@ -113,6 +114,11 @@ export const SilencesEditor = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
ruleUid,
|
ruleUid,
|
||||||
}: SilencesEditorProps) => {
|
}: SilencesEditorProps) => {
|
||||||
|
const [previewAlertsSupported, previewAlertsAllowed] = useAlertmanagerAbility(
|
||||||
|
AlertmanagerAction.PreviewSilencedInstances
|
||||||
|
);
|
||||||
|
const canPreview = previewAlertsSupported && previewAlertsAllowed;
|
||||||
|
|
||||||
const [createSilence, { isLoading }] = alertSilencesApi.endpoints.createSilence.useMutation();
|
const [createSilence, { isLoading }] = alertSilencesApi.endpoints.createSilence.useMutation();
|
||||||
const formAPI = useForm({ defaultValues: formValues });
|
const formAPI = useForm({ defaultValues: formValues });
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
@@ -236,7 +242,9 @@ export const SilencesEditor = ({
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
|
{canPreview && (
|
||||||
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matchers} ruleUid={ruleUid} />
|
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matchers} ruleUid={ruleUid} />
|
||||||
|
)}
|
||||||
</FieldSet>
|
</FieldSet>
|
||||||
<Stack gap={1}>
|
<Stack gap={1}>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ import {
|
|||||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
import { alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi';
|
import { alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi';
|
||||||
import { alertmanagerApi } from 'app/features/alerting/unified/api/alertmanagerApi';
|
|
||||||
import { featureDiscoveryApi } from 'app/features/alerting/unified/api/featureDiscoveryApi';
|
import { featureDiscoveryApi } from 'app/features/alerting/unified/api/featureDiscoveryApi';
|
||||||
import { MATCHER_ALERT_RULE_UID, SILENCES_POLL_INTERVAL_MS } from 'app/features/alerting/unified/utils/constants';
|
import { MATCHER_ALERT_RULE_UID, SILENCES_POLL_INTERVAL_MS } from 'app/features/alerting/unified/utils/constants';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME, getDatasourceAPIUid } from 'app/features/alerting/unified/utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME, getDatasourceAPIUid } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import { AlertmanagerAlert, Silence, SilenceState } from 'app/plugins/datasource/alertmanager/types';
|
import { AlertmanagerAlert, Silence, SilenceState } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
|
||||||
|
import { alertmanagerApi } from '../../api/alertmanagerApi';
|
||||||
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
||||||
import { parseMatchers } from '../../utils/alertmanager';
|
import { parseMatchers } from '../../utils/alertmanager';
|
||||||
import { getSilenceFiltersFromUrlParams, makeAMLink, stringifyErrorLike } from '../../utils/misc';
|
import { getSilenceFiltersFromUrlParams, makeAMLink, stringifyErrorLike } from '../../utils/misc';
|
||||||
@@ -35,7 +35,7 @@ import { SilenceStateTag } from './SilenceStateTag';
|
|||||||
import { SilencesFilter } from './SilencesFilter';
|
import { SilencesFilter } from './SilencesFilter';
|
||||||
|
|
||||||
export interface SilenceTableItem extends Silence {
|
export interface SilenceTableItem extends Silence {
|
||||||
silencedAlerts: AlertmanagerAlert[];
|
silencedAlerts: AlertmanagerAlert[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SilenceTableColumnProps = DynamicTableColumnProps<SilenceTableItem>;
|
type SilenceTableColumnProps = DynamicTableColumnProps<SilenceTableItem>;
|
||||||
@@ -47,10 +47,15 @@ interface Props {
|
|||||||
const API_QUERY_OPTIONS = { pollingInterval: SILENCES_POLL_INTERVAL_MS, refetchOnFocus: true };
|
const API_QUERY_OPTIONS = { pollingInterval: SILENCES_POLL_INTERVAL_MS, refetchOnFocus: true };
|
||||||
|
|
||||||
const SilencesTable = ({ alertManagerSourceName }: Props) => {
|
const SilencesTable = ({ alertManagerSourceName }: Props) => {
|
||||||
|
const [previewAlertsSupported, previewAlertsAllowed] = useAlertmanagerAbility(
|
||||||
|
AlertmanagerAction.PreviewSilencedInstances
|
||||||
|
);
|
||||||
|
const canPreview = previewAlertsSupported && previewAlertsAllowed;
|
||||||
|
|
||||||
const { data: alertManagerAlerts = [], isLoading: amAlertsIsLoading } =
|
const { data: alertManagerAlerts = [], isLoading: amAlertsIsLoading } =
|
||||||
alertmanagerApi.endpoints.getAlertmanagerAlerts.useQuery(
|
alertmanagerApi.endpoints.getAlertmanagerAlerts.useQuery(
|
||||||
{ amSourceName: alertManagerSourceName, filter: { silenced: true, active: true, inhibited: true } },
|
{ amSourceName: alertManagerSourceName, filter: { silenced: true, active: true, inhibited: true } },
|
||||||
API_QUERY_OPTIONS
|
{ ...API_QUERY_OPTIONS, skip: !canPreview }
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -83,26 +88,26 @@ const SilencesTable = ({ alertManagerSourceName }: Props) => {
|
|||||||
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
||||||
};
|
};
|
||||||
return filteredSilencesNotExpired.map((silence) => {
|
return filteredSilencesNotExpired.map((silence) => {
|
||||||
const silencedAlerts = findSilencedAlerts(silence.id);
|
const silencedAlerts = canPreview ? findSilencedAlerts(silence.id) : undefined;
|
||||||
return {
|
return {
|
||||||
id: silence.id,
|
id: silence.id,
|
||||||
data: { ...silence, silencedAlerts },
|
data: { ...silence, silencedAlerts },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [filteredSilencesNotExpired, alertManagerAlerts]);
|
}, [filteredSilencesNotExpired, alertManagerAlerts, canPreview]);
|
||||||
|
|
||||||
const itemsExpired = useMemo((): SilenceTableItemProps[] => {
|
const itemsExpired = useMemo((): SilenceTableItemProps[] => {
|
||||||
const findSilencedAlerts = (id: string) => {
|
const findSilencedAlerts = (id: string) => {
|
||||||
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
||||||
};
|
};
|
||||||
return filteredSilencesExpired.map((silence) => {
|
return filteredSilencesExpired.map((silence) => {
|
||||||
const silencedAlerts = findSilencedAlerts(silence.id);
|
const silencedAlerts = canPreview ? findSilencedAlerts(silence.id) : undefined;
|
||||||
return {
|
return {
|
||||||
id: silence.id,
|
id: silence.id,
|
||||||
data: { ...silence, silencedAlerts },
|
data: { ...silence, silencedAlerts },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [filteredSilencesExpired, alertManagerAlerts]);
|
}, [filteredSilencesExpired, alertManagerAlerts, canPreview]);
|
||||||
|
|
||||||
if (isLoading || amAlertsIsLoading) {
|
if (isLoading || amAlertsIsLoading) {
|
||||||
return <LoadingPlaceholder text="Loading silences..." />;
|
return <LoadingPlaceholder text="Loading silences..." />;
|
||||||
@@ -304,7 +309,7 @@ function useColumns(alertManagerSourceName: string) {
|
|||||||
id: 'alerts',
|
id: 'alerts',
|
||||||
label: 'Alerts silenced',
|
label: 'Alerts silenced',
|
||||||
renderCell: function renderSilencedAlerts({ data: { silencedAlerts } }) {
|
renderCell: function renderSilencedAlerts({ data: { silencedAlerts } }) {
|
||||||
return <span data-testid="alerts">{silencedAlerts.length}</span>;
|
return <span data-testid="alerts">{Array.isArray(silencedAlerts) ? silencedAlerts.length : '-'}</span>;
|
||||||
},
|
},
|
||||||
size: 2,
|
size: 2,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -136,6 +136,10 @@ exports[`alertmanager abilities should report Create / Update / Delete actions a
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
],
|
],
|
||||||
|
"preview-silenced-alerts": [
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
],
|
||||||
"update-external-configuration": [
|
"update-external-configuration": [
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@@ -245,6 +249,10 @@ exports[`alertmanager abilities should report everything except exporting for Mi
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
"preview-silenced-alerts": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
],
|
||||||
"update-external-configuration": [
|
"update-external-configuration": [
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
@@ -354,6 +362,10 @@ exports[`alertmanager abilities should report everything is supported for builti
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
"preview-silenced-alerts": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
],
|
||||||
"update-external-configuration": [
|
"update-external-configuration": [
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export enum AlertmanagerAction {
|
|||||||
CreateSilence = 'create-silence',
|
CreateSilence = 'create-silence',
|
||||||
ViewSilence = 'view-silence',
|
ViewSilence = 'view-silence',
|
||||||
UpdateSilence = 'update-silence',
|
UpdateSilence = 'update-silence',
|
||||||
|
PreviewSilencedInstances = 'preview-silenced-alerts',
|
||||||
|
|
||||||
// mute timings
|
// mute timings
|
||||||
ViewMuteTiming = 'view-mute-timing',
|
ViewMuteTiming = 'view-mute-timing',
|
||||||
@@ -83,6 +84,7 @@ export enum AlertingAction {
|
|||||||
UpdateAlertRule = 'update-alert-rule',
|
UpdateAlertRule = 'update-alert-rule',
|
||||||
DeleteAlertRule = 'delete-alert-rule',
|
DeleteAlertRule = 'delete-alert-rule',
|
||||||
ExportGrafanaManagedRules = 'export-grafana-managed-rules',
|
ExportGrafanaManagedRules = 'export-grafana-managed-rules',
|
||||||
|
ReadConfigurationStatus = 'read-configuration-status',
|
||||||
|
|
||||||
// external (any compatible alerting data source)
|
// external (any compatible alerting data source)
|
||||||
CreateExternalAlertRule = 'create-external-alert-rule',
|
CreateExternalAlertRule = 'create-external-alert-rule',
|
||||||
@@ -110,6 +112,11 @@ export const useAlertingAbilities = (): Abilities<AlertingAction> => {
|
|||||||
[AlertingAction.UpdateAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleUpdate),
|
[AlertingAction.UpdateAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleUpdate),
|
||||||
[AlertingAction.DeleteAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleDelete),
|
[AlertingAction.DeleteAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleDelete),
|
||||||
[AlertingAction.ExportGrafanaManagedRules]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleRead),
|
[AlertingAction.ExportGrafanaManagedRules]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleRead),
|
||||||
|
[AlertingAction.ReadConfigurationStatus]: [
|
||||||
|
AlwaysSupported,
|
||||||
|
ctx.hasPermission(AccessControlAction.AlertingInstanceRead) ||
|
||||||
|
ctx.hasPermission(AccessControlAction.AlertingNotificationsRead),
|
||||||
|
],
|
||||||
|
|
||||||
// external
|
// external
|
||||||
[AlertingAction.CreateExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalWrite),
|
[AlertingAction.CreateExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalWrite),
|
||||||
@@ -241,6 +248,7 @@ export function useAllAlertmanagerAbilities(): Abilities<AlertmanagerAction> {
|
|||||||
[AlertmanagerAction.CreateSilence]: toAbility(AlwaysSupported, instancePermissions.create),
|
[AlertmanagerAction.CreateSilence]: toAbility(AlwaysSupported, instancePermissions.create),
|
||||||
[AlertmanagerAction.ViewSilence]: toAbility(AlwaysSupported, instancePermissions.read),
|
[AlertmanagerAction.ViewSilence]: toAbility(AlwaysSupported, instancePermissions.read),
|
||||||
[AlertmanagerAction.UpdateSilence]: toAbility(AlwaysSupported, instancePermissions.update),
|
[AlertmanagerAction.UpdateSilence]: toAbility(AlwaysSupported, instancePermissions.update),
|
||||||
|
[AlertmanagerAction.PreviewSilencedInstances]: toAbility(AlwaysSupported, instancePermissions.read),
|
||||||
// -- mute timtings --
|
// -- mute timtings --
|
||||||
[AlertmanagerAction.CreateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.create),
|
[AlertmanagerAction.CreateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.create),
|
||||||
[AlertmanagerAction.ViewMuteTiming]: toAbility(AlwaysSupported, notificationsPermissions.read),
|
[AlertmanagerAction.ViewMuteTiming]: toAbility(AlwaysSupported, notificationsPermissions.read),
|
||||||
|
|||||||
@@ -48,6 +48,21 @@ export const notificationsPermissions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const silencesPermissions = {
|
||||||
|
read: {
|
||||||
|
grafana: AccessControlAction.AlertingSilenceRead,
|
||||||
|
external: AccessControlAction.AlertingInstanceRead,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
grafana: AccessControlAction.AlertingSilenceCreate,
|
||||||
|
external: AccessControlAction.AlertingInstancesExternalWrite,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
grafana: AccessControlAction.AlertingSilenceUpdate,
|
||||||
|
external: AccessControlAction.AlertingInstancesExternalWrite,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const provisioningPermissions = {
|
export const provisioningPermissions = {
|
||||||
read: AccessControlAction.AlertingProvisioningRead,
|
read: AccessControlAction.AlertingProvisioningRead,
|
||||||
readSecrets: AccessControlAction.AlertingProvisioningReadSecrets,
|
readSecrets: AccessControlAction.AlertingProvisioningReadSecrets,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { alertmanagerApi } from '../api/alertmanagerApi';
|
|||||||
import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources';
|
import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources';
|
||||||
import { isAlertManagerWithConfigAPI } from '../state/AlertmanagerContext';
|
import { isAlertManagerWithConfigAPI } from '../state/AlertmanagerContext';
|
||||||
|
|
||||||
import { instancesPermissions, notificationsPermissions } from './access-control';
|
import { instancesPermissions, notificationsPermissions, silencesPermissions } from './access-control';
|
||||||
import { getAllDataSources } from './config';
|
import { getAllDataSources } from './config';
|
||||||
|
|
||||||
export const GRAFANA_RULES_SOURCE_NAME = 'grafana';
|
export const GRAFANA_RULES_SOURCE_NAME = 'grafana';
|
||||||
@@ -144,9 +144,15 @@ export function getAlertManagerDataSourcesByPermission(permission: 'instance' |
|
|||||||
const permissions = {
|
const permissions = {
|
||||||
instance: instancesPermissions.read,
|
instance: instancesPermissions.read,
|
||||||
notification: notificationsPermissions.read,
|
notification: notificationsPermissions.read,
|
||||||
|
silence: silencesPermissions.read,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (contextSrv.hasPermission(permissions[permission].grafana)) {
|
const builtinAlertmanagerPermissions = Object.values(permissions).flatMap((permissions) => permissions.grafana);
|
||||||
|
const hasPermissionsForInternalAlertmanager = builtinAlertmanagerPermissions.some((permission) =>
|
||||||
|
contextSrv.hasPermission(permission)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasPermissionsForInternalAlertmanager) {
|
||||||
availableInternalDataSources.push(grafanaAlertManagerDataSource);
|
availableInternalDataSources.push(grafanaAlertManagerDataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user