diff --git a/pkg/services/ngalert/accesscontrol/fakes/rules.go b/pkg/services/ngalert/accesscontrol/fakes/rules.go index f666419beca..52989e44eab 100644 --- a/pkg/services/ngalert/accesscontrol/fakes/rules.go +++ b/pkg/services/ngalert/accesscontrol/fakes/rules.go @@ -5,7 +5,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" ) @@ -22,8 +21,8 @@ type FakeRuleService struct { AuthorizeDatasourceAccessForRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error HasAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) (bool, error) AuthorizeAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error - HasAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) (bool, error) - AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) error + HasAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) (bool, error) + AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) error AuthorizeRuleChangesFunc func(context.Context, identity.Requester, *store.GroupDelta) error Calls []Call @@ -77,7 +76,7 @@ func (s *FakeRuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user i return nil } -func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) (bool, error) { +func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) (bool, error) { s.Calls = append(s.Calls, Call{"HasAccessInFolder", []interface{}{ctx, user, namespaced}}) if s.HasAccessInFolderFunc != nil { return s.HasAccessInFolderFunc(ctx, user, namespaced) @@ -85,7 +84,7 @@ func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.R return false, nil } -func (s *FakeRuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error { +func (s *FakeRuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error { s.Calls = append(s.Calls, Call{"AuthorizeAccessInFolder", []interface{}{ctx, user, namespaced}}) if s.AuthorizeAccessInFolderFunc != nil { return s.AuthorizeAccessInFolderFunc(ctx, user, namespaced) diff --git a/pkg/services/ngalert/accesscontrol/rules.go b/pkg/services/ngalert/accesscontrol/rules.go index 998b30f56a0..2b8e2fd2914 100644 --- a/pkg/services/ngalert/accesscontrol/rules.go +++ b/pkg/services/ngalert/accesscontrol/rules.go @@ -31,10 +31,6 @@ func NewRuleService(ac accesscontrol.AccessControl) *RuleService { } } -type Namespaced interface { - GetNamespaceUID() string -} - // getReadFolderAccessEvaluator constructs accesscontrol.Evaluator that checks all permissions required to read rules in specific folder func getReadFolderAccessEvaluator(folderUID string) accesscontrol.Evaluator { return accesscontrol.EvalAll( @@ -130,7 +126,7 @@ func (r *RuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user ident // - ("folders:read") read the folder // - ("alert.rules:read") read alert rules in the folder // Returns false if the requester does not have enough permissions, and error if something went wrong during the permission evaluation. -func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, rule Namespaced) (bool, error) { +func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) (bool, error) { eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID())) return r.HasAccess(ctx, user, eval) } @@ -140,7 +136,7 @@ func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Reque // - ("folders:read") read the folder // - ("alert.rules:read") read alert rules in the folder // Returns error if at least one permission is missing or if something went wrong during the permission evaluation -func (r *RuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, rule Namespaced) error { +func (r *RuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) error { eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID())) return r.HasAccessOrError(ctx, user, eval, func() string { return fmt.Sprintf("access rules in folder '%s'", rule.GetNamespaceUID()) diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index df99909b9b4..1b40ec4a2d9 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -45,7 +45,7 @@ type RuleAccessControlService interface { AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error AuthorizeDatasourceAccessForRule(ctx context.Context, user identity.Requester, rule *models.AlertRule) error AuthorizeDatasourceAccessForRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error - AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error + AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error } // API handlers. diff --git a/pkg/services/ngalert/api/testing.go b/pkg/services/ngalert/api/testing.go index 6cb35aa101e..3a84fbd6952 100644 --- a/pkg/services/ngalert/api/testing.go +++ b/pkg/services/ngalert/api/testing.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/state" @@ -147,7 +146,7 @@ func (f fakeRuleAccessControlService) AuthorizeAccessToRuleGroup(ctx context.Con return nil } -func (f fakeRuleAccessControlService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error { +func (f fakeRuleAccessControlService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error { return nil } diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index b505bf87ca6..b379810b579 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -269,6 +269,11 @@ type AlertRule struct { NotificationSettings []NotificationSettings `xorm:"notification_settings"` // we use slice to workaround xorm mapping that does not serialize a struct to JSON unless it's a slice } +// Namespaced describes a class of resources that are stored in a specific namespace. +type Namespaced interface { + GetNamespaceUID() string +} + // AlertRuleWithOptionals This is to avoid having to pass in additional arguments deep in the call stack. Alert rule // object is created in an early validation step without knowledge about current alert rule fields or if they need to be // overridden. This is done in a later step and, in that step, we did not have knowledge about if a field was optional diff --git a/pkg/services/ngalert/notifier/silence_svc.go b/pkg/services/ngalert/notifier/silence_svc.go index dd40f11015e..aab960a4bcd 100644 --- a/pkg/services/ngalert/notifier/silence_svc.go +++ b/pkg/services/ngalert/notifier/silence_svc.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" ) @@ -24,7 +23,7 @@ type SilenceService struct { } type RuleAccessControlService interface { - HasAccessInFolder(ctx context.Context, user identity.Requester, rule accesscontrol.Namespaced) (bool, error) + HasAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) (bool, error) } // SilenceAccessControlService provides access control for silences. diff --git a/pkg/services/ngalert/notifier/silence_svc_test.go b/pkg/services/ngalert/notifier/silence_svc_test.go index 5bfcf7d05cd..6e81f98bcbc 100644 --- a/pkg/services/ngalert/notifier/silence_svc_test.go +++ b/pkg/services/ngalert/notifier/silence_svc_test.go @@ -15,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" @@ -61,7 +60,7 @@ func TestWithRuleMetadata(t *testing.T) { user := ac.BackgroundUser("test", 1, org.RoleNone, nil) t.Run("Attach rule metadata to silences", func(t *testing.T) { ruleAuthz := fakes.FakeRuleService{} - ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) { + ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) { return true, nil } @@ -95,7 +94,7 @@ func TestWithRuleMetadata(t *testing.T) { }) t.Run("Don't attach full rule metadata if no access or global", func(t *testing.T) { ruleAuthz := fakes.FakeRuleService{} - ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) { + ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) { return silence.GetNamespaceUID() == "folder1", nil } @@ -134,7 +133,7 @@ func TestWithRuleMetadata(t *testing.T) { }) t.Run("Don't check same namespace access more than once", func(t *testing.T) { ruleAuthz := fakes.FakeRuleService{} - ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) { + ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) { return true, nil } @@ -159,7 +158,7 @@ func TestWithRuleMetadata(t *testing.T) { require.NoError(t, svc.WithRuleMetadata(context.Background(), user, silencesWithMetadata...)) assert.Lenf(t, ruleAuthz.Calls, 1, "HasAccessInFolder should be called only once per namespace") assert.Equal(t, "HasAccessInFolder", ruleAuthz.Calls[0].MethodName) - assert.Equal(t, "folder1", ruleAuthz.Calls[0].Arguments[2].(accesscontrol.Namespaced).GetNamespaceUID()) + assert.Equal(t, "folder1", ruleAuthz.Calls[0].Arguments[2].(models.Namespaced).GetNamespaceUID()) }) } diff --git a/pkg/services/ngalert/provisioning/accesscontrol.go b/pkg/services/ngalert/provisioning/accesscontrol.go index 8e503efadc1..97ae4cc6294 100644 --- a/pkg/services/ngalert/provisioning/accesscontrol.go +++ b/pkg/services/ngalert/provisioning/accesscontrol.go @@ -5,7 +5,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" ) @@ -13,7 +12,7 @@ import ( type RuleAccessControlService interface { HasAccess(ctx context.Context, user identity.Requester, evaluator ac.Evaluator) (bool, error) AuthorizeAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error - AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error + AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error } diff --git a/pkg/services/ngalert/provisioning/accesscontrol_test.go b/pkg/services/ngalert/provisioning/accesscontrol_test.go index 3f771ddfa3f..8df47a15634 100644 --- a/pkg/services/ngalert/provisioning/accesscontrol_test.go +++ b/pkg/services/ngalert/provisioning/accesscontrol_test.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/accesscontrol" - accesscontrol2 "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" @@ -200,7 +199,7 @@ func TestAuthorizeAccessToRule(t *testing.T) { rs.HasAccessFunc = func(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) { return false, nil } - rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, namespaced accesscontrol2.Namespaced) error { + rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, namespaced models.Namespaced) error { return nil } @@ -231,7 +230,7 @@ func TestAuthorizeAccessToRule(t *testing.T) { return false, nil } expected = errors.New("test2") - rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, rule accesscontrol2.Namespaced) error { + rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, rule models.Namespaced) error { return expected } diff --git a/pkg/services/ngalert/provisioning/alert_rules_test.go b/pkg/services/ngalert/provisioning/alert_rules_test.go index 1fca30486fd..8786acc63db 100644 --- a/pkg/services/ngalert/provisioning/alert_rules_test.go +++ b/pkg/services/ngalert/provisioning/alert_rules_test.go @@ -1020,7 +1020,7 @@ func TestGetAlertRule(t *testing.T) { service, _, _, ac := initServiceWithData(t) expected := errors.New("test") - ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error { + ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error { assert.Equal(t, u, user) assert.EqualValues(t, rule, namespaced) return expected @@ -1034,7 +1034,7 @@ func TestGetAlertRule(t *testing.T) { assert.Equal(t, "AuthorizeRuleRead", ac.Calls[0].Method) ac.Calls = nil - ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error { + ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error { return nil } diff --git a/pkg/services/ngalert/provisioning/testing.go b/pkg/services/ngalert/provisioning/testing.go index a61bdcc00c0..13422e79b50 100644 --- a/pkg/services/ngalert/provisioning/testing.go +++ b/pkg/services/ngalert/provisioning/testing.go @@ -9,7 +9,6 @@ import ( mock "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier" "github.com/grafana/grafana/pkg/services/ngalert/store" @@ -161,7 +160,7 @@ type fakeRuleAccessControlService struct { mu sync.Mutex Calls []call AuthorizeAccessToRuleGroupFunc func(ctx context.Context, user identity.Requester, rules models.RulesGroup) error - AuthorizeAccessInFolderFunc func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error + AuthorizeAccessInFolderFunc func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error AuthorizeRuleChangesFunc func(ctx context.Context, user identity.Requester, change *store.GroupDelta) error CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error) CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)