mirror of https://github.com/grafana/grafana.git
499 lines
22 KiB
TypeScript
499 lines
22 KiB
TypeScript
import { useMemo } from 'react';
|
||
|
||
import { contextSrv as ctx } from 'app/core/services/context_srv';
|
||
import { PERMISSIONS_CONTACT_POINTS_READ } from 'app/features/alerting/unified/components/contact-points/permissions';
|
||
import {
|
||
PERMISSIONS_TIME_INTERVALS_MODIFY,
|
||
PERMISSIONS_TIME_INTERVALS_READ,
|
||
} from 'app/features/alerting/unified/components/mute-timings/permissions';
|
||
import {
|
||
PERMISSIONS_NOTIFICATION_POLICIES_MODIFY,
|
||
PERMISSIONS_NOTIFICATION_POLICIES_READ,
|
||
} from 'app/features/alerting/unified/components/notification-policies/permissions';
|
||
import { useFolder } from 'app/features/alerting/unified/hooks/useFolder';
|
||
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
|
||
import { AccessControlAction } from 'app/types';
|
||
import { CombinedRule, RuleGroupIdentifierV2 } from 'app/types/unified-alerting';
|
||
import { RulerRuleDTO } from 'app/types/unified-alerting-dto';
|
||
|
||
import { alertmanagerApi } from '../api/alertmanagerApi';
|
||
import { useAlertmanager } from '../state/AlertmanagerContext';
|
||
import { getInstancesPermissions, getNotificationsPermissions, getRulesPermissions } from '../utils/access-control';
|
||
import { getRulesSourceName } from '../utils/datasource';
|
||
import { getGroupOriginName } from '../utils/groupIdentifier';
|
||
import { isAdmin } from '../utils/misc';
|
||
import { isFederatedRuleGroup, isGrafanaRecordingRule, isGrafanaRulerRule, isPluginProvidedRule } from '../utils/rules';
|
||
|
||
import { useIsRuleEditable } from './useIsRuleEditable';
|
||
|
||
/**
|
||
* These hooks will determine if
|
||
* 1. the action is supported in the current context (alertmanager, alert rule or general context)
|
||
* 2. user is allowed to perform actions based on their set of permissions / assigned role
|
||
*/
|
||
|
||
// this enum lists all of the available actions we can perform within the context of an alertmanager
|
||
export enum AlertmanagerAction {
|
||
// configuration
|
||
ViewExternalConfiguration = 'view-external-configuration',
|
||
UpdateExternalConfiguration = 'update-external-configuration',
|
||
|
||
// contact points
|
||
CreateContactPoint = 'create-contact-point',
|
||
ViewContactPoint = 'view-contact-point',
|
||
UpdateContactPoint = 'edit-contact-points',
|
||
DeleteContactPoint = 'delete-contact-point',
|
||
ExportContactPoint = 'export-contact-point',
|
||
|
||
// notification templates
|
||
CreateNotificationTemplate = 'create-notification-template',
|
||
ViewNotificationTemplate = 'view-notification-template',
|
||
UpdateNotificationTemplate = 'edit-notification-template',
|
||
DeleteNotificationTemplate = 'delete-notification-template',
|
||
DecryptSecrets = 'decrypt-secrets',
|
||
|
||
// notification policies
|
||
CreateNotificationPolicy = 'create-notification-policy',
|
||
ViewNotificationPolicyTree = 'view-notification-policy-tree',
|
||
UpdateNotificationPolicyTree = 'update-notification-policy-tree',
|
||
DeleteNotificationPolicy = 'delete-notification-policy',
|
||
ExportNotificationPolicies = 'export-notification-policies',
|
||
ViewAutogeneratedPolicyTree = 'view-autogenerated-policy-tree',
|
||
|
||
// silences – these cannot be deleted only "expired" (updated)
|
||
CreateSilence = 'create-silence',
|
||
ViewSilence = 'view-silence',
|
||
UpdateSilence = 'update-silence',
|
||
PreviewSilencedInstances = 'preview-silenced-alerts',
|
||
|
||
// mute timings
|
||
ViewMuteTiming = 'view-mute-timing',
|
||
CreateMuteTiming = 'create-mute-timing',
|
||
UpdateMuteTiming = 'update-mute-timing',
|
||
DeleteMuteTiming = 'delete-mute-timing',
|
||
ExportMuteTimings = 'export-mute-timings',
|
||
|
||
// Alert groups
|
||
ViewAlertGroups = 'view-alert-groups',
|
||
}
|
||
|
||
// this enum lists all of the available actions we can take on a single alert rule
|
||
export enum AlertRuleAction {
|
||
Duplicate = 'duplicate-alert-rule',
|
||
View = 'view-alert-rule',
|
||
Update = 'update-alert-rule',
|
||
Delete = 'delete-alert-rule',
|
||
Explore = 'explore-alert-rule',
|
||
Silence = 'silence-alert-rule',
|
||
ModifyExport = 'modify-export-rule',
|
||
Pause = 'pause-alert-rule',
|
||
Restore = 'restore-alert-rule',
|
||
}
|
||
|
||
// this enum lists all of the actions we can perform within alerting in general, not linked to a specific
|
||
// alert source, rule or alertmanager
|
||
export enum AlertingAction {
|
||
// internal (Grafana managed)
|
||
CreateAlertRule = 'create-alert-rule',
|
||
ViewAlertRule = 'view-alert-rule',
|
||
UpdateAlertRule = 'update-alert-rule',
|
||
DeleteAlertRule = 'delete-alert-rule',
|
||
ExportGrafanaManagedRules = 'export-grafana-managed-rules',
|
||
ReadConfigurationStatus = 'read-configuration-status',
|
||
|
||
// external (any compatible alerting data source)
|
||
CreateExternalAlertRule = 'create-external-alert-rule',
|
||
ViewExternalAlertRule = 'view-external-alert-rule',
|
||
UpdateExternalAlertRule = 'update-external-alert-rule',
|
||
DeleteExternalAlertRule = 'delete-external-alert-rule',
|
||
}
|
||
|
||
// these just makes it easier to read the code :)
|
||
const AlwaysSupported = true;
|
||
const NotSupported = false;
|
||
|
||
export type Action = AlertmanagerAction | AlertingAction | AlertRuleAction;
|
||
export type Ability = [actionSupported: boolean, actionAllowed: boolean];
|
||
export type Abilities<T extends Action> = Record<T, Ability>;
|
||
|
||
/**
|
||
* This one will check for alerting abilities that don't apply to any particular alert source or alert rule
|
||
*/
|
||
export const useAlertingAbilities = (): Abilities<AlertingAction> => {
|
||
return {
|
||
// internal (Grafana managed)
|
||
[AlertingAction.CreateAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleCreate),
|
||
[AlertingAction.ViewAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleRead),
|
||
[AlertingAction.UpdateAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleUpdate),
|
||
[AlertingAction.DeleteAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleDelete),
|
||
[AlertingAction.ExportGrafanaManagedRules]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleRead),
|
||
[AlertingAction.ReadConfigurationStatus]: [
|
||
AlwaysSupported,
|
||
ctx.hasPermission(AccessControlAction.AlertingInstanceRead) ||
|
||
ctx.hasPermission(AccessControlAction.AlertingNotificationsRead),
|
||
],
|
||
|
||
// external
|
||
[AlertingAction.CreateExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalWrite),
|
||
[AlertingAction.ViewExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalRead),
|
||
[AlertingAction.UpdateExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalWrite),
|
||
[AlertingAction.DeleteExternalAlertRule]: toAbility(AlwaysSupported, AccessControlAction.AlertingRuleExternalWrite),
|
||
};
|
||
};
|
||
|
||
export const useAlertingAbility = (action: AlertingAction): Ability => {
|
||
const allAbilities = useAlertingAbilities();
|
||
return allAbilities[action];
|
||
};
|
||
|
||
/**
|
||
* This hook will check if we support the action and have sufficient permissions for it on a single alert rule
|
||
*/
|
||
export function useAlertRuleAbility(rule: CombinedRule, action: AlertRuleAction): Ability {
|
||
const abilities = useAllAlertRuleAbilities(rule);
|
||
|
||
return useMemo(() => {
|
||
return abilities[action];
|
||
}, [abilities, action]);
|
||
}
|
||
|
||
export function useAlertRuleAbilities(rule: CombinedRule, actions: AlertRuleAction[]): Ability[] {
|
||
const abilities = useAllAlertRuleAbilities(rule);
|
||
|
||
return useMemo(() => {
|
||
return actions.map((action) => abilities[action]);
|
||
}, [abilities, actions]);
|
||
}
|
||
|
||
export function useRulerRuleAbility(
|
||
rule: RulerRuleDTO | undefined,
|
||
groupIdentifier: RuleGroupIdentifierV2,
|
||
action: AlertRuleAction
|
||
): Ability {
|
||
const abilities = useAllRulerRuleAbilities(rule, groupIdentifier);
|
||
|
||
return useMemo(() => {
|
||
return abilities[action];
|
||
}, [abilities, action]);
|
||
}
|
||
|
||
export function useRulerRuleAbilities(
|
||
rule: RulerRuleDTO,
|
||
groupIdentifier: RuleGroupIdentifierV2,
|
||
actions: AlertRuleAction[]
|
||
): Ability[] {
|
||
const abilities = useAllRulerRuleAbilities(rule, groupIdentifier);
|
||
|
||
return useMemo(() => {
|
||
return actions.map((action) => abilities[action]);
|
||
}, [abilities, actions]);
|
||
}
|
||
|
||
// This hook is being called a lot in different places
|
||
// In some cases multiple times for ~80 rules (e.g. on the list page)
|
||
// We need to investigate further if some of these calls are redundant
|
||
// In the meantime, memoizing the result helps
|
||
export function useAllAlertRuleAbilities(rule: CombinedRule): Abilities<AlertRuleAction> {
|
||
const rulesSourceName = getRulesSourceName(rule.namespace.rulesSource);
|
||
|
||
const {
|
||
isEditable,
|
||
isRemovable,
|
||
isRulerAvailable = false,
|
||
loading,
|
||
} = useIsRuleEditable(rulesSourceName, rule.rulerRule);
|
||
const [_, exportAllowed] = useAlertingAbility(AlertingAction.ExportGrafanaManagedRules);
|
||
const canSilence = useCanSilence(rule.rulerRule);
|
||
|
||
const abilities = useMemo<Abilities<AlertRuleAction>>(() => {
|
||
const isProvisioned = isGrafanaRulerRule(rule.rulerRule) && Boolean(rule.rulerRule.grafana_alert.provenance);
|
||
const isFederated = isFederatedRuleGroup(rule.group);
|
||
const isGrafanaManagedAlertRule = isGrafanaRulerRule(rule.rulerRule);
|
||
const isPluginProvided = isPluginProvidedRule(rule.rulerRule);
|
||
|
||
// if a rule is either provisioned, federated or provided by a plugin rule, we don't allow it to be removed or edited
|
||
const immutableRule = isProvisioned || isFederated || isPluginProvided;
|
||
|
||
// while we gather info, pretend it's not supported
|
||
const MaybeSupported = loading ? NotSupported : isRulerAvailable;
|
||
const MaybeSupportedUnlessImmutable = immutableRule ? NotSupported : MaybeSupported;
|
||
|
||
// Creating duplicates of plugin-provided rules does not seem to make a lot of sense
|
||
const duplicateSupported = isPluginProvided ? NotSupported : MaybeSupported;
|
||
|
||
const rulesPermissions = getRulesPermissions(rulesSourceName);
|
||
|
||
const abilities: Abilities<AlertRuleAction> = {
|
||
[AlertRuleAction.Duplicate]: toAbility(duplicateSupported, rulesPermissions.create),
|
||
[AlertRuleAction.View]: toAbility(AlwaysSupported, rulesPermissions.read),
|
||
[AlertRuleAction.Update]: [MaybeSupportedUnlessImmutable, isEditable ?? false],
|
||
[AlertRuleAction.Delete]: [MaybeSupportedUnlessImmutable, isRemovable ?? false],
|
||
[AlertRuleAction.Explore]: toAbility(AlwaysSupported, AccessControlAction.DataSourcesExplore),
|
||
[AlertRuleAction.Silence]: canSilence,
|
||
[AlertRuleAction.ModifyExport]: [isGrafanaManagedAlertRule, exportAllowed],
|
||
[AlertRuleAction.Pause]: [MaybeSupportedUnlessImmutable && isGrafanaManagedAlertRule, isEditable ?? false],
|
||
[AlertRuleAction.Restore]: [MaybeSupportedUnlessImmutable && isGrafanaManagedAlertRule, isEditable ?? false],
|
||
};
|
||
|
||
return abilities;
|
||
}, [rule, loading, isRulerAvailable, isEditable, isRemovable, rulesSourceName, exportAllowed, canSilence]);
|
||
|
||
return abilities;
|
||
}
|
||
|
||
export function useAllRulerRuleAbilities(
|
||
rule: RulerRuleDTO | undefined,
|
||
groupIdentifier: RuleGroupIdentifierV2
|
||
): Abilities<AlertRuleAction> {
|
||
const rulesSourceName = getGroupOriginName(groupIdentifier);
|
||
|
||
const { isEditable, isRemovable, isRulerAvailable = false, loading } = useIsRuleEditable(rulesSourceName, rule);
|
||
const [_, exportAllowed] = useAlertingAbility(AlertingAction.ExportGrafanaManagedRules);
|
||
const canSilence = useCanSilence(rule);
|
||
|
||
const abilities = useMemo<Abilities<AlertRuleAction>>(() => {
|
||
const isProvisioned = isGrafanaRulerRule(rule) && Boolean(rule.grafana_alert.provenance);
|
||
// const isFederated = isFederatedRuleGroup();
|
||
const isFederated = false;
|
||
const isGrafanaManagedAlertRule = isGrafanaRulerRule(rule);
|
||
const isPluginProvided = isPluginProvidedRule(rule);
|
||
|
||
// if a rule is either provisioned, federated or provided by a plugin rule, we don't allow it to be removed or edited
|
||
const immutableRule = isProvisioned || isFederated || isPluginProvided;
|
||
|
||
// while we gather info, pretend it's not supported
|
||
const MaybeSupported = loading ? NotSupported : isRulerAvailable;
|
||
const MaybeSupportedUnlessImmutable = immutableRule ? NotSupported : MaybeSupported;
|
||
|
||
// Creating duplicates of plugin-provided rules does not seem to make a lot of sense
|
||
const duplicateSupported = isPluginProvided ? NotSupported : MaybeSupported;
|
||
|
||
const rulesPermissions = getRulesPermissions(rulesSourceName);
|
||
|
||
const abilities: Abilities<AlertRuleAction> = {
|
||
[AlertRuleAction.Duplicate]: toAbility(duplicateSupported, rulesPermissions.create),
|
||
[AlertRuleAction.View]: toAbility(AlwaysSupported, rulesPermissions.read),
|
||
[AlertRuleAction.Update]: [MaybeSupportedUnlessImmutable, isEditable ?? false],
|
||
[AlertRuleAction.Delete]: [MaybeSupportedUnlessImmutable, isRemovable ?? false],
|
||
[AlertRuleAction.Explore]: toAbility(AlwaysSupported, AccessControlAction.DataSourcesExplore),
|
||
[AlertRuleAction.Silence]: canSilence,
|
||
[AlertRuleAction.ModifyExport]: [isGrafanaManagedAlertRule, exportAllowed],
|
||
[AlertRuleAction.Pause]: [MaybeSupportedUnlessImmutable && isGrafanaManagedAlertRule, isEditable ?? false],
|
||
[AlertRuleAction.Restore]: [MaybeSupportedUnlessImmutable && isGrafanaManagedAlertRule, isEditable ?? false],
|
||
};
|
||
|
||
return abilities;
|
||
}, [rule, loading, isRulerAvailable, rulesSourceName, isEditable, isRemovable, canSilence, exportAllowed]);
|
||
|
||
return abilities;
|
||
}
|
||
|
||
export function useAllAlertmanagerAbilities(): Abilities<AlertmanagerAction> {
|
||
const {
|
||
selectedAlertmanager,
|
||
hasConfigurationAPI,
|
||
isGrafanaAlertmanager: isGrafanaFlavoredAlertmanager,
|
||
} = useAlertmanager();
|
||
|
||
// These are used for interacting with Alertmanager resources where we apply alert.notifications:<name> permissions.
|
||
// There are different permissions based on wether the built-in alertmanager is selected (grafana) or an external one.
|
||
const notificationsPermissions = getNotificationsPermissions(selectedAlertmanager!);
|
||
const instancePermissions = getInstancesPermissions(selectedAlertmanager!);
|
||
|
||
// list out all of the abilities, and if the user has permissions to perform them
|
||
const abilities: Abilities<AlertmanagerAction> = {
|
||
// -- configuration --
|
||
[AlertmanagerAction.ViewExternalConfiguration]: toAbility(
|
||
AlwaysSupported,
|
||
AccessControlAction.AlertingNotificationsExternalRead
|
||
),
|
||
[AlertmanagerAction.UpdateExternalConfiguration]: toAbility(
|
||
hasConfigurationAPI,
|
||
AccessControlAction.AlertingNotificationsExternalWrite
|
||
),
|
||
// -- contact points --
|
||
[AlertmanagerAction.CreateContactPoint]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.create,
|
||
// TODO: Move this into the permissions config and generalise that code to allow for an array of permissions
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversCreate] : [])
|
||
),
|
||
[AlertmanagerAction.ViewContactPoint]: toAbility(
|
||
AlwaysSupported,
|
||
notificationsPermissions.read,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_CONTACT_POINTS_READ : [])
|
||
),
|
||
[AlertmanagerAction.UpdateContactPoint]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.update,
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversWrite] : [])
|
||
),
|
||
[AlertmanagerAction.DeleteContactPoint]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.delete,
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversWrite] : [])
|
||
),
|
||
// At the time of writing, only Grafana flavored alertmanager supports exporting,
|
||
// and if a user can view the contact point, then they can also export it
|
||
// So the only check we make is if the alertmanager is Grafana flavored
|
||
[AlertmanagerAction.ExportContactPoint]: [isGrafanaFlavoredAlertmanager, isGrafanaFlavoredAlertmanager],
|
||
// -- notification templates --
|
||
[AlertmanagerAction.CreateNotificationTemplate]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.create,
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesWrite] : [])
|
||
),
|
||
[AlertmanagerAction.ViewNotificationTemplate]: toAbility(
|
||
AlwaysSupported,
|
||
notificationsPermissions.read,
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesRead] : [])
|
||
),
|
||
[AlertmanagerAction.UpdateNotificationTemplate]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.update,
|
||
...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesWrite] : [])
|
||
),
|
||
[AlertmanagerAction.DeleteNotificationTemplate]: toAbility(hasConfigurationAPI, notificationsPermissions.delete),
|
||
// -- notification policies --
|
||
[AlertmanagerAction.CreateNotificationPolicy]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.create,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_NOTIFICATION_POLICIES_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.ViewNotificationPolicyTree]: toAbility(
|
||
AlwaysSupported,
|
||
notificationsPermissions.read,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_NOTIFICATION_POLICIES_READ : [])
|
||
),
|
||
[AlertmanagerAction.UpdateNotificationPolicyTree]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.update,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_NOTIFICATION_POLICIES_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.DeleteNotificationPolicy]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.delete,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_NOTIFICATION_POLICIES_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.ExportNotificationPolicies]: toAbility(
|
||
isGrafanaFlavoredAlertmanager,
|
||
notificationsPermissions.read
|
||
),
|
||
[AlertmanagerAction.DecryptSecrets]: toAbility(
|
||
isGrafanaFlavoredAlertmanager,
|
||
notificationsPermissions.provisioning.readSecrets
|
||
),
|
||
[AlertmanagerAction.ViewAutogeneratedPolicyTree]: [isGrafanaFlavoredAlertmanager, isAdmin()],
|
||
// -- silences --
|
||
// for now, all supported Alertmanager flavors have API endpoints for managing silences
|
||
[AlertmanagerAction.CreateSilence]: toAbility(AlwaysSupported, instancePermissions.create),
|
||
[AlertmanagerAction.ViewSilence]: toAbility(AlwaysSupported, instancePermissions.read),
|
||
[AlertmanagerAction.UpdateSilence]: toAbility(AlwaysSupported, instancePermissions.update),
|
||
[AlertmanagerAction.PreviewSilencedInstances]: toAbility(AlwaysSupported, instancePermissions.read),
|
||
// -- mute timings --
|
||
[AlertmanagerAction.CreateMuteTiming]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.create,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.ViewMuteTiming]: toAbility(
|
||
AlwaysSupported,
|
||
notificationsPermissions.read,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_READ : [])
|
||
),
|
||
[AlertmanagerAction.UpdateMuteTiming]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.update,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.DeleteMuteTiming]: toAbility(
|
||
hasConfigurationAPI,
|
||
notificationsPermissions.delete,
|
||
...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : [])
|
||
),
|
||
[AlertmanagerAction.ExportMuteTimings]: toAbility(isGrafanaFlavoredAlertmanager, notificationsPermissions.read),
|
||
[AlertmanagerAction.ViewAlertGroups]: toAbility(AlwaysSupported, instancePermissions.read),
|
||
};
|
||
|
||
return abilities;
|
||
}
|
||
|
||
export function useAlertmanagerAbility(action: AlertmanagerAction): Ability {
|
||
const abilities = useAllAlertmanagerAbilities();
|
||
|
||
return useMemo(() => {
|
||
return abilities[action];
|
||
}, [abilities, action]);
|
||
}
|
||
|
||
export function useAlertmanagerAbilities(actions: AlertmanagerAction[]): Ability[] {
|
||
const abilities = useAllAlertmanagerAbilities();
|
||
|
||
return useMemo(() => {
|
||
return actions.map((action) => abilities[action]);
|
||
}, [abilities, actions]);
|
||
}
|
||
|
||
const { useGetGrafanaAlertingConfigurationStatusQuery } = alertmanagerApi;
|
||
/**
|
||
* We don't want to show the silence button if either
|
||
* 1. the user has no permissions to create silences
|
||
* 2. the admin has configured to only send instances to external AMs
|
||
*/
|
||
function useCanSilence(rule?: RulerRuleDTO): [boolean, boolean] {
|
||
const folderUID = isGrafanaRulerRule(rule) ? rule.grafana_alert.namespace_uid : undefined;
|
||
const { loading: folderIsLoading, folder } = useFolder(folderUID);
|
||
|
||
const isGrafanaManagedRule = rule && isGrafanaRulerRule(rule);
|
||
const isGrafanaRecording = rule && isGrafanaRecordingRule(rule);
|
||
|
||
const silenceSupported = useGrafanaRulesSilenceSupport();
|
||
const canSilenceInFolder = useCanSilenceInFolder(folderUID);
|
||
|
||
if (!rule) {
|
||
return [false, false];
|
||
}
|
||
|
||
// we don't support silencing when the rule is not a Grafana managed alerting rule
|
||
// we simply don't know what Alertmanager the ruler is sending alerts to
|
||
if (!isGrafanaManagedRule || isGrafanaRecording || folderIsLoading || !folder) {
|
||
return [false, false];
|
||
}
|
||
|
||
return [silenceSupported, canSilenceInFolder];
|
||
}
|
||
|
||
function useCanSilenceInFolder(folderUID?: string) {
|
||
const folderPermissions = useFolderPermissions(folderUID);
|
||
|
||
const hasFolderSilencePermission = folderPermissions[AccessControlAction.AlertingSilenceCreate] ?? false;
|
||
const hasGlobalSilencePermission = ctx.hasPermission(AccessControlAction.AlertingInstanceCreate);
|
||
|
||
// User is permitted to silence if they either have the "global" permissions of "AlertingInstanceCreate",
|
||
// or the folder specific access control of "AlertingSilenceCreate"
|
||
const allowedToSilence = hasGlobalSilencePermission || hasFolderSilencePermission;
|
||
return allowedToSilence;
|
||
}
|
||
|
||
function useGrafanaRulesSilenceSupport() {
|
||
const { currentData: amConfigStatus, isLoading } = useGetGrafanaAlertingConfigurationStatusQuery(undefined);
|
||
|
||
const interactsOnlyWithExternalAMs = amConfigStatus?.alertmanagersChoice === AlertmanagerChoice.External;
|
||
const interactsWithAll = amConfigStatus?.alertmanagersChoice === AlertmanagerChoice.All;
|
||
const silenceSupported = !interactsOnlyWithExternalAMs || interactsWithAll;
|
||
|
||
return isLoading ? false : silenceSupported;
|
||
}
|
||
|
||
function useFolderPermissions(folderUID?: string): Record<string, boolean> {
|
||
const { folder } = useFolder(folderUID);
|
||
return folder?.accessControl ?? {};
|
||
}
|
||
|
||
// just a convenient function
|
||
const toAbility = (
|
||
supported: boolean,
|
||
/** If user has any of these permissions, then they are allowed to perform the action */
|
||
...actions: AccessControlAction[]
|
||
): Ability => [supported, actions.some((action) => action && ctx.hasPermission(action))];
|