From acbbfde256304c516f0fcd55f36cdf710f0c22a7 Mon Sep 17 00:00:00 2001 From: Ieva Date: Wed, 8 Oct 2025 13:37:12 +0100 Subject: [PATCH] AuthZ service: Expand the logic to also evaluate action sets (#112124) * expand AuthZ service logic to also evaluate action sets * handle folder creation * fix test * simplify mapper code Co-authored-by: gamab * more accurate variable name Co-authored-by: gamab * break alerting import cycle * Apply suggestion from @gamab --------- Co-authored-by: gamab Co-authored-by: Gabriel MABILLE --- .../ossaccesscontrol/receivers.go | 29 +++--- pkg/services/authz/rbac/mapper.go | 69 +++++++++++++- pkg/services/authz/rbac/models.go | 2 + pkg/services/authz/rbac/service.go | 28 +++--- pkg/services/authz/rbac/service_test.go | 91 ++++++++++++++++++- .../snapshot_mgmt_alerts_test.go | 3 +- pkg/services/ngalert/accesscontrol.go | 58 ++++++------ .../ngalert/accesscontrol/receivers.go | 66 +++----------- .../ngalert/accesscontrol/receivers_test.go | 84 ++++++++--------- .../ngalert/accesscontrol/rules_test.go | 2 +- pkg/services/ngalert/models/accesscontrol.go | 44 +++++++++ .../ngalert/notifier/receiver_svc_test.go | 74 +++++++-------- .../notifications/receivers/receiver_test.go | 23 +++-- 13 files changed, 359 insertions(+), 214 deletions(-) create mode 100644 pkg/services/ngalert/models/accesscontrol.go diff --git a/pkg/services/accesscontrol/ossaccesscontrol/receivers.go b/pkg/services/accesscontrol/ossaccesscontrol/receivers.go index 3ef4638b3dc..49ca4a0b3e8 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/receivers.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/receivers.go @@ -13,8 +13,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/licensing" - "github.com/grafana/grafana/pkg/services/ngalert" - alertingac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" + "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/team" @@ -29,8 +28,8 @@ var ReceiversAdminActions = append(ReceiversEditActions, []string{accesscontrol. // defaultPermissions returns the default permissions for a newly created receiver. func defaultPermissions() []accesscontrol.SetResourcePermissionCommand { return []accesscontrol.SetResourcePermissionCommand{ - {BuiltinRole: string(org.RoleEditor), Permission: string(alertingac.ReceiverPermissionEdit)}, - {BuiltinRole: string(org.RoleViewer), Permission: string(alertingac.ReceiverPermissionView)}, + {BuiltinRole: string(org.RoleEditor), Permission: string(models.PermissionEdit)}, + {BuiltinRole: string(org.RoleViewer), Permission: string(models.PermissionView)}, } } @@ -43,7 +42,7 @@ func ProvideReceiverPermissionsService( Resource: "receivers", ResourceAttribute: "uid", ResourceTranslator: func(ctx context.Context, orgID int64, resourceID string) (string, error) { - return alertingac.ScopeReceiversProvider.GetResourceIDFromUID(resourceID), nil + return models.ScopeReceiversProvider.GetResourceIDFromUID(resourceID), nil }, Assignments: resourcepermissions.Assignments{ Users: true, @@ -52,13 +51,13 @@ func ProvideReceiverPermissionsService( ServiceAccounts: true, }, PermissionsToActions: map[string][]string{ - string(alertingac.ReceiverPermissionView): append([]string{}, ReceiversViewActions...), - string(alertingac.ReceiverPermissionEdit): append([]string{}, ReceiversEditActions...), - string(alertingac.ReceiverPermissionAdmin): append([]string{}, ReceiversAdminActions...), + string(models.PermissionView): append([]string{}, ReceiversViewActions...), + string(models.PermissionEdit): append([]string{}, ReceiversEditActions...), + string(models.PermissionAdmin): append([]string{}, ReceiversAdminActions...), }, ReaderRoleName: "Alerting receiver permission reader", WriterRoleName: "Alerting receiver permission writer", - RoleGroup: ngalert.AlertRolesGroup, + RoleGroup: models.AlertRolesGroup, } srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService) @@ -79,7 +78,7 @@ type ReceiverPermissionsService struct { // SetDefaultPermissions sets the default permissions for a newly created receiver. func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, orgID int64, user identity.Requester, uid string) { r.log.Debug("Setting default permissions for receiver", "receiver_uid", uid) - resourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(uid) + resourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(uid) permissions := defaultPermissions() clearCache := false if user != nil && user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) { @@ -88,7 +87,7 @@ func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, o r.log.Error("Could not make user admin", "receiver_uid", uid, "resource_id", resourceId, "id", user.GetID(), "error", err) } else { permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ - UserID: userID, Permission: string(alertingac.ReceiverPermissionAdmin), + UserID: userID, Permission: string(models.PermissionAdmin), }) clearCache = true } @@ -109,7 +108,7 @@ func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, o // permissions to read and write permissions for the receiver, as well as read permissions for users, service accounts, and teams. func copyPermissionUser(orgID int64) identity.Requester { return accesscontrol.BackgroundUser("receiver_access_service", orgID, org.RoleAdmin, accesscontrol.ConcatPermissions( - accesscontrol.PermissionsForActions(ReceiversAdminActions, alertingac.ScopeReceiversAll), + accesscontrol.PermissionsForActions(ReceiversAdminActions, models.ScopeReceiversAll), []accesscontrol.Permission{ // Permissions needed for GetPermissions to return user, service account, and team permissions. {Action: accesscontrol.ActionOrgUsersRead, Scope: accesscontrol.ScopeUsersAll}, {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, @@ -124,8 +123,8 @@ func copyPermissionUser(orgID int64) identity.Requester { // name. func (r ReceiverPermissionsService) CopyPermissions(ctx context.Context, orgID int64, user identity.Requester, oldUID, newUID string) (int, error) { r.log.Debug("Copying permissions from receiver", "old_uid", oldUID, "new_uid", newUID) - oldResourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(oldUID) - newResourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(newUID) + oldResourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(oldUID) + newResourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(newUID) currentPermissions, err := r.GetPermissions(ctx, copyPermissionUser(orgID), oldResourceId) if err != nil { return 0, err @@ -153,7 +152,7 @@ func (r ReceiverPermissionsService) CopyPermissions(ctx context.Context, orgID i } func (r ReceiverPermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, uid string) error { - return r.Service.DeleteResourcePermissions(ctx, orgID, alertingac.ScopeReceiversProvider.GetResourceIDFromUID(uid)) + return r.Service.DeleteResourcePermissions(ctx, orgID, models.ScopeReceiversProvider.GetResourceIDFromUID(uid)) } // toSetResourcePermissionCommands converts a list of resource permissions to a list of set resource permission commands. diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index e688ef16e08..cf51380b22d 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -2,8 +2,10 @@ package rbac import ( "fmt" + "slices" "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" ) // Mapping maps a verb to a RBAC action and a resource name to a RBAC scope. @@ -11,6 +13,9 @@ type Mapping interface { // action returns the action for the given verb. // If no action is found, it returns false. Action(verb string) (string, bool) + // ActionSets returns the action sets for the given verb. + // If no action sets are found, it returns an empty slice. This is expected for resources that do not have action sets (anything apart from dashboards and folders). + ActionSets(verb string) []string // scope returns the scope for the given resource name. Scope(name string) string // prefix returns the scope prefix for the translation. @@ -27,6 +32,7 @@ type translation struct { resource string attribute string verbMapping map[string]string + actionSetMapping map[string][]string folderSupport bool skipScopeOnCreate bool } @@ -36,6 +42,11 @@ func (t translation) Action(verb string) (string, bool) { return action, ok } +func (t translation) ActionSets(verb string) []string { + actionSets := t.actionSetMapping[verb] + return actionSets +} + func (t translation) Scope(name string) string { return t.resource + ":" + t.attribute + ":" + name } @@ -101,13 +112,67 @@ func newResourceTranslation(resource string, attribute string, folderSupport, sk } } +// newDashboardTranslation creates a translation for dashboards and also maps the actions to action sets +func newDashboardTranslation() translation { + dashTranslation := newResourceTranslation("dashboards", "uid", true, false) + + actionSetMapping := make(map[string][]string) + for verb, rbacAction := range dashTranslation.verbMapping { + var dashActionSets []string + if slices.Contains(ossaccesscontrol.DashboardViewActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:view") + dashActionSets = append(dashActionSets, "folders:view") + } + if slices.Contains(ossaccesscontrol.DashboardEditActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:edit") + dashActionSets = append(dashActionSets, "folders:edit") + } + if slices.Contains(ossaccesscontrol.DashboardAdminActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:admin") + dashActionSets = append(dashActionSets, "folders:admin") + } + actionSetMapping[verb] = dashActionSets + } + + dashTranslation.actionSetMapping = actionSetMapping + return dashTranslation +} + +// newFolderTranslation creates a translation for folders and also maps the actions to action sets +func newFolderTranslation() translation { + folderTranslation := newResourceTranslation("folders", "uid", true, false) + + actionSetMapping := make(map[string][]string) + for verb, rbacAction := range folderTranslation.verbMapping { + var actionSets []string + // Folder creation has not been added to the FolderEditActions and FolderAdminActions slices (https://github.com/grafana/identity-access-team/issues/794) + // so we handle it as a special case for now + if rbacAction == "folders:create" { + actionSets = append(actionSets, "folders:edit") + actionSets = append(actionSets, "folders:admin") + } + if slices.Contains(ossaccesscontrol.FolderViewActions, rbacAction) { + actionSets = append(actionSets, "folders:view") + } + if slices.Contains(ossaccesscontrol.FolderEditActions, rbacAction) { + actionSets = append(actionSets, "folders:edit") + } + if slices.Contains(ossaccesscontrol.FolderAdminActions, rbacAction) { + actionSets = append(actionSets, "folders:admin") + } + actionSetMapping[verb] = actionSets + } + folderTranslation.actionSetMapping = actionSetMapping + return folderTranslation +} + func NewMapperRegistry() MapperRegistry { mapper := mapper(map[string]map[string]translation{ "dashboard.grafana.app": { - "dashboards": newResourceTranslation("dashboards", "uid", true, false), + "dashboards": newDashboardTranslation(), }, "folder.grafana.app": { - "folders": newResourceTranslation("folders", "uid", true, false), + "folders": newFolderTranslation(), }, "iam.grafana.app": { // Users is a special case. We translate user permissions from id to uid based. diff --git a/pkg/services/authz/rbac/models.go b/pkg/services/authz/rbac/models.go index 62495a5139e..38e2beab59e 100644 --- a/pkg/services/authz/rbac/models.go +++ b/pkg/services/authz/rbac/models.go @@ -7,6 +7,7 @@ type checkRequest struct { IdentityType claims.IdentityType UserUID string Action string // Verb has been mapped into an action + ActionSets []string Group string Resource string Verb string @@ -22,6 +23,7 @@ type listRequest struct { Resource string Verb string Action string + ActionSets []string Options *ListRequestOptions } diff --git a/pkg/services/authz/rbac/service.go b/pkg/services/authz/rbac/service.go index 9af70a26277..45d2c97c11d 100644 --- a/pkg/services/authz/rbac/service.go +++ b/pkg/services/authz/rbac/service.go @@ -160,7 +160,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv } s.metrics.permissionCacheUsage.WithLabelValues("false", checkReq.Action).Inc() - permissions, err := s.getIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action) + permissions, err := s.getIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action, checkReq.ActionSets) if err != nil { ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err) s.metrics.requestCount.WithLabelValues("true", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() @@ -223,7 +223,7 @@ func (s *Service) List(ctx context.Context, req *authzv1.ListRequest) (*authzv1. if err != nil || listReq.Options.SkipCache { s.metrics.permissionCacheUsage.WithLabelValues("false", listReq.Action).Inc() - permissions, err = s.getIdentityPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action) + permissions, err = s.getIdentityPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action, listReq.ActionSets) if err != nil { ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err) s.metrics.requestCount.WithLabelValues("true", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() @@ -260,7 +260,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe return nil, err } - action, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) + action, actionSets, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) if err != nil { return nil, err } @@ -270,6 +270,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe UserUID: userUID, IdentityType: idType, Action: action, + ActionSets: actionSets, Group: req.GetGroup(), Resource: req.GetResource(), Verb: req.GetVerb(), @@ -293,7 +294,7 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ return nil, err } - action, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) + action, actionSets, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) if err != nil { return nil, err } @@ -311,6 +312,7 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ UserUID: userUID, IdentityType: idType, Action: action, + ActionSets: actionSets, Group: req.GetGroup(), Resource: req.GetResource(), Verb: req.GetVerb(), @@ -359,34 +361,30 @@ func (s *Service) validateSubject(ctx context.Context, subject string) (string, } // Find the action for a selected verb -func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, error) { +func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, []string, error) { ctxLogger := s.logger.FromContext(ctx) t, ok := s.mapper.Get(group, resource) if !ok { ctxLogger.Error("unsupported resource", "group", group, "resource", resource) - return "", status.Error(codes.NotFound, "unsupported resource") + return "", nil, status.Error(codes.NotFound, "unsupported resource") } action, ok := t.Action(verb) if !ok { ctxLogger.Error("unsupported verb", "group", group, "resource", resource, "verb", verb) - return "", status.Error(codes.NotFound, "unsupported verb") + return "", nil, status.Error(codes.NotFound, "unsupported verb") } - return action, nil + actionSets := t.ActionSets(verb) + + return action, actionSets, nil } -func (s *Service) getIdentityPermissions(ctx context.Context, ns types.NamespaceInfo, idType types.IdentityType, userID, action string) (map[string]bool, error) { +func (s *Service) getIdentityPermissions(ctx context.Context, ns types.NamespaceInfo, idType types.IdentityType, userID, action string, actionSets []string) (map[string]bool, error) { ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getIdentityPermissions") defer span.End() - // When checking folder creation permissions, also check edit and admin action sets for folder, as the scoped folder create actions aren't stored in the DB separately - var actionSets []string - if action == "folders:create" { - actionSets = append(actionSets, "folders:edit", "folders:admin") - } - switch idType { case types.TypeAnonymous: return s.getAnonymousPermissions(ctx, ns, action, actionSets) diff --git a/pkg/services/authz/rbac/service_test.go b/pkg/services/authz/rbac/service_test.go index 884ade0613a..c3a9f29a3c1 100644 --- a/pkg/services/authz/rbac/service_test.go +++ b/pkg/services/authz/rbac/service_test.go @@ -3,6 +3,7 @@ package rbac import ( "context" "fmt" + "slices" "testing" "time" @@ -363,6 +364,7 @@ func TestService_mapping(t *testing.T) { }, output: &checkRequest{ Action: "folders:create", + ActionSets: []string{"folders:edit", "folders:admin"}, Group: "folder.grafana.app", Resource: "folders", Name: "aaa", @@ -618,6 +620,7 @@ func TestService_getUserPermissions(t *testing.T) { type testCase struct { name string permissions []accesscontrol.Permission + action string cacheHit bool expectedPerms map[string]bool } @@ -628,12 +631,14 @@ func TestService_getUserPermissions(t *testing.T) { permissions: []accesscontrol.Permission{ {Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"}, }, + action: "dashboards:read", cacheHit: false, expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true}, }, { name: "should return error if store fails", permissions: nil, + action: "dashboards:read", cacheHit: false, expectedPerms: map[string]bool{}, }, @@ -642,6 +647,7 @@ func TestService_getUserPermissions(t *testing.T) { permissions: []accesscontrol.Permission{ {Action: "teams:read", Scope: "teams:id:1"}, }, + action: "teams:read", cacheHit: false, expectedPerms: map[string]bool{"teams:uid:t1": true}, }, @@ -654,10 +660,9 @@ func TestService_getUserPermissions(t *testing.T) { ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} userID := &store.UserIdentifiers{UID: "test-uid", ID: 112} - action := "dashboards:read" if tc.cacheHit { - s.permCache.Set(ctx, userPermCacheKey(ns.Value, userID.UID, action), tc.expectedPerms) + s.permCache.Set(ctx, userPermCacheKey(ns.Value, userID.UID, tc.action), tc.expectedPerms) } store := &fakeStore{ @@ -677,7 +682,7 @@ func TestService_getUserPermissions(t *testing.T) { disableNsCheck: true, } - perms, err := s.getIdentityPermissions(ctx, ns, types.TypeUser, userID.UID, action) + perms, err := s.getIdentityPermissions(ctx, ns, types.TypeUser, userID.UID, tc.action, nil) require.NoError(t, err) require.Len(t, perms, len(tc.expectedPerms)) for scope := range perms { @@ -1086,6 +1091,53 @@ func TestService_Check(t *testing.T) { }, expected: true, }, + { + name: "should take into account action sets", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + Name: "dash1", + }, + permissions: []accesscontrol.Permission{ + {Action: "dashboards:admin", Scope: "dashboards:uid:dash1"}, + }, + expected: true, + }, + { + name: "should take into account folder action sets for dashboard access", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + Name: "dash1", + Folder: "some_folder", + }, + permissions: []accesscontrol.Permission{ + {Action: "folders:edit", Scope: "folders:uid:some_folder"}, + }, + expected: true, + }, + { + name: "lower level action set or action set on a different resource should not grant higher level access", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "folder.grafana.app", + Resource: "folders", + Verb: "delete", + Name: "folder1", + }, + permissions: []accesscontrol.Permission{ + {Action: "folders:view", Scope: "folders:uid:folder1"}, + {Action: "folders:edit", Scope: "folders:uid:other_folder"}, + }, + expected: false, + }, { // We've had cases where permissions were saved to the database // without splitting the scope into 'kind', 'attribute', and 'identifier'. @@ -1135,6 +1187,10 @@ func TestService_Check(t *testing.T) { if tc.req.Resource == "teams" { expAction = "teams:read" } + if tc.req.Resource == "folders" { + expAction = "folders:delete" + } + perms, ok := s.permCache.Get(ctx, userPermCacheKey("org-12", "test-uid", expAction)) require.True(t, ok) require.Len(t, perms, 1) @@ -1492,6 +1548,27 @@ func TestService_List(t *testing.T) { Folders: []string{"fold1"}, }, }, + { + name: "should list permissions for user with permission or action set permissions", + req: &authzv1.ListRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + }, + permissions: []accesscontrol.Permission{ + {Action: "dashboards:read", Scope: "dashboards:uid:dash1"}, + {Action: "dashboards:read", Scope: "dashboards:uid:dash2"}, + {Action: "dashboards:read", Scope: "folders:uid:fold1"}, + {Action: "dashboards:edit", Scope: "dashboards:uid:dash3"}, + {Action: "folders:view", Scope: "folders:uid:fold2"}, + }, + expected: &authzv1.ListResponse{ + Items: []string{"dash1", "dash2", "dash3"}, + Folders: []string{"fold1", "fold2"}, + }, + }, { name: "should return empty list for user without permission", req: &authzv1.ListRequest{ @@ -1824,7 +1901,13 @@ func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace types.Name if f.err { return nil, fmt.Errorf("store error") } - return f.userPermissions, nil + var permissions []accesscontrol.Permission + for _, p := range f.userPermissions { + if p.Action == query.Action || slices.Contains(query.ActionSets, p.Action) { + permissions = append(permissions, p) + } + } + return permissions, nil } func (f *fakeStore) ListFolders(ctx context.Context, namespace types.NamespaceInfo) ([]store.Folder, error) { diff --git a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go index 151d55562f9..ec50629c24b 100644 --- a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go +++ b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go @@ -20,7 +20,6 @@ import ( "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" - ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/user" @@ -99,7 +98,7 @@ func TestGetContactPoints(t *testing.T) { Permissions: map[int64]map[string][]string{ 1: { accesscontrol.ActionAlertingNotificationsRead: nil, - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }, } diff --git a/pkg/services/ngalert/accesscontrol.go b/pkg/services/ngalert/accesscontrol.go index 259db2bd5a9..e36c123c621 100644 --- a/pkg/services/ngalert/accesscontrol.go +++ b/pkg/services/ngalert/accesscontrol.go @@ -5,19 +5,17 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" - ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" + "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" ) -const AlertRolesGroup = "Alerting" - var ( rulesReaderRole = accesscontrol.RoleRegistration{ Role: accesscontrol.RoleDTO{ Name: accesscontrol.FixedRolePrefix + "alerting.rules:reader", DisplayName: "Rules Reader", Description: "Read alert rules in all Grafana folders and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingRuleRead, @@ -47,7 +45,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.rules:writer", DisplayName: "Rules Writer", Description: "Add, update, and delete rules in any Grafana folder and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingRuleCreate, @@ -82,7 +80,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.instances:reader", DisplayName: "Instances and Silences Reader", Description: "Read instances and silences of Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingInstanceRead, @@ -100,7 +98,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.instances:writer", DisplayName: "Silences Writer", Description: "Add and update silences in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(instancesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingInstanceCreate, @@ -121,9 +119,9 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:reader", DisplayName: "Contact Point Reader", Description: "Read all contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversRead, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}, }, }, } @@ -133,7 +131,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:creator", DisplayName: "Contact Point Creator", Description: "Create new contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingReceiversCreate}, {Action: accesscontrol.ActionAlertingReceiversTest}, @@ -146,10 +144,10 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:writer", DisplayName: "Contact Point Writer", Description: "Create, update, and delete all contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(receiversReaderRole.Role.Permissions, receiversCreatorRole.Role.Permissions, []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversUpdate, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversDelete, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversAll}, }), }, } @@ -159,7 +157,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.templates:reader", DisplayName: "Templates Reader", Description: "Read all templates in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTemplatesRead}, }, @@ -171,7 +169,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.templates:writer", DisplayName: "Templates Writer", Description: "Create, update, and delete all templates in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(templatesReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTemplatesWrite}, {Action: accesscontrol.ActionAlertingNotificationsTemplatesDelete}, @@ -184,7 +182,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.time-intervals:reader", DisplayName: "Time Intervals Reader", Description: "Read all time intervals in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsRead}, }, @@ -196,7 +194,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.time-intervals:writer", DisplayName: "Time Intervals Writer", Description: "Create, update, and delete all time intervals in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(timeIntervalsReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsWrite}, {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsDelete}, @@ -209,7 +207,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.routes:reader", DisplayName: "Notification Policies Reader", Description: "Read all notification policies in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions([]accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingRoutesRead}, }), @@ -221,7 +219,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.routes:writer", DisplayName: "Notification Policies Writer", Description: "Update and reset notification policies in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(routesReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingRoutesWrite}, }), @@ -233,7 +231,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.notifications:reader", DisplayName: "Notifications Reader", Description: "Read notification policies and contact points in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(receiversReaderRole.Role.Permissions, templatesReaderRole.Role.Permissions, timeIntervalsReaderRole.Role.Permissions, routesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingNotificationsRead, // TODO remove when we decide tò limit access to raw config API @@ -251,7 +249,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.notifications:writer", DisplayName: "Notifications Writer", Description: "Add, update, and delete contact points and notification policies in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(notificationsReaderRole.Role.Permissions, receiversWriterRole.Role.Permissions, templatesWriterRole.Role.Permissions, timeIntervalsWriterRole.Role.Permissions, routesWriterRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingNotificationsWrite, // TODO remove when we decide tò limit access to raw config API @@ -269,7 +267,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:reader", DisplayName: "Full read-only access", Description: "Read alert rules, instances, silences, contact points, and notification policies in Grafana and all external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesReaderRole.Role.Permissions, instancesReaderRole.Role.Permissions, notificationsReaderRole.Role.Permissions), }, Grants: []string{string(org.RoleViewer)}, @@ -280,7 +278,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:writer", DisplayName: "Full write access", Description: "Add, update and delete alert rules, instances, silences, contact points, and notification policies in Grafana and all external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesWriterRole.Role.Permissions, instancesWriterRole.Role.Permissions, notificationsWriterRole.Role.Permissions), }, Grants: []string{string(org.RoleEditor)}, @@ -291,11 +289,11 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:admin", DisplayName: "Full admin access", Description: "Full write access in Grafana and all external providers, including their permissions and secrets", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(alertingWriterRole.Role.Permissions, []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversPermissionsRead, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversPermissionsWrite, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversReadSecrets, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversAll}, }), }, Grants: []string{string(org.RoleAdmin)}, @@ -306,7 +304,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning:writer", DisplayName: "Access to alert rules provisioning API", Description: "Manage all alert rules, contact points, notification policies, silences, etc. in the organization via provisioning API.", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningRead, // organization scope @@ -340,7 +338,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning.secrets:reader", DisplayName: "Read via Provisioning API + Export Secrets", Description: "Read all alert rules, contact points, notification policies, silences, etc. in the organization via provisioning API and use export with decrypted secrets", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningReadSecrets, // organization scope @@ -364,7 +362,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning.provenance:writer", DisplayName: "Set provisioning status", Description: "Set provisioning status for alerting resources. Should be used together with other regular roles (Notifications Writer and/or Rules Writer)", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningSetStatus, // organization scope diff --git a/pkg/services/ngalert/accesscontrol/receivers.go b/pkg/services/ngalert/accesscontrol/receivers.go index 1016f246a41..eb2904854bf 100644 --- a/pkg/services/ngalert/accesscontrol/receivers.go +++ b/pkg/services/ngalert/accesscontrol/receivers.go @@ -2,52 +2,10 @@ package accesscontrol import ( "context" - // #nosec G505 Used only for shortening the uid, not for security purposes. - "crypto/sha1" - "encoding/hex" "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" - "github.com/grafana/grafana/pkg/util" -) - -const ( - ScopeReceiversRoot = "receivers" -) - -var ( - ScopeReceiversProvider = ReceiverScopeProvider{ac.NewScopeProvider(ScopeReceiversRoot)} - ScopeReceiversAll = ScopeReceiversProvider.GetResourceAllScope() -) - -type ReceiverScopeProvider struct { - ac.ScopeProvider -} - -func (p ReceiverScopeProvider) GetResourceScopeUID(uid string) string { - return ScopeReceiversProvider.ScopeProvider.GetResourceScopeUID(p.GetResourceIDFromUID(uid)) -} - -// GetResourceIDFromUID converts a receiver uid to a resource id. This is necessary as resource ids are limited to 40 characters. -// If the uid is already less than or equal to 40 characters, it is returned as is. -func (p ReceiverScopeProvider) GetResourceIDFromUID(uid string) string { - if len(uid) <= util.MaxUIDLength { - return uid - } - // #nosec G505 Used only for shortening the uid, not for security purposes. - h := sha1.New() - h.Write([]byte(uid)) - return hex.EncodeToString(h.Sum(nil)) -} - -// ReceiverPermission is a type for representing a receiver permission. -type ReceiverPermission string - -const ( - ReceiverPermissionView ReceiverPermission = "View" - ReceiverPermissionEdit ReceiverPermission = "Edit" - ReceiverPermissionAdmin ReceiverPermission = "Admin" ) var ( @@ -65,19 +23,19 @@ var ( // Asserts read-only access to all redacted receivers. readRedactedAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsRead), - ac.EvalPermission(ac.ActionAlertingReceiversRead, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversRead, models.ScopeReceiversAll), readDecryptedAllReceiversEval, ) // Asserts read-only access to all decrypted receivers. readDecryptedAllReceiversEval = ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, models.ScopeReceiversAll), ) // Asserts read-only access to a specific redacted receiver. readRedactedReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsRead), - ac.EvalPermission(ac.ActionAlertingReceiversRead, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversRead, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), readDecryptedReceiverEval(uid), ) } @@ -85,7 +43,7 @@ var ( // Asserts read-only access to a specific decrypted receiver. readDecryptedReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -126,14 +84,14 @@ var ( // Asserts update access to all receivers. updateAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversUpdate, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversUpdate, models.ScopeReceiversAll), ) // Asserts update access to a specific receiver. updateReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversUpdate, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversUpdate, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -148,14 +106,14 @@ var ( // Asserts delete access to all receivers. deleteAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversDelete, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversDelete, models.ScopeReceiversAll), ) // Asserts delete access to a specific receiver. deleteReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversDelete, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversDelete, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -169,15 +127,15 @@ var ( // Asserts resource permissions access to all receivers. permissionsAllReceiversEval = ac.EvalAll( - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversAll), - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, models.ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, models.ScopeReceiversAll), ) // Asserts resource permissions access to a specific receiver. permissionsReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAll( - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversProvider.GetResourceScopeUID(uid)), - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } ) diff --git a/pkg/services/ngalert/accesscontrol/receivers_test.go b/pkg/services/ngalert/accesscontrol/receivers_test.go index 6ad2e1db463..7be1e9498e2 100644 --- a/pkg/services/ngalert/accesscontrol/receivers_test.go +++ b/pkg/services/ngalert/accesscontrol/receivers_test.go @@ -85,7 +85,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver read. { name: "global receiver reader should have no elevated permissions", - user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversRead, Scope: ScopeReceiversAll}), + user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -94,7 +94,7 @@ func TestReceiverAccess(t *testing.T) { }, { name: "global receiver secret reader should have secret permissions", - user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversAll}), + user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret), recv2.UID: permissions(models.ReceiverPermissionReadSecret), @@ -104,8 +104,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver secret reader should have per-receiver", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret), @@ -173,7 +173,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver update. { name: "global receiver update should have write but no delete", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite), recv2.UID: permissions(models.ReceiverPermissionWrite), @@ -183,8 +183,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver update should have per-receiver write but no delete", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite), @@ -195,8 +195,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver update should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -207,7 +207,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver delete. { name: "global receiver delete should have delete but no write", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionDelete), recv2.UID: permissions(models.ReceiverPermissionDelete), @@ -217,8 +217,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver delete should have per-receiver delete but no write", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionDelete), @@ -229,8 +229,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver delete should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -241,7 +241,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver admin. { name: "receiver read permissions alone can't admin", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -250,7 +250,7 @@ func TestReceiverAccess(t *testing.T) { }, { name: "receiver write permissions alone can't admin", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -260,8 +260,8 @@ func TestReceiverAccess(t *testing.T) { { name: "global receiver read + write permissions can admin", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionAdmin), @@ -272,10 +272,10 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver read + write permissions should have per-receiver admin", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionAdmin), @@ -286,10 +286,10 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver admin should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -302,7 +302,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy provisioning secret read, receiver write", user: newViewUser( ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -319,7 +319,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy provisioning secret read, receiver delete", user: newViewUser( ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -336,7 +336,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy write, receiver secret", user: newViewUser( ac.Permission{Action: ac.ActionAlertingNotificationsWrite}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete), @@ -347,12 +347,12 @@ func TestReceiverAccess(t *testing.T) { { name: "mixed secret / delete / write", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite), @@ -363,12 +363,12 @@ func TestReceiverAccess(t *testing.T) { { name: "mixed requires read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite), @@ -404,7 +404,7 @@ func newEmptyUser(permissions ...ac.Permission) identity.Requester { func newViewUser(permissions ...ac.Permission) identity.Requester { return ac.BackgroundUser("test", orgID, org.RoleNone, append([]ac.Permission{ - {Action: ac.ActionAlertingReceiversRead, Scope: ScopeReceiversAll}, + {Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}, {Action: ac.ActionAlertingNotificationsRead}, }, permissions...)) } diff --git a/pkg/services/ngalert/accesscontrol/rules_test.go b/pkg/services/ngalert/accesscontrol/rules_test.go index a9006f5ef0f..0cf9a4454ab 100644 --- a/pkg/services/ngalert/accesscontrol/rules_test.go +++ b/pkg/services/ngalert/accesscontrol/rules_test.go @@ -85,7 +85,7 @@ func getReceiverScopesForRules(rules models.RulesGroup) []string { var result []string for _, rule := range rules { for _, ns := range rule.NotificationSettings { - scope := ScopeReceiversProvider.GetResourceScopeUID(legacy_storage.NameToUid(ns.Receiver)) + scope := models.ScopeReceiversProvider.GetResourceScopeUID(legacy_storage.NameToUid(ns.Receiver)) if _, ok := scopesMap[scope]; ok { continue } diff --git a/pkg/services/ngalert/models/accesscontrol.go b/pkg/services/ngalert/models/accesscontrol.go new file mode 100644 index 00000000000..1a8da627e84 --- /dev/null +++ b/pkg/services/ngalert/models/accesscontrol.go @@ -0,0 +1,44 @@ +package models + +import ( + // #nosec G505 Used only for shortening the uid, not for security purposes. + "crypto/sha1" + "encoding/hex" + + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/util" +) + +const ( + ScopeReceiversRoot = "receivers" + AlertRolesGroup = "Alerting" + + PermissionView ReceiverPermission = "View" + PermissionEdit ReceiverPermission = "Edit" + PermissionAdmin ReceiverPermission = "Admin" +) + +var ( + ScopeReceiversProvider = ReceiverScopeProvider{accesscontrol.NewScopeProvider(ScopeReceiversRoot)} + ScopeReceiversAll = ScopeReceiversProvider.GetResourceAllScope() +) + +type ReceiverScopeProvider struct { + accesscontrol.ScopeProvider +} + +func (p ReceiverScopeProvider) GetResourceScopeUID(uid string) string { + return ScopeReceiversProvider.ScopeProvider.GetResourceScopeUID(p.GetResourceIDFromUID(uid)) +} + +// GetResourceIDFromUID converts a receiver uid to a resource id. This is necessary as resource ids are limited to 40 characters. +// If the uid is already less than or equal to 40 characters, it is returned as is. +func (p ReceiverScopeProvider) GetResourceIDFromUID(uid string) string { + if len(uid) <= util.MaxUIDLength { + return uid + } + // #nosec G505 Used only for shortening the uid, not for security purposes. + h := sha1.New() + h.Write([]byte(uid)) + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/pkg/services/ngalert/notifier/receiver_svc_test.go b/pkg/services/ngalert/notifier/receiver_svc_test.go index ad968282ed1..85e7733f4bd 100644 --- a/pkg/services/ngalert/notifier/receiver_svc_test.go +++ b/pkg/services/ngalert/notifier/receiver_svc_test.go @@ -117,7 +117,7 @@ func TestIntegrationReceiverService_DecryptRedact(t *testing.T) { Permissions: map[int64]map[string][]string{ 1: { accesscontrol.ActionAlertingNotificationsRead: nil, - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }, } @@ -343,7 +343,7 @@ func TestReceiverService_Create(t *testing.T) { }} decryptUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{ 1: { - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }} @@ -556,7 +556,7 @@ func TestReceiverService_Update(t *testing.T) { }} decryptUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{ 1: { - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }} @@ -949,30 +949,30 @@ func TestReceiverServiceAC_Read(t *testing.T) { }, { name: "global receivers permissions - read all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), visible: allReceivers(), }, { name: "single receivers permissions - read some", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), visible: []models.Receiver{recv1, recv3}, }, { name: "global receivers secret permissions - read all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}}, existing: allReceivers(), visible: allReceivers(), }, { name: "single receivers secret permissions - read some", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), visible: []models.Receiver{recv1, recv3}, @@ -1175,15 +1175,15 @@ func TestReceiverServiceAC_Update(t *testing.T) { }, { name: "global receivers permissions - not authorized without read", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: nil, }, { name: "single receivers permissions - not authorized without read", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), hasAccess: nil, @@ -1196,7 +1196,7 @@ func TestReceiverServiceAC_Update(t *testing.T) { }, { name: "global receivers permissions - update all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {ac.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {models.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: allReceivers(), }, @@ -1204,12 +1204,12 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1219,12 +1219,12 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers mixed read permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1234,8 +1234,8 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers mixed global read permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingNotificationsRead: nil, }, @@ -1327,15 +1327,15 @@ func TestReceiverServiceAC_Delete(t *testing.T) { }, { name: "global receivers permissions - not authorized without read", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: nil, }, { name: "single receivers permissions - not authorized without read", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), hasAccess: nil, @@ -1348,7 +1348,7 @@ func TestReceiverServiceAC_Delete(t *testing.T) { }, { name: "global receivers permissions - delete all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {ac.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {models.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: allReceivers(), }, @@ -1356,12 +1356,12 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1371,12 +1371,12 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers mixed read permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1386,8 +1386,8 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers mixed global read permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingNotificationsRead: nil, }, diff --git a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go index ced6afdb670..0f7b476ecad 100644 --- a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go +++ b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go @@ -35,7 +35,6 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder/foldertest" - alertingac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/api" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -204,7 +203,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns read, noneUser has no metadata but has access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionView)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionView)}}, expACMetadata: nil, expRead: true, }, @@ -212,7 +211,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns write, noneUser has write metadata and access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: writeACMetadata, expRead: true, }, @@ -220,7 +219,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, noneUser has all metadata and access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -229,7 +228,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns read to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionView)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionView)}}, expACMetadata: nil, expRead: false, }, @@ -237,7 +236,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns write to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: nil, expRead: false, }, @@ -245,7 +244,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: nil, expRead: false, }, @@ -254,7 +253,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns editor, viewer has write metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: writeACMetadata, expRead: true, }, @@ -262,7 +261,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, viewer has all metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -270,7 +269,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, editor has all metadata and access", creatingUser: admin, testUser: editor, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(editor), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(editor), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -279,7 +278,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to staff, viewer has no metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: nil, expRead: true, }, @@ -287,7 +286,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to staff, editor has all metadata and access", creatingUser: admin, testUser: editor, - assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, },