diff --git a/pkg/services/accesscontrol/accesscontrol.go b/pkg/services/accesscontrol/accesscontrol.go index 965cd622249..724898f0b6e 100644 --- a/pkg/services/accesscontrol/accesscontrol.go +++ b/pkg/services/accesscontrol/accesscontrol.go @@ -30,6 +30,8 @@ type Service interface { SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, options SearchOptions) (map[int64][]Permission, error) // ClearUserPermissionCache removes the permission cache entry for the given user ClearUserPermissionCache(user *user.SignedInUser) + // SearchUserPermissions returns single user's permissions filtered by an action prefix or an action + SearchUserPermissions(ctx context.Context, orgID int64, filterOptions SearchOptions) ([]Permission, error) // DeleteUserPermissions removes all permissions user has in org and all permission to that user // If orgID is set to 0 remove permissions from all orgs DeleteUserPermissions(ctx context.Context, orgID, userID int64) error @@ -53,6 +55,7 @@ type SearchOptions struct { ActionPrefix string // Needed for the PoC v1, it's probably going to be removed. Action string Scope string + UserID int64 // ID for the user for which to return information, if none is specified information is returned for all users. } type TeamPermissionsService interface { diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index b915a1b3123..fd83c593593 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -59,8 +59,8 @@ func ProvideOSSService(cfg *setting.Cfg, store store, cache *localcache.CacheSer type store interface { GetUserPermissions(ctx context.Context, query accesscontrol.GetUserPermissionsQuery) ([]accesscontrol.Permission, error) - SearchUsersPermissions(ctx context.Context, orgID int64, option accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) - GetUsersBasicRoles(ctx context.Context, orgID int64) (map[int64][]string, error) + SearchUsersPermissions(ctx context.Context, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) + GetUsersBasicRoles(ctx context.Context, userFilter []int64, orgID int64) (map[int64][]string, error) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error } @@ -264,7 +264,7 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, user *user.SignedI } } - usersRoles, err := s.store.GetUsersBasicRoles(ctx, orgID) + usersRoles, err := s.store.GetUsersBasicRoles(ctx, nil, orgID) if err != nil { return nil, err } @@ -330,3 +330,87 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, user *user.SignedI return res, nil } + +func (s *Service) SearchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) { + timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary) + defer timer.ObserveDuration() + + if searchOptions.UserID == 0 { + return nil, fmt.Errorf("expected user ID to be specified") + } + + if permissions, success := s.searchUserPermissionsFromCache(orgID, searchOptions); success { + return permissions, nil + } + return s.searchUserPermissions(ctx, orgID, searchOptions) +} + +func (s *Service) searchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) { + // Get permissions for user's basic roles from RAM + roleList, err := s.store.GetUsersBasicRoles(ctx, []int64{searchOptions.UserID}, orgID) + if err != nil { + return nil, fmt.Errorf("could not fetch basic roles for the user: %w", err) + } + var roles []string + var ok bool + if roles, ok = roleList[searchOptions.UserID]; !ok { + return nil, fmt.Errorf("found no basic roles for user %d in organisation %d", searchOptions.UserID, orgID) + } + permissions := make([]accesscontrol.Permission, 0) + for _, builtin := range roles { + if basicRole, ok := s.roles[builtin]; ok { + for _, permission := range basicRole.Permissions { + if PermissionMatchesSearchOptions(permission, searchOptions) { + permissions = append(permissions, permission) + } + } + } + } + + // Get permissions from the DB + dbPermissions, err := s.store.SearchUsersPermissions(ctx, orgID, searchOptions) + if err != nil { + return nil, err + } + permissions = append(permissions, dbPermissions[searchOptions.UserID]...) + + return permissions, nil +} + +func (s *Service) searchUserPermissionsFromCache(orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, bool) { + // Create a temp signed in user object to retrieve cache key + tempUser := &user.SignedInUser{ + UserID: searchOptions.UserID, + OrgID: orgID, + } + key, err := permissionCacheKey(tempUser) + if err != nil { + s.log.Debug("could not obtain cache key to search user permissions", "error", err.Error()) + return nil, false + } + + permissions, ok := s.cache.Get(key) + if !ok { + return nil, false + } + + s.log.Debug("using cached permissions", "key", key) + filteredPermissions := make([]accesscontrol.Permission, 0) + for _, permission := range permissions.([]accesscontrol.Permission) { + if PermissionMatchesSearchOptions(permission, searchOptions) { + filteredPermissions = append(filteredPermissions, permission) + } + } + + return filteredPermissions, true +} + +func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchOptions accesscontrol.SearchOptions) bool { + if searchOptions.Scope != "" && permission.Scope != searchOptions.Scope { + return false + } + if searchOptions.Action != "" { + return permission.Action == searchOptions.Action + } + return strings.HasPrefix(permission.Action, searchOptions.ActionPrefix) +} diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index fd9d6bb24a8..fe5e57f3473 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -523,6 +523,159 @@ func TestService_SearchUsersPermissions(t *testing.T) { } } +func TestService_SearchUserPermissions(t *testing.T) { + ctx := context.Background() + tests := []struct { + name string + searchOption accesscontrol.SearchOptions + ramRoles map[string]*accesscontrol.RoleDTO // BasicRole => RBAC BasicRole + storedPerms map[int64][]accesscontrol.Permission // UserID => Permissions + storedRoles map[int64][]string // UserID => Roles + want []accesscontrol.Permission + wantErr bool + }{ + { + name: "ram only", + searchOption: accesscontrol.SearchOptions{ + ActionPrefix: "teams", + UserID: 2, + }, + ramRoles: map[string]*accesscontrol.RoleDTO{ + string(roletype.RoleEditor): {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsCreate}, + }}, + string(roletype.RoleAdmin): {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + }}, + accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + }}, + }, + storedRoles: map[int64][]string{ + 1: {string(roletype.RoleEditor)}, + 2: {string(roletype.RoleAdmin), accesscontrol.RoleGrafanaAdmin}, + }, + want: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}}, + }, + { + name: "stored only", + searchOption: accesscontrol.SearchOptions{ + ActionPrefix: "teams", + UserID: 2, + }, + storedPerms: map[int64][]accesscontrol.Permission{ + 1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}}, + 2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}}, + }, + storedRoles: map[int64][]string{ + 1: {string(roletype.RoleEditor)}, + 2: {string(roletype.RoleAdmin), accesscontrol.RoleGrafanaAdmin}, + }, + want: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + }, + }, + { + name: "ram and stored", + searchOption: accesscontrol.SearchOptions{ + ActionPrefix: "teams", + UserID: 2, + }, + ramRoles: map[string]*accesscontrol.RoleDTO{ + string(roletype.RoleAdmin): {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + }}, + accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + }}, + }, + storedPerms: map[int64][]accesscontrol.Permission{ + 1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}}, + 2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}}, + }, + storedRoles: map[int64][]string{ + 1: {string(roletype.RoleEditor)}, + 2: {string(roletype.RoleAdmin), accesscontrol.RoleGrafanaAdmin}, + }, + want: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}, + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + }, + }, + { + name: "check action prefix filter works correctly", + searchOption: accesscontrol.SearchOptions{ + ActionPrefix: "teams", + UserID: 1, + }, + ramRoles: map[string]*accesscontrol.RoleDTO{ + string(roletype.RoleEditor): {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionUsersCreate}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"}, + }}, + }, + storedRoles: map[int64][]string{ + 1: {string(roletype.RoleEditor)}, + }, + want: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}, + }, + }, + { + name: "check action filter works correctly", + searchOption: accesscontrol.SearchOptions{ + Action: accesscontrol.ActionTeamsRead, + UserID: 1, + }, + ramRoles: map[string]*accesscontrol.RoleDTO{ + string(roletype.RoleEditor): {Permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + {Action: accesscontrol.ActionUsersCreate}, + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}, + {Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"}, + }}, + }, + storedRoles: map[int64][]string{ + 1: {string(roletype.RoleEditor)}, + }, + want: []accesscontrol.Permission{ + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}, + {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ac := setupTestEnv(t) + + ac.roles = tt.ramRoles + ac.store = actest.FakeStore{ + ExpectedUsersPermissions: tt.storedPerms, + ExpectedUsersRoles: tt.storedRoles, + } + + got, err := ac.searchUserPermissions(ctx, 1, tt.searchOption) + if tt.wantErr { + require.NotNil(t, err) + return + } + require.Nil(t, err) + + assert.ElementsMatch(t, got, tt.want) + }) + } +} + func TestPermissionCacheKey(t *testing.T) { testcases := []struct { name string diff --git a/pkg/services/accesscontrol/actest/fake.go b/pkg/services/accesscontrol/actest/fake.go index 3f6bad09e01..50235d6ff1c 100644 --- a/pkg/services/accesscontrol/actest/fake.go +++ b/pkg/services/accesscontrol/actest/fake.go @@ -11,10 +11,12 @@ var _ accesscontrol.Service = new(FakeService) var _ accesscontrol.RoleRegistry = new(FakeService) type FakeService struct { - ExpectedErr error - ExpectedDisabled bool - ExpectedPermissions []accesscontrol.Permission - ExpectedUsersPermissions map[int64][]accesscontrol.Permission + ExpectedErr error + ExpectedDisabled bool + ExpectedCachedPermissions bool + ExpectedPermissions []accesscontrol.Permission + ExpectedFilteredUserPermissions []accesscontrol.Permission + ExpectedUsersPermissions map[int64][]accesscontrol.Permission } func (f FakeService) GetUsageStats(ctx context.Context) map[string]interface{} { @@ -29,6 +31,10 @@ func (f FakeService) SearchUsersPermissions(ctx context.Context, user *user.Sign return f.ExpectedUsersPermissions, f.ExpectedErr } +func (f FakeService) SearchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) { + return f.ExpectedFilteredUserPermissions, f.ExpectedErr +} + func (f FakeService) ClearUserPermissionCache(user *user.SignedInUser) {} func (f FakeService) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error { @@ -81,7 +87,7 @@ func (f FakeStore) SearchUsersPermissions(ctx context.Context, orgID int64, opti return f.ExpectedUsersPermissions, f.ExpectedErr } -func (f FakeStore) GetUsersBasicRoles(ctx context.Context, orgID int64) (map[int64][]string, error) { +func (f FakeStore) GetUsersBasicRoles(ctx context.Context, userFilter []int64, orgID int64) (map[int64][]string, error) { return f.ExpectedUsersRoles, f.ExpectedErr } diff --git a/pkg/services/accesscontrol/api/api.go b/pkg/services/accesscontrol/api/api.go index 5c06a9ef239..eadfaf0a028 100644 --- a/pkg/services/accesscontrol/api/api.go +++ b/pkg/services/accesscontrol/api/api.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "strconv" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" @@ -9,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/models" ac "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/web" ) func NewAccessControlAPI(router routing.RouteRegister, accesscontrol ac.AccessControl, service ac.Service, @@ -35,8 +37,11 @@ func (api *AccessControlAPI) RegisterAPIEndpoints() { rr.Get("/user/actions", middleware.ReqSignedIn, routing.Wrap(api.getUserActions)) rr.Get("/user/permissions", middleware.ReqSignedIn, routing.Wrap(api.getUserPermissions)) if api.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) { + userIDScope := ac.Scope("users", "id", ac.Parameter(":userID")) rr.Get("/users/permissions/search", authorize(middleware.ReqSignedIn, - ac.EvalPermission(ac.ActionUsersPermissionsRead)), routing.Wrap(api.SearchUsersPermissions)) + ac.EvalPermission(ac.ActionUsersPermissionsRead)), routing.Wrap(api.searchUsersPermissions)) + rr.Get("/user/:userID/permissions/search", authorize(middleware.ReqSignedIn, + ac.EvalPermission(ac.ActionUsersPermissionsRead, userIDScope)), routing.Wrap(api.searchUserPermissions)) } }) } @@ -66,7 +71,7 @@ func (api *AccessControlAPI) getUserPermissions(c *models.ReqContext) response.R } // GET /api/access-control/users/permissions -func (api *AccessControlAPI) SearchUsersPermissions(c *models.ReqContext) response.Response { +func (api *AccessControlAPI) searchUsersPermissions(c *models.ReqContext) response.Response { searchOptions := ac.SearchOptions{ ActionPrefix: c.Query("actionPrefix"), Action: c.Query("action"), @@ -91,3 +96,30 @@ func (api *AccessControlAPI) SearchUsersPermissions(c *models.ReqContext) respon return response.JSON(http.StatusOK, permsByAction) } + +// GET /api/access-control/user/:userID/permissions/search +func (api *AccessControlAPI) searchUserPermissions(c *models.ReqContext) response.Response { + userIDString := web.Params(c.Req)[":userID"] + userID, err := strconv.ParseInt(userIDString, 10, 64) + if err != nil { + response.Error(http.StatusBadRequest, "user ID is invalid", err) + } + + searchOptions := ac.SearchOptions{ + ActionPrefix: c.Query("actionPrefix"), + Action: c.Query("action"), + Scope: c.Query("scope"), + UserID: userID, + } + // Validate inputs + if (searchOptions.ActionPrefix != "") == (searchOptions.Action != "") { + return response.JSON(http.StatusBadRequest, "provide one of 'action' or 'actionPrefix'") + } + + permissions, err := api.Service.SearchUserPermissions(c.Req.Context(), c.OrgID, searchOptions) + if err != nil { + response.Error(http.StatusInternalServerError, "could not search user permissions", err) + } + + return response.JSON(http.StatusOK, ac.GroupScopesByAction(permissions)) +} diff --git a/pkg/services/accesscontrol/database/database.go b/pkg/services/accesscontrol/database/database.go index 99508e51c16..2ecdecff5c9 100644 --- a/pkg/services/accesscontrol/database/database.go +++ b/pkg/services/accesscontrol/database/database.go @@ -86,11 +86,12 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i INNER JOIN ( SELECT u.id AS user_id FROM ` + s.sql.GetDialect().Quote("user") + ` AS u WHERE u.is_admin - ) AS sa ON 1 = 1 + ) AS sa ON 1 = 1 WHERE br.role = ? ) AS up WHERE (org_id = ? OR org_id = ?) ` + params := []interface{}{accesscontrol.RoleGrafanaAdmin, accesscontrol.GlobalOrgID, orgID} if options.ActionPrefix != "" { @@ -106,6 +107,11 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i params = append(params, options.Scope) } + if options.UserID != 0 { + q += ` AND user_id = ?` + params = append(params, options.UserID) + } + return sess.SQL(q, params...). Find(&dbPerms) }); err != nil { @@ -121,7 +127,7 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i } // GetUsersBasicRoles returns the list of user basic roles (Admin, Editor, Viewer, Grafana Admin) indexed by UserID -func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, orgID int64) (map[int64][]string, error) { +func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, userFilter []int64, orgID int64) (map[int64][]string, error) { type UserOrgRole struct { UserID int64 `xorm:"id"` OrgRole string `xorm:"role"` @@ -132,12 +138,19 @@ func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, orgID int64 // Find roles q := ` SELECT u.id, ou.role, u.is_admin - FROM ` + s.sql.GetDialect().Quote("user") + ` AS u + FROM ` + s.sql.GetDialect().Quote("user") + ` AS u LEFT JOIN org_user AS ou ON u.id = ou.user_id - WHERE u.is_admin OR ou.org_id = ? + WHERE (u.is_admin OR ou.org_id = ?) ` + params := []interface{}{orgID} + if len(userFilter) > 0 { + q += "AND u.id IN (?" + strings.Repeat(",?", len(userFilter)-1) + ")" + for _, u := range userFilter { + params = append(params, u) + } + } - return sess.SQL(q, orgID).Find(&dbRoles) + return sess.SQL(q, params...).Find(&dbRoles) }); err != nil { return nil, err } diff --git a/pkg/services/accesscontrol/database/database_test.go b/pkg/services/accesscontrol/database/database_test.go index f45dc2d389e..1da35456232 100644 --- a/pkg/services/accesscontrol/database/database_test.go +++ b/pkg/services/accesscontrol/database/database_test.go @@ -412,6 +412,34 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) { {Action: "teams:read", Scope: "teams:id:200"}}, }, }, + { + name: "all assignments for one user by actionPrefix", + users: []testUser{ + {orgRole: org.RoleAdmin, isAdmin: true}, + {orgRole: org.RoleEditor, isAdmin: false}, + }, + permCmds: []rs.SetResourcePermissionsCommand{ + // User assignments + {User: accesscontrol.User{ID: 1, IsExternal: false}, SetResourcePermissionCommand: readTeamPerm("1")}, + {User: accesscontrol.User{ID: 2, IsExternal: false}, SetResourcePermissionCommand: readTeamPerm("2")}, + // Team assignments + {TeamID: 1, SetResourcePermissionCommand: readTeamPerm("10")}, + {TeamID: 2, SetResourcePermissionCommand: readTeamPerm("20")}, + // Basic Assignments + {BuiltinRole: string(org.RoleAdmin), SetResourcePermissionCommand: readTeamPerm("100")}, + {BuiltinRole: string(org.RoleEditor), SetResourcePermissionCommand: readTeamPerm("200")}, + // Server Admin Assignment + {BuiltinRole: accesscontrol.RoleGrafanaAdmin, SetResourcePermissionCommand: readTeamPerm("1000")}, + }, + options: accesscontrol.SearchOptions{ + ActionPrefix: "teams:", + UserID: 1, + }, + wantPerm: map[int64][]accesscontrol.Permission{ + 1: {{Action: "teams:read", Scope: "teams:id:1"}, {Action: "teams:read", Scope: "teams:id:10"}, + {Action: "teams:read", Scope: "teams:id:100"}, {Action: "teams:read", Scope: "teams:id:1000"}}, + }, + }, { name: "filter permissions by action prefix", users: []testUser{{orgRole: org.RoleAdmin, isAdmin: true}}, @@ -519,10 +547,11 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) { func TestAccessControlStore_GetUsersBasicRoles(t *testing.T) { ctx := context.Background() tests := []struct { - name string - users []testUser - wantRoles map[int64][]string - wantErr bool + name string + users []testUser + userFilter []int64 + wantRoles map[int64][]string + wantErr bool }{ { name: "user with basic role", @@ -548,6 +577,17 @@ func TestAccessControlStore_GetUsersBasicRoles(t *testing.T) { 2: {accesscontrol.RoleGrafanaAdmin}, }, }, + { + name: "when filtered to one user, returns results only for that user", + userFilter: []int64{2}, + users: []testUser{ + {orgRole: org.RoleAdmin, isAdmin: false}, + {orgRole: org.RoleEditor, isAdmin: true}, + }, + wantRoles: map[int64][]string{ + 2: {string(org.RoleEditor), accesscontrol.RoleGrafanaAdmin}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -555,7 +595,7 @@ func TestAccessControlStore_GetUsersBasicRoles(t *testing.T) { dbUsers := createUsersAndTeams(t, helperServices{userSvc, teamSvc, orgSvc}, 1, tt.users) // Test - dbRoles, err := acStore.GetUsersBasicRoles(ctx, 1) + dbRoles, err := acStore.GetUsersBasicRoles(ctx, tt.userFilter, 1) if tt.wantErr { require.NotNil(t, err) return diff --git a/pkg/services/accesscontrol/mock/mock.go b/pkg/services/accesscontrol/mock/mock.go index c1f3e944b2f..1ae451c0069 100644 --- a/pkg/services/accesscontrol/mock/mock.go +++ b/pkg/services/accesscontrol/mock/mock.go @@ -29,6 +29,7 @@ type Calls struct { RegisterAttributeScopeResolver []interface{} DeleteUserPermissions []interface{} SearchUsersPermissions []interface{} + SearchUserPermissions []interface{} } type Mock struct { @@ -54,6 +55,7 @@ type Mock struct { RegisterScopeAttributeResolverFunc func(string, accesscontrol.ScopeAttributeResolver) DeleteUserPermissionsFunc func(context.Context, int64) error SearchUsersPermissionsFunc func(context.Context, *user.SignedInUser, int64, accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) + SearchUserPermissionsFunc func(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) scopeResolvers accesscontrol.Resolvers } @@ -215,7 +217,7 @@ func (m *Mock) DeleteUserPermissions(ctx context.Context, orgID, userID int64) e return nil } -// GetSimplifiedUsersPermissions returns all users' permissions filtered by an action prefix +// SearchUsersPermissions returns all users' permissions filtered by an action prefix func (m *Mock) SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { m.Calls.SearchUsersPermissions = append(m.Calls.SearchUsersPermissions, []interface{}{ctx, user, orgID, options}) // Use override if provided @@ -224,3 +226,12 @@ func (m *Mock) SearchUsersPermissions(ctx context.Context, user *user.SignedInUs } return nil, nil } + +func (m *Mock) SearchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) { + m.Calls.SearchUserPermissions = append(m.Calls.SearchUserPermissions, []interface{}{ctx, orgID, searchOptions}) + // Use override if provided + if m.SearchUserPermissionsFunc != nil { + return m.SearchUserPermissionsFunc(ctx, orgID, searchOptions) + } + return nil, nil +}