grafana/pkg/services/accesscontrol/mock/mock.go
Alexander Zobnin 82a88cc83f
Access control: Extend GetUserPermissions() to query permissions in org (#83392)
* Access control: Extend GetUserPermissions() to query permissions in specific org

* Use db query to fetch permissions in org

* refactor

* refactor

* use conditional join

* minor refactor

* Add test cases

* Search permissions correctly in OSS vs Enterprise

* Get permissions from memory

* Refactor

* remove unused func

* Add tests for GetUserPermissionsInOrg

* fix linter
2024-03-04 13:29:13 +01:00

269 lines
11 KiB
Go

package mock
import (
"context"
"errors"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/user"
)
type fullAccessControl interface {
accesscontrol.AccessControl
accesscontrol.Service
plugins.RoleRegistry
RegisterFixedRoles(context.Context) error
}
type Calls struct {
Evaluate []interface{}
GetUserPermissions []interface{}
GetUserPermissionsInOrg []interface{}
ClearUserPermissionCache []interface{}
DeclareFixedRoles []interface{}
DeclarePluginRoles []interface{}
GetUserBuiltInRoles []interface{}
RegisterFixedRoles []interface{}
RegisterAttributeScopeResolver []interface{}
DeleteUserPermissions []interface{}
DeleteTeamPermissions []interface{}
SearchUsersPermissions []interface{}
SearchUserPermissions []interface{}
SaveExternalServiceRole []interface{}
DeleteExternalServiceRole []interface{}
}
type Mock struct {
// Unless an override is provided, permissions will be returned by GetUserPermissions
permissions []accesscontrol.Permission
// Unless an override is provided, builtInRoles will be returned by GetUserBuiltInRoles
builtInRoles []string
// Track the list of calls
Calls Calls
// Override functions
EvaluateFunc func(context.Context, identity.Requester, accesscontrol.Evaluator) (bool, error)
GetUserPermissionsFunc func(context.Context, identity.Requester, accesscontrol.Options) ([]accesscontrol.Permission, error)
GetUserPermissionsInOrgFunc func(context.Context, identity.Requester, int64) ([]accesscontrol.Permission, error)
ClearUserPermissionCacheFunc func(identity.Requester)
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
DeclarePluginRolesFunc func(context.Context, string, string, []plugins.RoleRegistration) error
GetUserBuiltInRolesFunc func(user identity.Requester) []string
RegisterFixedRolesFunc func() error
RegisterScopeAttributeResolverFunc func(string, accesscontrol.ScopeAttributeResolver)
DeleteUserPermissionsFunc func(context.Context, int64) error
DeleteTeamPermissionsFunc func(context.Context, int64) error
SearchUsersPermissionsFunc func(context.Context, identity.Requester, int64, accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error)
SearchUserPermissionsFunc func(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error)
SaveExternalServiceRoleFunc func(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error
DeleteExternalServiceRoleFunc func(ctx context.Context, externalServiceID string) error
SyncUserRolesFunc func(ctx context.Context, orgID int64, cmd accesscontrol.SyncUserRolesCommand) error
scopeResolvers accesscontrol.Resolvers
}
// Ensure the mock stays in line with the interface
var _ fullAccessControl = New()
// Deprecated: use fake service and real access control evaluator instead
func New() *Mock {
mock := &Mock{
Calls: Calls{},
permissions: []accesscontrol.Permission{},
builtInRoles: []string{},
scopeResolvers: accesscontrol.NewResolvers(log.NewNopLogger()),
}
return mock
}
func (m *Mock) GetUsageStats(ctx context.Context) map[string]interface{} {
return make(map[string]interface{})
}
func (m *Mock) WithPermissions(permissions []accesscontrol.Permission) *Mock {
m.permissions = permissions
return m
}
func (m *Mock) WithBuiltInRoles(builtInRoles []string) *Mock {
m.builtInRoles = builtInRoles
return m
}
// Evaluate evaluates access to the given resource.
// This mock uses GetUserPermissions to then call the evaluator Evaluate function.
func (m *Mock) Evaluate(ctx context.Context, usr identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
m.Calls.Evaluate = append(m.Calls.Evaluate, []interface{}{ctx, usr, evaluator})
// Use override if provided
if m.EvaluateFunc != nil {
return m.EvaluateFunc(ctx, usr, evaluator)
}
var permissions map[string][]string
permissions = usr.GetPermissions()
if len(permissions) == 0 {
userPermissions, err := m.GetUserPermissions(ctx, usr, accesscontrol.Options{ReloadCache: true})
if err != nil {
return false, err
}
permissions = accesscontrol.GroupScopesByAction(userPermissions)
}
if evaluator.Evaluate(permissions) {
return true, nil
}
resolvedEvaluator, err := evaluator.MutateScopes(ctx, m.scopeResolvers.GetScopeAttributeMutator(usr.GetOrgID()))
if err != nil {
if errors.Is(err, accesscontrol.ErrResolverNotFound) {
return false, nil
}
return false, err
}
return resolvedEvaluator.Evaluate(permissions), nil
}
// GetUserPermissions returns user permissions.
// This mock return m.permissions unless an override is provided.
func (m *Mock) GetUserPermissions(ctx context.Context, user identity.Requester, opts accesscontrol.Options) ([]accesscontrol.Permission, error) {
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts})
// Use override if provided
if m.GetUserPermissionsFunc != nil {
return m.GetUserPermissionsFunc(ctx, user, opts)
}
// Otherwise return the Permissions list
return m.permissions, nil
}
func (m *Mock) GetUserPermissionsInOrg(ctx context.Context, user identity.Requester, orgID int64) ([]accesscontrol.Permission, error) {
m.Calls.GetUserPermissionsInOrg = append(m.Calls.GetUserPermissionsInOrg, []interface{}{ctx, user, orgID})
// Use override if provided
if m.GetUserPermissionsInOrgFunc != nil {
return m.GetUserPermissionsInOrgFunc(ctx, user, orgID)
}
// Otherwise return the Permissions list
return m.permissions, nil
}
func (m *Mock) ClearUserPermissionCache(user identity.Requester) {
m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user})
// Use override if provided
if m.ClearUserPermissionCacheFunc != nil {
m.ClearUserPermissionCacheFunc(user)
}
}
// DeclareFixedRoles allow the caller to declare, to the service, fixed roles and their
// assignments to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
// This mock returns no error unless an override is provided.
func (m *Mock) DeclareFixedRoles(registrations ...accesscontrol.RoleRegistration) error {
m.Calls.DeclareFixedRoles = append(m.Calls.DeclareFixedRoles, []interface{}{registrations})
// Use override if provided
if m.DeclareFixedRolesFunc != nil {
return m.DeclareFixedRolesFunc(registrations...)
}
return nil
}
// RegisterFixedRoles registers all roles declared to AccessControl
// This mock returns no error unless an override is provided.
func (m *Mock) RegisterFixedRoles(ctx context.Context) error {
m.Calls.RegisterFixedRoles = append(m.Calls.RegisterFixedRoles, []struct{}{})
// Use override if provided
if m.RegisterFixedRolesFunc != nil {
return m.RegisterFixedRolesFunc()
}
return nil
}
// DeclarePluginRoles allow the caller to declare, to the service, plugin roles and their
// assignments to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
// This mock returns no error unless an override is provided.
func (m *Mock) DeclarePluginRoles(ctx context.Context, ID, name string, regs []plugins.RoleRegistration) error {
m.Calls.DeclarePluginRoles = append(m.Calls.DeclarePluginRoles, []interface{}{ctx, ID, name, regs})
// Use override if provided
if m.DeclarePluginRolesFunc != nil {
return m.DeclarePluginRolesFunc(ctx, ID, name, regs)
}
return nil
}
func (m *Mock) RegisterScopeAttributeResolver(scopePrefix string, resolver accesscontrol.ScopeAttributeResolver) {
m.scopeResolvers.AddScopeAttributeResolver(scopePrefix, resolver)
m.Calls.RegisterAttributeScopeResolver = append(m.Calls.RegisterAttributeScopeResolver, []struct{}{})
// Use override if provided
if m.RegisterScopeAttributeResolverFunc != nil {
m.RegisterScopeAttributeResolverFunc(scopePrefix, resolver)
}
}
func (m *Mock) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error {
m.Calls.DeleteUserPermissions = append(m.Calls.DeleteUserPermissions, []interface{}{ctx, orgID, userID})
// Use override if provided
if m.DeleteUserPermissionsFunc != nil {
return m.DeleteUserPermissionsFunc(ctx, userID)
}
return nil
}
func (m *Mock) DeleteTeamPermissions(ctx context.Context, orgID, teamID int64) error {
m.Calls.DeleteTeamPermissions = append(m.Calls.DeleteTeamPermissions, []interface{}{ctx, orgID, teamID})
// Use override if provided
if m.DeleteTeamPermissionsFunc != nil {
return m.DeleteTeamPermissionsFunc(ctx, teamID)
}
return nil
}
// SearchUsersPermissions returns all users' permissions filtered by an action prefix
func (m *Mock) SearchUsersPermissions(ctx context.Context, usr identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
user := usr.(*user.SignedInUser)
m.Calls.SearchUsersPermissions = append(m.Calls.SearchUsersPermissions, []interface{}{ctx, user, options})
// Use override if provided
if m.SearchUsersPermissionsFunc != nil {
return m.SearchUsersPermissionsFunc(ctx, user, usr.GetOrgID(), options)
}
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
}
func (m *Mock) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error {
m.Calls.SaveExternalServiceRole = append(m.Calls.SaveExternalServiceRole, []interface{}{ctx, cmd})
// Use override if provided
if m.SaveExternalServiceRoleFunc != nil {
return m.SaveExternalServiceRoleFunc(ctx, cmd)
}
return nil
}
func (m *Mock) DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error {
m.Calls.DeleteExternalServiceRole = append(m.Calls.DeleteExternalServiceRole, []interface{}{ctx, externalServiceID})
// Use override if provided
if m.DeleteExternalServiceRoleFunc != nil {
return m.DeleteExternalServiceRoleFunc(ctx, externalServiceID)
}
return nil
}
func (m *Mock) SyncUserRoles(ctx context.Context, orgID int64, cmd accesscontrol.SyncUserRolesCommand) error {
if m.SyncUserRolesFunc != nil {
return m.SyncUserRolesFunc(ctx, orgID, cmd)
}
return nil
}