Skip to content

Commit

Permalink
Alerting: Add two sets of provisioning actions for rules and notifica…
Browse files Browse the repository at this point in the history
…tions (#87149)
  • Loading branch information
yuri-tceretian committed May 9, 2024
1 parent 36ef611 commit 356a295
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 11 deletions.
11 changes: 8 additions & 3 deletions pkg/services/accesscontrol/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,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"

Expand Down
18 changes: 18 additions & 0 deletions pkg/services/ngalert/accesscontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,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,
Expand All @@ -213,6 +225,12 @@ var (
{
Action: accesscontrol.ActionAlertingProvisioningRead, // organization scope
},
{
Action: accesscontrol.ActionAlertingRulesProvisioningRead, // organization scope
},
{
Action: accesscontrol.ActionAlertingNotificationsProvisioningRead, // organization scope
},
},
},
Grants: []string{string(org.RoleAdmin)},
Expand Down
26 changes: 21 additions & 5 deletions pkg/services/ngalert/api/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,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),
Expand All @@ -268,6 +270,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),
Expand All @@ -280,6 +283,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),
Expand All @@ -295,6 +299,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),
)
Expand All @@ -303,6 +308,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),
Expand All @@ -311,6 +317,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),
Expand All @@ -319,6 +326,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),
Expand All @@ -328,6 +336,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),
Expand All @@ -339,6 +348,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),
Expand All @@ -362,15 +372,21 @@ 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),
),
)
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 {
Expand Down
6 changes: 5 additions & 1 deletion pkg/services/ngalert/provisioning/accesscontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
))
}

Expand All @@ -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),
))
}
13 changes: 11 additions & 2 deletions pkg/services/ngalert/provisioning/accesscontrol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
})

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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])
})
Expand Down Expand Up @@ -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])
})

Expand Down

0 comments on commit 356a295

Please sign in to comment.