From 0a2f2397441867282806cd94cf0c23fc3dcad297 Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 14:24:59 -0400 Subject: [PATCH] [v11.0.x] Alerting: Add two sets of provisioning actions for rules and notifications (#87572) Alerting: Add two sets of provisioning actions for rules and notifications (#87149) (cherry picked from commit 356a29592be6c5fe5de81138f6a55e03a8cdfac2) Co-authored-by: Yuri Tseretyan --- pkg/services/accesscontrol/models.go | 11 +++++--- pkg/services/ngalert/accesscontrol.go | 18 +++++++++++++ pkg/services/ngalert/api/authorization.go | 26 +++++++++++++++---- .../ngalert/provisioning/accesscontrol.go | 6 ++++- .../provisioning/accesscontrol_test.go | 13 ++++++++-- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index d0f5aeb54d9..e942621fe1a 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -466,9 +466,14 @@ const ( ActionAlertingNotificationsExternalRead = "alert.notifications.external:read" // Alerting provisioning actions - ActionAlertingProvisioningRead = "alert.provisioning:read" - ActionAlertingProvisioningReadSecrets = "alert.provisioning.secrets:read" - ActionAlertingProvisioningWrite = "alert.provisioning:write" + ActionAlertingProvisioningRead = "alert.provisioning:read" + ActionAlertingProvisioningReadSecrets = "alert.provisioning.secrets:read" + ActionAlertingProvisioningWrite = "alert.provisioning:write" + ActionAlertingRulesProvisioningRead = "alert.rules.provisioning:read" + ActionAlertingRulesProvisioningWrite = "alert.rules.provisioning:write" + ActionAlertingNotificationsProvisioningRead = "alert.notifications.provisioning:read" + ActionAlertingNotificationsProvisioningWrite = "alert.notifications.provisioning:write" + // ActionAlertingProvisioningSetStatus Gives access to set provisioning status to alerting resources. Cannot be used alone. Only in conjunction with other permissions. ActionAlertingProvisioningSetStatus = "alert.provisioning.provenance:write" diff --git a/pkg/services/ngalert/accesscontrol.go b/pkg/services/ngalert/accesscontrol.go index 3c8d21eaee4..013bf13a406 100644 --- a/pkg/services/ngalert/accesscontrol.go +++ b/pkg/services/ngalert/accesscontrol.go @@ -179,6 +179,18 @@ var ( { Action: accesscontrol.ActionAlertingProvisioningWrite, // organization scope }, + { + Action: accesscontrol.ActionAlertingRulesProvisioningRead, // organization scope + }, + { + Action: accesscontrol.ActionAlertingRulesProvisioningWrite, // organization scope + }, + { + Action: accesscontrol.ActionAlertingNotificationsProvisioningRead, // organization scope + }, + { + Action: accesscontrol.ActionAlertingNotificationsProvisioningWrite, // organization scope + }, { Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll, @@ -201,6 +213,12 @@ var ( { Action: accesscontrol.ActionAlertingProvisioningRead, // organization scope }, + { + Action: accesscontrol.ActionAlertingRulesProvisioningRead, // organization scope + }, + { + Action: accesscontrol.ActionAlertingNotificationsProvisioningRead, // organization scope + }, }, }, Grants: []string{string(org.RoleAdmin)}, diff --git a/pkg/services/ngalert/api/authorization.go b/pkg/services/ngalert/api/authorization.go index b97b0c9599d..80565745d26 100644 --- a/pkg/services/ngalert/api/authorization.go +++ b/pkg/services/ngalert/api/authorization.go @@ -218,15 +218,17 @@ func (api *API) authorize(method, path string) web.Handler { http.MethodGet + "/api/v1/provisioning/mute-timings/export", http.MethodGet + "/api/v1/provisioning/mute-timings/{name}/export": eval = ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingNotificationsRead), // organization scope - ac.EvalPermission(ac.ActionAlertingProvisioningRead), // organization scope - ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), // organization scope + ac.EvalPermission(ac.ActionAlertingNotificationsRead), // organization scope + ac.EvalPermission(ac.ActionAlertingProvisioningRead), // organization scope + ac.EvalPermission(ac.ActionAlertingNotificationsProvisioningRead), // organization scope + ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), // organization scope ) case http.MethodGet + "/api/v1/provisioning/alert-rules", http.MethodGet + "/api/v1/provisioning/alert-rules/export": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningRead), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningRead), ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), ac.EvalAll( // scopes are enforced in the handler ac.EvalPermission(ac.ActionAlertingRuleRead), @@ -237,6 +239,7 @@ func (api *API) authorize(method, path string) web.Handler { http.MethodGet + "/api/v1/provisioning/alert-rules/{UID}/export": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningRead), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningRead), ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleRead), @@ -249,6 +252,7 @@ func (api *API) authorize(method, path string) web.Handler { scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(ac.Parameter(":FolderUID")) eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningRead), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningRead), ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleRead, scope), @@ -264,6 +268,7 @@ func (api *API) authorize(method, path string) web.Handler { http.MethodGet + "/api/v1/provisioning/mute-timings/{name}": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningRead), + ac.EvalPermission(ac.ActionAlertingNotificationsProvisioningRead), // organization scope ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), ac.EvalPermission(ac.ActionAlertingNotificationsRead), ) @@ -272,6 +277,7 @@ func (api *API) authorize(method, path string) web.Handler { case http.MethodPost + "/api/v1/provisioning/alert-rules": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleCreate), // more granular permissions are enforced by the handler via "authorizeRuleChanges" ac.EvalPermission(ac.ActionAlertingProvisioningSetStatus), @@ -280,6 +286,7 @@ func (api *API) authorize(method, path string) web.Handler { case http.MethodPut + "/api/v1/provisioning/alert-rules/{UID}": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleUpdate), // more granular permissions are enforced by the handler via "authorizeRuleChanges" ac.EvalPermission(ac.ActionAlertingProvisioningSetStatus), @@ -288,6 +295,7 @@ func (api *API) authorize(method, path string) web.Handler { case http.MethodDelete + "/api/v1/provisioning/alert-rules/{UID}": eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleDelete), // more granular permissions are enforced by the handler via "authorizeRuleChanges" ac.EvalPermission(ac.ActionAlertingProvisioningSetStatus), @@ -297,6 +305,7 @@ func (api *API) authorize(method, path string) web.Handler { scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(ac.Parameter(":FolderUID")) eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleDelete, scope), ac.EvalPermission(ac.ActionAlertingRuleRead, scope), @@ -308,6 +317,7 @@ func (api *API) authorize(method, path string) web.Handler { scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(ac.Parameter(":FolderUID")) eval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleRead, scope), ac.EvalPermission(dashboards.ActionFoldersRead, scope), @@ -331,7 +341,8 @@ func (api *API) authorize(method, path string) web.Handler { http.MethodPut + "/api/v1/provisioning/mute-timings/{name}", http.MethodDelete + "/api/v1/provisioning/mute-timings/{name}": eval = ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingProvisioningWrite), // organization scope, + ac.EvalPermission(ac.ActionAlertingProvisioningWrite), // organization scope, + ac.EvalPermission(ac.ActionAlertingNotificationsProvisioningWrite), // organization scope ac.EvalAll( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), ac.EvalPermission(ac.ActionAlertingProvisioningSetStatus), @@ -339,7 +350,12 @@ func (api *API) authorize(method, path string) web.Handler { ) case http.MethodGet + "/api/v1/notifications/time-intervals/{name}", http.MethodGet + "/api/v1/notifications/time-intervals": - eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsRead), ac.EvalPermission(ac.ActionAlertingNotificationsTimeIntervalsRead), ac.EvalPermission(ac.ActionAlertingProvisioningRead)) + eval = ac.EvalAny( + ac.EvalPermission(ac.ActionAlertingNotificationsRead), + ac.EvalPermission(ac.ActionAlertingNotificationsTimeIntervalsRead), + ac.EvalPermission(ac.ActionAlertingProvisioningRead), + ac.EvalPermission(ac.ActionAlertingNotificationsProvisioningRead), // organization scope + ) } if eval != nil { diff --git a/pkg/services/ngalert/provisioning/accesscontrol.go b/pkg/services/ngalert/provisioning/accesscontrol.go index 91567e6b1f3..33305fec79a 100644 --- a/pkg/services/ngalert/provisioning/accesscontrol.go +++ b/pkg/services/ngalert/provisioning/accesscontrol.go @@ -64,6 +64,7 @@ func (p *provisioningRuleAccessControl) CanReadAllRules(ctx context.Context, use return p.HasAccess(ctx, user, ac.EvalAny( ac.EvalPermission(ac.ActionAlertingProvisioningRead), ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningRead), )) } @@ -72,5 +73,8 @@ func (p *provisioningRuleAccessControl) CanReadAllRules(ctx context.Context, use // It returns true if the user has permission, false otherwise. // It returns an error if there is a problem checking the permission. func (p *provisioningRuleAccessControl) CanWriteAllRules(ctx context.Context, user identity.Requester) (bool, error) { - return p.HasAccess(ctx, user, ac.EvalPermission(ac.ActionAlertingProvisioningWrite)) + return p.HasAccess(ctx, user, ac.EvalAny( + ac.EvalPermission(ac.ActionAlertingProvisioningWrite), + ac.EvalPermission(ac.ActionAlertingRulesProvisioningWrite), + )) } diff --git a/pkg/services/ngalert/provisioning/accesscontrol_test.go b/pkg/services/ngalert/provisioning/accesscontrol_test.go index 04d9000c0ec..4cce8dbb959 100644 --- a/pkg/services/ngalert/provisioning/accesscontrol_test.go +++ b/pkg/services/ngalert/provisioning/accesscontrol_test.go @@ -36,6 +36,7 @@ func TestCanReadAllRules(t *testing.T) { require.Equal(t, accesscontrol.EvalAny( accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningRead), accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningReadSecrets), + accesscontrol.EvalPermission(accesscontrol.ActionAlertingRulesProvisioningRead), ).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) }) @@ -67,7 +68,11 @@ func TestCanWriteAllRules(t *testing.T) { require.Len(t, rs.Calls, 1) require.Equal(t, "HasAccess", rs.Calls[0].MethodName) - require.Equal(t, accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningWrite).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) + require.Equal(t, + accesscontrol.EvalAny( + accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningWrite), + accesscontrol.EvalPermission(accesscontrol.ActionAlertingRulesProvisioningWrite), + ).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) }) t.Run("should return error", func(t *testing.T) { @@ -104,6 +109,7 @@ func TestAuthorizeAccessToRuleGroup(t *testing.T) { assert.Equal(t, accesscontrol.EvalAny( accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningRead), accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningReadSecrets), + accesscontrol.EvalPermission(accesscontrol.ActionAlertingRulesProvisioningRead), ).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) assert.Equal(t, testUser, rs.Calls[0].Arguments[1]) }) @@ -176,7 +182,10 @@ func TestAuthorizeRuleChanges(t *testing.T) { require.Len(t, rs.Calls, 1) require.Equal(t, "HasAccess", rs.Calls[0].MethodName) - assert.Equal(t, accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningWrite).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) + assert.Equal(t, accesscontrol.EvalAny( + accesscontrol.EvalPermission(accesscontrol.ActionAlertingProvisioningWrite), + accesscontrol.EvalPermission(accesscontrol.ActionAlertingRulesProvisioningWrite), + ).GoString(), rs.Calls[0].Arguments[2].(accesscontrol.Evaluator).GoString()) assert.Equal(t, testUser, rs.Calls[0].Arguments[1]) })