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 <gabi.mabs@gmail.com>

* more accurate variable name Co-authored-by: gamab <gabi.mabs@gmail.com>

* break alerting import cycle

* Apply suggestion from @gamab

---------

Co-authored-by: gamab <gabi.mabs@gmail.com>
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
Ieva
2025-10-08 13:37:12 +01:00
committed by GitHub
co-authored by gamab Gabriel MABILLE
parent bb1d7d9070
commit acbbfde256
13 changed files with 359 additions and 214 deletions
@@ -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.
+67 -2
View File
@@ -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.
+2
View File
@@ -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
}
+13 -15
View File
@@ -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)
+87 -4
View File
@@ -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) {
@@ -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},
},
},
}
+28 -30
View File
@@ -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
+12 -54
View File
@@ -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)),
)
}
)
@@ -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...))
}
@@ -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
}
@@ -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))
}
@@ -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,
},
@@ -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,
},