Alerting: Disallow invalid rule namespace UIDs in provisioning API (#83938)

* Disallow invalid rule namespace UIDs in provisioning

Reject requests with rules that reference a nonexistent folder or have an empty folder uid
This commit is contained in:
William Wernert
2024-03-14 09:58:25 -04:00
committed by GitHub
parent 8e90e02db2
commit 8690a42e33
9 changed files with 134 additions and 21 deletions
@@ -7,7 +7,9 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@@ -25,6 +27,7 @@ type AlertRuleService struct {
rulesPerRuleGroupLimit int64
ruleStore RuleStore
provenanceStore ProvisioningStore
folderService folder.Service
dashboardService dashboards.DashboardService
quotas QuotaChecker
xact TransactionManager
@@ -34,6 +37,7 @@ type AlertRuleService struct {
func NewAlertRuleService(ruleStore RuleStore,
provenanceStore ProvisioningStore,
folderService folder.Service,
dashboardService dashboards.DashboardService,
quotas QuotaChecker,
xact TransactionManager,
@@ -49,6 +53,7 @@ func NewAlertRuleService(ruleStore RuleStore,
rulesPerRuleGroupLimit: rulesPerRuleGroupLimit,
ruleStore: ruleStore,
provenanceStore: provenanceStore,
folderService: folderService,
dashboardService: dashboardService,
quotas: quotas,
xact: xact,
@@ -127,7 +132,7 @@ func (service *AlertRuleService) GetAlertRuleWithFolderTitle(ctx context.Context
// CreateAlertRule creates a new alert rule. This function will ignore any
// interval that is set in the rule struct and use the already existing group
// interval or the default one.
func (service *AlertRuleService) CreateAlertRule(ctx context.Context, rule models.AlertRule, provenance models.Provenance, userID int64) (models.AlertRule, error) {
func (service *AlertRuleService) CreateAlertRule(ctx context.Context, user identity.Requester, rule models.AlertRule, provenance models.Provenance) (models.AlertRule, error) {
if rule.UID == "" {
rule.UID = util.GenerateShortUID()
} else if err := util.ValidateUID(rule.UID); err != nil {
@@ -145,6 +150,9 @@ func (service *AlertRuleService) CreateAlertRule(ctx context.Context, rule model
if err != nil {
return models.AlertRule{}, err
}
if err = service.ensureRuleNamespace(ctx, user, rule); err != nil {
return models.AlertRule{}, err
}
rule.Updated = time.Now()
if len(rule.NotificationSettings) > 0 {
validator, err := service.nsValidatorProvider.Validator(ctx, rule.OrgID)
@@ -176,6 +184,11 @@ func (service *AlertRuleService) CreateAlertRule(ctx context.Context, rule model
return errors.New("couldn't find newly created id")
}
// default to 0 if there is no user
userID := int64(0)
if user != nil {
userID, _ = identity.UserIdentifier(user.GetNamespacedID())
}
if err = service.checkLimitsTransactionCtx(ctx, rule.OrgID, userID); err != nil {
return err
}
@@ -639,3 +652,32 @@ func (service *AlertRuleService) checkGroupLimits(group models.AlertRuleGroup) e
return nil
}
// ensureRuleNamespace ensures that the rule has a valid namespace UID.
// If the rule does not have a namespace UID or the namespace (folder) does not exist it will return an error.
func (service *AlertRuleService) ensureRuleNamespace(ctx context.Context, user identity.Requester, rule models.AlertRule) error {
if rule.NamespaceUID == "" {
return fmt.Errorf("%w: folderUID must be set", models.ErrAlertRuleFailedValidation)
}
if user == nil {
// user is nil when this is called during file provisioning,
// which already creates the folder if it does not exist
return nil
}
// ensure the namespace exists
_, err := service.folderService.Get(ctx, &folder.GetFolderQuery{
OrgID: rule.OrgID,
UID: &rule.NamespaceUID,
SignedInUser: user,
})
if err != nil {
if errors.Is(err, dashboards.ErrFolderNotFound) {
return fmt.Errorf("%w: folder does not exist", models.ErrAlertRuleFailedValidation)
}
return err
}
return nil
}