Alerting: Move interface Namespaced from accesscontrol to models package (#89439)

move Namespaced interface from accesscontrol to models
This commit is contained in:
Yuri Tseretyan 2024-06-19 16:18:33 -04:00 committed by GitHub
parent 5d328983a1
commit 92f10b73a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 24 additions and 30 deletions

View File

@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" 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/models"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
) )
@ -22,8 +21,8 @@ type FakeRuleService struct {
AuthorizeDatasourceAccessForRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error AuthorizeDatasourceAccessForRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error
HasAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) (bool, error) HasAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) (bool, error)
AuthorizeAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error AuthorizeAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error
HasAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) (bool, error) HasAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) (bool, error)
AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) error AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) error
AuthorizeRuleChangesFunc func(context.Context, identity.Requester, *store.GroupDelta) error AuthorizeRuleChangesFunc func(context.Context, identity.Requester, *store.GroupDelta) error
Calls []Call Calls []Call
@ -77,7 +76,7 @@ func (s *FakeRuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user i
return nil 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}}) s.Calls = append(s.Calls, Call{"HasAccessInFolder", []interface{}{ctx, user, namespaced}})
if s.HasAccessInFolderFunc != nil { if s.HasAccessInFolderFunc != nil {
return s.HasAccessInFolderFunc(ctx, user, namespaced) return s.HasAccessInFolderFunc(ctx, user, namespaced)
@ -85,7 +84,7 @@ func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.R
return false, nil 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}}) s.Calls = append(s.Calls, Call{"AuthorizeAccessInFolder", []interface{}{ctx, user, namespaced}})
if s.AuthorizeAccessInFolderFunc != nil { if s.AuthorizeAccessInFolderFunc != nil {
return s.AuthorizeAccessInFolderFunc(ctx, user, namespaced) return s.AuthorizeAccessInFolderFunc(ctx, user, namespaced)

View File

@ -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 // getReadFolderAccessEvaluator constructs accesscontrol.Evaluator that checks all permissions required to read rules in specific folder
func getReadFolderAccessEvaluator(folderUID string) accesscontrol.Evaluator { func getReadFolderAccessEvaluator(folderUID string) accesscontrol.Evaluator {
return accesscontrol.EvalAll( return accesscontrol.EvalAll(
@ -130,7 +126,7 @@ func (r *RuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user ident
// - ("folders:read") read the folder // - ("folders:read") read the folder
// - ("alert.rules:read") read alert rules in 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. // 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())) eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID()))
return r.HasAccess(ctx, user, eval) 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 // - ("folders:read") read the folder
// - ("alert.rules:read") read alert rules in 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 // 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())) eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID()))
return r.HasAccessOrError(ctx, user, eval, func() string { return r.HasAccessOrError(ctx, user, eval, func() string {
return fmt.Sprintf("access rules in folder '%s'", rule.GetNamespaceUID()) return fmt.Sprintf("access rules in folder '%s'", rule.GetNamespaceUID())

View File

@ -45,7 +45,7 @@ type RuleAccessControlService interface {
AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
AuthorizeDatasourceAccessForRule(ctx context.Context, user identity.Requester, rule *models.AlertRule) error AuthorizeDatasourceAccessForRule(ctx context.Context, user identity.Requester, rule *models.AlertRule) error
AuthorizeDatasourceAccessForRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) 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. // API handlers.

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" 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/eval"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/state"
@ -147,7 +146,7 @@ func (f fakeRuleAccessControlService) AuthorizeAccessToRuleGroup(ctx context.Con
return nil 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 return nil
} }

View File

@ -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 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 // 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 // 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 // overridden. This is done in a later step and, in that step, we did not have knowledge about if a field was optional

View File

@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
) )
@ -24,7 +23,7 @@ type SilenceService struct {
} }
type RuleAccessControlService interface { 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. // SilenceAccessControlService provides access control for silences.

View File

@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" 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/accesscontrol/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -61,7 +60,7 @@ func TestWithRuleMetadata(t *testing.T) {
user := ac.BackgroundUser("test", 1, org.RoleNone, nil) user := ac.BackgroundUser("test", 1, org.RoleNone, nil)
t.Run("Attach rule metadata to silences", func(t *testing.T) { t.Run("Attach rule metadata to silences", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{} 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 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) { t.Run("Don't attach full rule metadata if no access or global", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{} 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 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) { t.Run("Don't check same namespace access more than once", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{} 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 return true, nil
} }
@ -159,7 +158,7 @@ func TestWithRuleMetadata(t *testing.T) {
require.NoError(t, svc.WithRuleMetadata(context.Background(), user, silencesWithMetadata...)) require.NoError(t, svc.WithRuleMetadata(context.Background(), user, silencesWithMetadata...))
assert.Lenf(t, ruleAuthz.Calls, 1, "HasAccessInFolder should be called only once per namespace") 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, "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())
}) })
} }

View File

@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" 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/models"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
) )
@ -13,7 +12,7 @@ import (
type RuleAccessControlService interface { type RuleAccessControlService interface {
HasAccess(ctx context.Context, user identity.Requester, evaluator ac.Evaluator) (bool, error) HasAccess(ctx context.Context, user identity.Requester, evaluator ac.Evaluator) (bool, error)
AuthorizeAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) 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 AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
} }

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/accesscontrol" "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/accesscontrol/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store" "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) { rs.HasAccessFunc = func(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
return false, nil 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 return nil
} }
@ -231,7 +230,7 @@ func TestAuthorizeAccessToRule(t *testing.T) {
return false, nil return false, nil
} }
expected = errors.New("test2") 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 return expected
} }

View File

@ -1020,7 +1020,7 @@ func TestGetAlertRule(t *testing.T) {
service, _, _, ac := initServiceWithData(t) service, _, _, ac := initServiceWithData(t)
expected := errors.New("test") 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.Equal(t, u, user)
assert.EqualValues(t, rule, namespaced) assert.EqualValues(t, rule, namespaced)
return expected return expected
@ -1034,7 +1034,7 @@ func TestGetAlertRule(t *testing.T) {
assert.Equal(t, "AuthorizeRuleRead", ac.Calls[0].Method) assert.Equal(t, "AuthorizeRuleRead", ac.Calls[0].Method)
ac.Calls = nil 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 return nil
} }

View File

@ -9,7 +9,6 @@ import (
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/apimachinery/identity" "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/models"
"github.com/grafana/grafana/pkg/services/ngalert/notifier" "github.com/grafana/grafana/pkg/services/ngalert/notifier"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
@ -161,7 +160,7 @@ type fakeRuleAccessControlService struct {
mu sync.Mutex mu sync.Mutex
Calls []call Calls []call
AuthorizeAccessToRuleGroupFunc func(ctx context.Context, user identity.Requester, rules models.RulesGroup) error 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 AuthorizeRuleChangesFunc func(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error) CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)
CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error) CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)