Alerting: Relax permissions for access a rule (#103664)
This makes it so that it is: - No longer required to have datasource permissions to delete a rule. - No longer required to have datasource permissions to update non-query related fields of a rule.
This commit is contained in:
@@ -145,23 +145,12 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceU
|
||||
}
|
||||
rulesToDelete := make([]string, 0)
|
||||
provisioned := false
|
||||
auth := true
|
||||
for groupKey, rules := range deletionCandidates {
|
||||
if containsProvisionedAlerts(provenances, rules) {
|
||||
logger.Debug("Alert group cannot be deleted because it is provisioned", "group", groupKey.RuleGroup)
|
||||
provisioned = true
|
||||
continue
|
||||
}
|
||||
// XXX: Currently delete requires data source query access to all rules in the group.
|
||||
if err := srv.authz.AuthorizeDatasourceAccessForRuleGroup(ctx, c.SignedInUser, rules); err != nil {
|
||||
if errors.Is(err, authz.ErrAuthorizationBase) {
|
||||
logger.Debug("User is not authorized to delete rules in the group", "group", groupKey.RuleGroup)
|
||||
auth = false
|
||||
continue
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
uid := make([]string, 0, len(rules))
|
||||
for _, rule := range rules {
|
||||
uid = append(uid, rule.UID)
|
||||
@@ -177,17 +166,10 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceU
|
||||
return nil
|
||||
}
|
||||
// if none rules were deleted return an error.
|
||||
|
||||
// Check whether provisioned check failed first because if it is true, then all rules that the user can access (actually read via GET API) are provisioned.
|
||||
if provisioned {
|
||||
return errProvisionedResource
|
||||
}
|
||||
|
||||
// If auth is false, then the user is not authorized to delete any of the rules.
|
||||
if !auth {
|
||||
return authz.NewAuthorizationErrorGeneric("delete any existing rules in the namespace")
|
||||
}
|
||||
|
||||
logger.Info("No alert rules were deleted")
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -81,6 +81,24 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
|
||||
t.Run("when fine-grained access is enabled", func(t *testing.T) {
|
||||
t.Run("and group argument is empty", func(t *testing.T) {
|
||||
t.Run("allow deleting without access to datasource", func(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
provisioningStore := fakes.NewFakeProvisioningStore()
|
||||
|
||||
folderGen := gen.With(gen.WithNamespace(folder.ToFolderReference()))
|
||||
|
||||
authorizedRulesInFolder := folderGen.With(gen.WithGroupPrefix("authz-")).GenerateManyRef(1, 5)
|
||||
|
||||
ruleStore.PutRule(context.Background(), authorizedRulesInFolder...)
|
||||
|
||||
permissions := createPermissionsForRulesWithoutDS(authorizedRulesInFolder, orgID)
|
||||
requestCtx := createRequestContextWithPerms(orgID, permissions, nil)
|
||||
|
||||
response := createServiceWithProvenanceStore(ruleStore, provisioningStore).RouteDeleteAlertRules(requestCtx, folder.UID, "")
|
||||
|
||||
require.Equalf(t, 202, response.Status(), "Expected 202 but got %d: %v", response.Status(), string(response.Body()))
|
||||
assertRulesDeleted(t, authorizedRulesInFolder, ruleStore)
|
||||
})
|
||||
t.Run("return Forbidden if user is not authorized to access any group in the folder", func(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
ruleStore.PutRule(context.Background(), gen.With(gen.WithNamespace(folder.ToFolderReference())).GenerateManyRef(1, 5)...)
|
||||
@@ -108,8 +126,6 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
|
||||
ruleStore.PutRule(context.Background(), authorizedRulesInFolder...)
|
||||
ruleStore.PutRule(context.Background(), provisionedRulesInFolder...)
|
||||
// more rules in the same namespace but user does not have access to them
|
||||
ruleStore.PutRule(context.Background(), folderGen.With(gen.WithGroupPrefix("unauthz")).GenerateManyRef(1, 5)...)
|
||||
|
||||
permissions := createPermissionsForRules(append(authorizedRulesInFolder, provisionedRulesInFolder...), orgID)
|
||||
requestCtx := createRequestContextWithPerms(orgID, permissions, nil)
|
||||
@@ -130,8 +146,6 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
ruleStore.PutRule(context.Background(), provisionedRulesInFolder...)
|
||||
// more rules in the same namespace but user does not have access to them
|
||||
ruleStore.PutRule(context.Background(), folderGen.With(gen.WithSameGroup()).GenerateManyRef(1, 5)...)
|
||||
|
||||
permissions := createPermissionsForRules(provisionedRulesInFolder, orgID)
|
||||
requestCtx := createRequestContextWithPerms(orgID, permissions, nil)
|
||||
@@ -159,10 +173,8 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
|
||||
authorizedRulesInGroup := groupGen.GenerateManyRef(1, 5)
|
||||
ruleStore.PutRule(context.Background(), authorizedRulesInGroup...)
|
||||
// more rules in the same group but user is not authorized to access them
|
||||
ruleStore.PutRule(context.Background(), groupGen.GenerateManyRef(1, 5)...)
|
||||
|
||||
permissions := createPermissionsForRules(authorizedRulesInGroup, orgID)
|
||||
permissions := createPermissionsForRules([]*models.AlertRule{}, orgID)
|
||||
requestCtx := createRequestContextWithPerms(orgID, permissions, nil)
|
||||
|
||||
response := createService(ruleStore, nil).RouteDeleteAlertRules(requestCtx, folder.UID, authorizedRulesInGroup[0].RuleGroup)
|
||||
@@ -1014,3 +1026,17 @@ func createPermissionsForRules(rules []*models.AlertRule, orgID int64) map[int64
|
||||
}
|
||||
return map[int64]map[string][]string{orgID: permissions}
|
||||
}
|
||||
|
||||
func createPermissionsForRulesWithoutDS(rules []*models.AlertRule, orgID int64) map[int64]map[string][]string {
|
||||
ns := map[string]any{}
|
||||
permissions := map[string][]string{}
|
||||
for _, rule := range rules {
|
||||
if _, ok := ns[rule.NamespaceUID]; !ok {
|
||||
scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(rule.NamespaceUID)
|
||||
permissions[dashboards.ActionFoldersRead] = append(permissions[dashboards.ActionFoldersRead], scope)
|
||||
permissions[ac.ActionAlertingRuleRead] = append(permissions[ac.ActionAlertingRuleRead], scope)
|
||||
ns[rule.NamespaceUID] = struct{}{}
|
||||
}
|
||||
}
|
||||
return map[int64]map[string][]string{orgID: permissions}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user