From 6a050ff5514dd5dcc49a05c0ded828c2a8423fcb Mon Sep 17 00:00:00 2001 From: Konrad Lalik Date: Tue, 26 Aug 2025 08:39:36 +0200 Subject: [PATCH] Alerting: Fix permission checks for the Import to GMA (#109950) Fix permission checks for the Import rules buttons and import to GMA page --- public/app/features/alerting/routes.tsx | 5 ++++- .../unified/components/rules/CloudRules.tsx | 9 +++++++-- .../unified/rule-list/RuleList.v2.test.tsx | 20 ++++++++++++------- .../unified/rule-list/RuleList.v2.tsx | 9 +++++++-- public/app/types/accessControl.ts | 5 +++++ 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/public/app/features/alerting/routes.tsx b/public/app/features/alerting/routes.tsx index e51fd7cc27e..96bf0a4b421 100644 --- a/public/app/features/alerting/routes.tsx +++ b/public/app/features/alerting/routes.tsx @@ -226,7 +226,10 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] { }, { path: '/alerting/import-datasource-managed-rules', - roles: () => ['Admin'], + roles: evaluateAccess([ + AccessControlAction.AlertingRuleCreate, + AccessControlAction.AlertingProvisioningSetStatus, + ]), component: config.featureToggles.alertingMigrationUI ? importAlertingComponent( () => diff --git a/public/app/features/alerting/unified/components/rules/CloudRules.tsx b/public/app/features/alerting/unified/components/rules/CloudRules.tsx index e92d40c2969..efb102eb7ea 100644 --- a/public/app/features/alerting/unified/components/rules/CloudRules.tsx +++ b/public/app/features/alerting/unified/components/rules/CloudRules.tsx @@ -6,6 +6,8 @@ import { GrafanaTheme2, urlUtil } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { Badge, LinkButton, LoadingPlaceholder, Pagination, Spinner, Stack, Text, useStyles2 } from '@grafana/ui'; +import { contextSrv } from 'app/core/services/context_srv'; +import { AccessControlAction } from 'app/types/accessControl'; import { CombinedRuleNamespace } from 'app/types/unified-alerting'; import { DEFAULT_PER_PAGE_PAGINATION } from '../../../../../core/constants'; @@ -14,7 +16,6 @@ import { usePagination } from '../../hooks/usePagination'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { getPaginationStyles } from '../../styles/pagination'; import { getRulesDataSources, getRulesSourceUid } from '../../utils/datasource'; -import { isAdmin } from '../../utils/misc'; import { isAsyncRequestStatePending } from '../../utils/redux'; import { createRelativeUrl } from '../../utils/url'; @@ -49,7 +50,11 @@ export const CloudRules = ({ namespaces, expandAll }: Props) => { DEFAULT_PER_PAGE_PAGINATION ); - const canMigrateToGMA = hasDataSourcesConfigured && isAdmin() && config.featureToggles.alertingMigrationUI; + const canMigrateToGMA = + hasDataSourcesConfigured && + config.featureToggles.alertingMigrationUI && + contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate) && + contextSrv.hasPermission(AccessControlAction.AlertingProvisioningSetStatus); return (
diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx index 3b150a698df..33024dd123a 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx @@ -255,9 +255,12 @@ describe('RuleListActions', () => { describe('Import Alert Rules', () => { testWithFeatureToggles(['alertingMigrationUI']); - it('should show "Import alert rules" option when user is admin and feature toggle is enabled', async () => { - grantUserRole(OrgRole.Admin); - grantUserPermissions([AccessControlAction.AlertingRuleRead]); + it('should show "Import alert rules" option when user has required permissions and feature toggle is enabled', async () => { + grantUserPermissions([ + AccessControlAction.AlertingRuleRead, + AccessControlAction.AlertingRuleCreate, + AccessControlAction.AlertingProvisioningSetStatus, + ]); const { user } = render(); @@ -267,8 +270,8 @@ describe('RuleListActions', () => { expect(ui.menuOptions.importAlertRules.query(menu)).toBeInTheDocument(); }); - it('should not show "Import alert rules" option when user is not admin', async () => { - // Keep default Viewer role + it('should not show "Import alert rules" option when user lacks required permissions', async () => { + // Keep default Viewer role and only read permissions grantUserPermissions([AccessControlAction.AlertingRuleRead]); const { user } = render(); @@ -280,8 +283,11 @@ describe('RuleListActions', () => { }); it('should have correct URL for "Import alert rules" menu item', async () => { - grantUserRole(OrgRole.Admin); - grantUserPermissions([AccessControlAction.AlertingRuleRead]); + grantUserPermissions([ + AccessControlAction.AlertingRuleRead, + AccessControlAction.AlertingRuleCreate, + AccessControlAction.AlertingProvisioningSetStatus, + ]); const { user } = render(); diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx index 7018bc3f9c9..e828b00fa16 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx @@ -4,6 +4,8 @@ import { useToggle } from 'react-use'; import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { Button, Dropdown, Icon, LinkButton, Menu, Stack } from '@grafana/ui'; +import { contextSrv } from 'app/core/services/context_srv'; +import { AccessControlAction } from 'app/types/accessControl'; import { AlertingPageWrapper } from '../components/AlertingPageWrapper'; import { GrafanaRulesExporter } from '../components/export/GrafanaRulesExporter'; @@ -11,7 +13,6 @@ import { useListViewMode } from '../components/rules/Filter/RulesViewModeSelecto import { AIAlertRuleButtonComponent } from '../enterprise-components/AI/AIGenAlertRuleButton/addAIAlertRuleButton'; import { AlertingAction, useAlertingAbility } from '../hooks/useAbilities'; import { useRulesFilter } from '../hooks/useFilteredRules'; -import { isAdmin } from '../utils/misc'; import { FilterView } from './FilterView'; import { GroupedView } from './GroupedView'; @@ -44,7 +45,11 @@ export function RuleListActions() { const canExportRules = exportRulesSupported && exportRulesAllowed; const canCreateRules = canCreateGrafanaRules || canCreateCloudRules; - const canImportRulesToGMA = isAdmin() && config.featureToggles.alertingMigrationUI; + // Align import UI permission with convert endpoint requirements: rule create + provisioning set status + const canImportRulesToGMA = + config.featureToggles.alertingMigrationUI && + contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate) && + contextSrv.hasPermission(AccessControlAction.AlertingProvisioningSetStatus); const [showExportDrawer, toggleShowExportDrawer] = useToggle(false); diff --git a/public/app/types/accessControl.ts b/public/app/types/accessControl.ts index 1f86e39ea34..7ef86382edc 100644 --- a/public/app/types/accessControl.ts +++ b/public/app/types/accessControl.ts @@ -129,6 +129,11 @@ export enum AccessControlAction { AlertingProvisioningReadSecrets = 'alert.provisioning.secrets:read', AlertingProvisioningRead = 'alert.provisioning:read', AlertingProvisioningWrite = 'alert.provisioning:write', + AlertingRulesProvisioningRead = 'alert.rules.provisioning:read', + AlertingRulesProvisioningWrite = 'alert.rules.provisioning:write', + AlertingNotificationsProvisioningRead = 'alert.notifications.provisioning:read', + AlertingNotificationsProvisioningWrite = 'alert.notifications.provisioning:write', + AlertingProvisioningSetStatus = 'alert.provisioning.provenance:write', // Alerting receivers actions AlertingReceiversPermissionsRead = 'receivers.permissions:read',