diff --git a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md index 641d9bb4180..5fb3705232e 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md @@ -238,6 +238,7 @@ For more information on Cloud Access Policies and how to use them, see [Access p | `alert.notifications.templates:read` | None | Read templates. | | `alert.notifications.templates:write` | None | Create new or update existing templates. | | `alert.notifications.templates:delete` | None | Delete existing templates. | +| `alert.notifications.templates.test:write` | None | Test templates with custom payloads (preview and payload editor functionality). | | `alert.notifications.routes:read` | None | Read notification policies. | | `alert.notifications.routes:write` | None | Create new, update or delete notification policies | diff --git a/docs/sources/alerting/set-up/configure-rbac/_index.md b/docs/sources/alerting/set-up/configure-rbac/_index.md index 33fb9e57dcb..9c591e7361d 100644 --- a/docs/sources/alerting/set-up/configure-rbac/_index.md +++ b/docs/sources/alerting/set-up/configure-rbac/_index.md @@ -64,6 +64,7 @@ Grafana Alerting has the following permissions. | `alert.notifications.templates:read` | n/a | Read templates. | | `alert.notifications.templates:write` | n/a | Create new or update existing templates. | | `alert.notifications.templates:delete` | n/a | Delete existing templates. | +| `alert.notifications.templates.test:write` | n/a | Test templates with custom payloads (preview and payload editor functionality). | | `alert.notifications.routes:read` | n/a | Read notification policies. | | `alert.notifications.routes:write` | n/a | Create new, update and update notification policies. | diff --git a/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md b/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md index 668e84c41cb..be1489eb1c4 100644 --- a/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md +++ b/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md @@ -60,7 +60,7 @@ Details of the fixed roles and the access they provide for Grafana Alerting are | Contact Point Creator: `fixed:alerting.receivers:creator` | `alert.notifications.receivers:create` | Create a new contact point. The user is automatically granted full access to the created contact point. | | Contact Point Writer: `fixed:alerting.receivers:writer` | `alert.notifications.receivers:read`, `alert.notifications.receivers:write`, `alert.notifications.receivers:delete` for scope `receivers:*` and
`alert.notifications.receivers:create` | Create a new contact point and manage all existing contact points. | | Templates Reader: `fixed:alerting.templates:reader` | `alert.notifications.templates:read` | Read all notification templates. | -| Templates Writer: `fixed:alerting.templates:writer` | `alert.notifications.templates:read`, `alert.notifications.templates:write`, `alert.notifications.templates:delete` | Create new and manage existing notification templates. | +| Templates Writer: `fixed:alerting.templates:writer` | `alert.notifications.templates:read`, `alert.notifications.templates:write`, `alert.notifications.templates:delete`, `alert.notifications.templates.test:write` | Create new and manage existing notification templates. Test templates with custom payloads. | | Time Intervals Reader: `fixed:alerting.time-intervals:reader` | `alert.notifications.time-intervals:read` | Read all time intervals. | | Time Intervals Writer: `fixed:alerting.time-intervals:writer` | `alert.notifications.time-intervals:read`, `alert.notifications.time-intervals:write`, `alert.notifications.time-intervals:delete` | Create new and manage existing time intervals. | | Notification Policies Reader: `fixed:alerting.routes:reader` | `alert.notifications.routes:read` | Read all time intervals. | diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index dfda78ac9f0..ec67cd03c64 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -447,6 +447,7 @@ const ( ActionAlertingNotificationsTemplatesRead = "alert.notifications.templates:read" ActionAlertingNotificationsTemplatesWrite = "alert.notifications.templates:write" ActionAlertingNotificationsTemplatesDelete = "alert.notifications.templates:delete" + ActionAlertingNotificationsTemplatesTest = "alert.notifications.templates.test:write" // Alerting notifications time interval actions ActionAlertingNotificationsTimeIntervalsRead = "alert.notifications.time-intervals:read" diff --git a/pkg/services/ngalert/accesscontrol.go b/pkg/services/ngalert/accesscontrol.go index e36c123c621..15e3212de03 100644 --- a/pkg/services/ngalert/accesscontrol.go +++ b/pkg/services/ngalert/accesscontrol.go @@ -173,6 +173,7 @@ var ( Permissions: accesscontrol.ConcatPermissions(templatesReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTemplatesWrite}, {Action: accesscontrol.ActionAlertingNotificationsTemplatesDelete}, + {Action: accesscontrol.ActionAlertingNotificationsTemplatesTest}, }), }, } diff --git a/pkg/services/ngalert/api/authorization.go b/pkg/services/ngalert/api/authorization.go index 46168688ee6..1c107db22c6 100644 --- a/pkg/services/ngalert/api/authorization.go +++ b/pkg/services/ngalert/api/authorization.go @@ -251,7 +251,7 @@ func (api *API) authorize(method, path string) web.Handler { case http.MethodPost + "/api/alertmanager/grafana/config/api/v1/templates/test": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesRead), + ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesTest), ) // External Alertmanager Paths diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx index bb7708a5226..5a50ab55cd4 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx @@ -26,8 +26,10 @@ import { useStyles2, } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; +import { contextSrv } from 'app/core/services/context_srv'; import { ActiveTab as ContactPointsActiveTabs } from 'app/features/alerting/unified/components/contact-points/ContactPoints'; import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types'; +import { AccessControlAction } from 'app/types/accessControl'; import { AITemplateButtonComponent } from '../../enterprise-components/AI/AIGenTemplateButton/addAITemplateButton'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; @@ -100,6 +102,16 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) const formRef = useRef(null); const isGrafanaAlertManager = alertmanager === GRAFANA_RULES_SOURCE_NAME; + // Check if user has permission to test templates + const canTestTemplates = + contextSrv.hasPermission(AccessControlAction.AlertingNotificationsTemplatesTest) || + contextSrv.hasPermission(AccessControlAction.AlertingNotificationsWrite); + + // Only show preview and payload panels if both conditions are met: + // 1. It's a Grafana Alertmanager + // 2. User has the test permission + const showPreviewAndPayload = isGrafanaAlertManager && canTestTemplates; + const error = updateTemplateError ?? createTemplateError; const [cheatsheetOpened, toggleCheatsheetOpened] = useToggle(false); @@ -118,16 +130,16 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) // splitter for template and payload editor const columnSplitter = useSplitter({ direction: 'column', - // if Grafana Alertmanager, split 50/50, otherwise 100/0 because there is no payload editor - initialSize: isGrafanaAlertManager ? 0.5 : 1, + // if showing preview/payload panels, split 50/50, otherwise 100/0 because there is no payload editor + initialSize: showPreviewAndPayload ? 0.5 : 1, dragPosition: 'middle', }); // splitter for template editor and preview const rowSplitter = useSplitter({ direction: 'row', - // if Grafana Alertmanager, split 60/40, otherwise 100/0 because there is no preview - initialSize: isGrafanaAlertManager ? 0.6 : 1, + // if showing preview/payload panels, split 60/40, otherwise 100/0 because there is no preview + initialSize: showPreviewAndPayload ? 0.6 : 1, dragPosition: 'middle', }); @@ -319,8 +331,8 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) - {/* payload editor – only available for Grafana Alertmanager */} - {isGrafanaAlertManager && ( + {/* payload editor – only shown if user has test permission */} + {showPreviewAndPayload && ( <>
@@ -345,8 +357,8 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) )}
- {/* preview column – full height and half-width */} - {isGrafanaAlertManager && ( + {/* preview column – only shown if user has test permission */} + {showPreviewAndPayload && (