338a41f178
add single flight groups for user and anonymous permission checking
730 lines
21 KiB
Go
730 lines
21 KiB
Go
package rbac
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/grafana/authlib/claims"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sync/singleflight"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/authz/mappers"
|
|
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
|
)
|
|
|
|
func TestService_checkPermission(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
permissions []accesscontrol.Permission
|
|
check CheckRequest
|
|
expected bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return true if user has permission",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:uid:some_dashboard",
|
|
Kind: "dashboards",
|
|
Attribute: "uid",
|
|
Identifier: "some_dashboard",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "should return false if user has permission on a different resource",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:uid:another_dashboard",
|
|
Kind: "dashboards",
|
|
Attribute: "uid",
|
|
Identifier: "another_dashboard",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "should return true if user has wildcard permission on identifier",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:uid:*",
|
|
Kind: "dashboards",
|
|
Attribute: "uid",
|
|
Identifier: "*",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "should return true if user has wildcard permission on attribute",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:*",
|
|
Kind: "dashboards",
|
|
Attribute: "*",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "should return true if user has wildcard permission on kind",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "*",
|
|
Kind: "*",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "should return true if no resource is specified",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "folders:create",
|
|
},
|
|
},
|
|
check: CheckRequest{
|
|
Action: "folders:create",
|
|
Group: "folder.grafana.app",
|
|
Resource: "folders",
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "should return false if user has no permissions on resource",
|
|
permissions: []accesscontrol.Permission{},
|
|
check: CheckRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
Name: "some_dashboard",
|
|
},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s := &Service{logger: log.New("test"), actionMapper: mappers.NewK8sRbacMapper(), tracer: tracing.NewNoopTracerService()}
|
|
got, err := s.checkPermission(context.Background(), getScopeMap(tc.permissions), &tc.check)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expected, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_getUserTeams(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
teams []int64
|
|
cacheHit bool
|
|
expectedTeams []int64
|
|
expectedError bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return teams from cache if available",
|
|
teams: []int64{1, 2},
|
|
cacheHit: true,
|
|
expectedTeams: []int64{1, 2},
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "should return teams from identity store if not in cache",
|
|
teams: []int64{3, 4},
|
|
cacheHit: false,
|
|
expectedTeams: []int64{3, 4},
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "should return error if identity store fails",
|
|
teams: []int64{},
|
|
cacheHit: false,
|
|
expectedTeams: nil,
|
|
expectedError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
ns := claims.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12}
|
|
|
|
userIdentifiers := &store.UserIdentifiers{UID: "test-uid"}
|
|
identityStore := &fakeIdentityStore{teams: tc.teams, err: tc.expectedError}
|
|
|
|
cacheService := localcache.New(shortCacheTTL, shortCleanupInterval)
|
|
if tc.cacheHit {
|
|
cacheService.Set(userTeamCacheKey(ns.Value, userIdentifiers.UID), tc.expectedTeams, 0)
|
|
}
|
|
|
|
s := &Service{
|
|
teamCache: cacheService,
|
|
identityStore: identityStore,
|
|
logger: log.New("test"),
|
|
tracer: tracing.NewNoopTracerService(),
|
|
}
|
|
|
|
teams, err := s.getUserTeams(ctx, ns, userIdentifiers)
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.expectedTeams, teams)
|
|
if tc.cacheHit {
|
|
require.Zero(t, identityStore.calls)
|
|
} else {
|
|
require.Equal(t, 1, identityStore.calls)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_getUserBasicRole(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
basicRole store.BasicRole
|
|
cacheHit bool
|
|
expectedRole store.BasicRole
|
|
expectedError bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return basic role from cache if available",
|
|
basicRole: store.BasicRole{
|
|
Role: "viewer",
|
|
IsAdmin: false,
|
|
},
|
|
cacheHit: true,
|
|
expectedRole: store.BasicRole{
|
|
Role: "viewer",
|
|
IsAdmin: false,
|
|
},
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "should return basic role from store if not in cache",
|
|
basicRole: store.BasicRole{
|
|
Role: "editor",
|
|
IsAdmin: false,
|
|
},
|
|
cacheHit: false,
|
|
expectedRole: store.BasicRole{
|
|
Role: "editor",
|
|
IsAdmin: false,
|
|
},
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "should return error if store fails",
|
|
basicRole: store.BasicRole{},
|
|
cacheHit: false,
|
|
expectedRole: store.BasicRole{},
|
|
expectedError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
ns := claims.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12}
|
|
|
|
userIdentifiers := &store.UserIdentifiers{UID: "test-uid", ID: 1}
|
|
store := &fakeStore{basicRole: &tc.basicRole, err: tc.expectedError}
|
|
|
|
cacheService := localcache.New(shortCacheTTL, shortCleanupInterval)
|
|
if tc.cacheHit {
|
|
cacheService.Set(userBasicRoleCacheKey(ns.Value, userIdentifiers.UID), tc.expectedRole, 0)
|
|
}
|
|
|
|
s := &Service{
|
|
basicRoleCache: cacheService,
|
|
store: store,
|
|
logger: log.New("test"),
|
|
tracer: tracing.NewNoopTracerService(),
|
|
}
|
|
|
|
role, err := s.getUserBasicRole(ctx, ns, userIdentifiers)
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expectedRole, role)
|
|
if tc.cacheHit {
|
|
require.Zero(t, store.calls)
|
|
} else {
|
|
require.Equal(t, 1, store.calls)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_getUserPermissions(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
permissions []accesscontrol.Permission
|
|
cacheHit bool
|
|
expectedPerms map[string]bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return permissions from cache if available",
|
|
permissions: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"},
|
|
},
|
|
cacheHit: true,
|
|
expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true},
|
|
},
|
|
{
|
|
name: "should return permissions from store if not in cache",
|
|
permissions: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"},
|
|
},
|
|
cacheHit: false,
|
|
expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true},
|
|
},
|
|
{
|
|
name: "should return error if store fails",
|
|
permissions: nil,
|
|
cacheHit: false,
|
|
expectedPerms: map[string]bool{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
userID := &store.UserIdentifiers{UID: "test-uid", ID: 112}
|
|
ns := claims.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12}
|
|
action := "dashboards:read"
|
|
|
|
cacheService := localcache.New(shortCacheTTL, shortCleanupInterval)
|
|
if tc.cacheHit {
|
|
cacheService.Set(userPermCacheKey(ns.Value, userID.UID, action), tc.expectedPerms, 0)
|
|
}
|
|
|
|
store := &fakeStore{
|
|
userID: userID,
|
|
basicRole: &store.BasicRole{Role: "viewer", IsAdmin: false},
|
|
userPermissions: tc.permissions,
|
|
}
|
|
|
|
s := &Service{
|
|
store: store,
|
|
identityStore: &fakeIdentityStore{teams: []int64{1, 2}},
|
|
actionMapper: mappers.NewK8sRbacMapper(),
|
|
logger: log.New("test"),
|
|
tracer: tracing.NewNoopTracerService(),
|
|
idCache: localcache.New(longCacheTTL, longCleanupInterval),
|
|
permCache: cacheService,
|
|
sf: new(singleflight.Group),
|
|
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
|
|
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
|
}
|
|
|
|
perms, err := s.getUserPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
|
require.NoError(t, err)
|
|
require.Len(t, perms, len(tc.expectedPerms))
|
|
for _, perm := range tc.permissions {
|
|
_, ok := tc.expectedPerms[perm.Scope]
|
|
require.True(t, ok)
|
|
}
|
|
if tc.cacheHit {
|
|
require.Equal(t, 1, store.calls) // only get user id
|
|
} else {
|
|
require.Equal(t, 3, store.calls) // get user id, basic role, and permissions
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_buildFolderTree(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
folders []store.Folder
|
|
cacheHit bool
|
|
expectedTree map[string]FolderNode
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return folder tree from cache if available",
|
|
folders: []store.Folder{
|
|
{UID: "folder1", ParentUID: nil},
|
|
{UID: "folder2", ParentUID: strPtr("folder1")},
|
|
},
|
|
cacheHit: true,
|
|
expectedTree: map[string]FolderNode{
|
|
"folder1": {uid: "folder1", childrenUIDs: []string{"folder2"}},
|
|
"folder2": {uid: "folder2", parentUID: strPtr("folder1")},
|
|
},
|
|
},
|
|
{
|
|
name: "should return folder tree from store if not in cache",
|
|
folders: []store.Folder{
|
|
{UID: "folder1", ParentUID: nil},
|
|
{UID: "folder2", ParentUID: strPtr("folder1")},
|
|
},
|
|
cacheHit: false,
|
|
expectedTree: map[string]FolderNode{
|
|
"folder1": {uid: "folder1", childrenUIDs: []string{"folder2"}},
|
|
"folder2": {uid: "folder2", parentUID: strPtr("folder1")},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
ns := claims.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12}
|
|
|
|
cacheService := localcache.New(shortCacheTTL, shortCleanupInterval)
|
|
if tc.cacheHit {
|
|
cacheService.Set(folderCacheKey(ns.Value), tc.expectedTree, 0)
|
|
}
|
|
|
|
store := &fakeStore{folders: tc.folders}
|
|
|
|
s := &Service{
|
|
store: store,
|
|
folderCache: cacheService,
|
|
logger: log.New("test"),
|
|
sf: new(singleflight.Group),
|
|
tracer: tracing.NewNoopTracerService(),
|
|
}
|
|
|
|
tree, err := s.buildFolderTree(ctx, ns)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, tree, len(tc.expectedTree))
|
|
for _, folder := range tc.folders {
|
|
node, ok := tree[folder.UID]
|
|
require.True(t, ok)
|
|
// Check parent
|
|
if folder.ParentUID != nil {
|
|
require.NotNil(t, node.parentUID)
|
|
require.Equal(t, *folder.ParentUID, *node.parentUID)
|
|
} else {
|
|
require.Nil(t, node.parentUID)
|
|
}
|
|
// Check children
|
|
if len(node.childrenUIDs) > 0 {
|
|
epectedChildren := tc.expectedTree[folder.UID].childrenUIDs
|
|
require.ElementsMatch(t, node.childrenUIDs, epectedChildren)
|
|
}
|
|
}
|
|
if tc.cacheHit {
|
|
require.Zero(t, store.calls)
|
|
} else {
|
|
require.Equal(t, 1, store.calls)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_listPermission(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
permissions []accesscontrol.Permission
|
|
folderTree map[string]FolderNode
|
|
list ListRequest
|
|
expectedDashboards []string
|
|
expectedFolders []string
|
|
expectedAll bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "should return wildcard if user has a wildcard permission",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "*",
|
|
Kind: "*",
|
|
},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
expectedAll: true,
|
|
},
|
|
{
|
|
name: "should return dashboards and folders that user has direct access to",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:uid:some_dashboard",
|
|
Kind: "dashboards",
|
|
Attribute: "uid",
|
|
Identifier: "some_dashboard",
|
|
},
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_1",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_1",
|
|
},
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_2",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_2",
|
|
},
|
|
},
|
|
folderTree: map[string]FolderNode{
|
|
"some_folder_1": {uid: "some_folder_1"},
|
|
"some_folder_2": {uid: "some_folder_2"},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
expectedDashboards: []string{"some_dashboard"},
|
|
expectedFolders: []string{"some_folder_1", "some_folder_2"},
|
|
},
|
|
{
|
|
name: "should return folders that user has inherited access to",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_parent",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_1",
|
|
},
|
|
},
|
|
folderTree: map[string]FolderNode{
|
|
"some_folder_parent": {uid: "some_folder_parent", childrenUIDs: []string{"some_folder_child"}},
|
|
"some_folder_child": {uid: "some_folder_child", parentUID: strPtr("some_folder_parent"), childrenUIDs: []string{"some_folder_subchild1", "some_folder_subchild2"}},
|
|
"some_folder_subchild1": {uid: "some_folder_subchild1", parentUID: strPtr("some_folder_child")},
|
|
"some_folder_subchild2": {uid: "some_folder_subchild2", parentUID: strPtr("some_folder_child"), childrenUIDs: []string{"some_folder_subsubchild"}},
|
|
"some_folder_subsubchild": {uid: "some_folder_subsubchild", parentUID: strPtr("some_folder_subchild2")},
|
|
"some_folder_1": {uid: "some_folder_1", parentUID: strPtr("some_other_folder")},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
expectedFolders: []string{"some_folder_parent", "some_folder_child", "some_folder_subchild1", "some_folder_subchild2", "some_folder_subsubchild"},
|
|
},
|
|
{
|
|
name: "should return folders that user has inherited access to as well as dashboards that user has direct access to",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "dashboards:uid:some_dashboard",
|
|
Kind: "dashboards",
|
|
Attribute: "uid",
|
|
Identifier: "some_dashboard",
|
|
},
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_parent",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_parent",
|
|
},
|
|
},
|
|
folderTree: map[string]FolderNode{
|
|
"some_folder_parent": {uid: "some_folder_parent", childrenUIDs: []string{"some_folder_child"}},
|
|
"some_folder_child": {uid: "some_folder_child", parentUID: strPtr("some_folder_parent")},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
expectedDashboards: []string{"some_dashboard"},
|
|
expectedFolders: []string{"some_folder_parent", "some_folder_child"},
|
|
},
|
|
{
|
|
name: "should deduplicate folders that user has inherited as well as direct access to",
|
|
permissions: []accesscontrol.Permission{
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_child",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_child",
|
|
},
|
|
{
|
|
Action: "dashboards:read",
|
|
Scope: "folders:uid:some_folder_parent",
|
|
Kind: "folders",
|
|
Attribute: "uid",
|
|
Identifier: "some_folder_parent",
|
|
},
|
|
},
|
|
folderTree: map[string]FolderNode{
|
|
"some_folder_parent": {uid: "some_folder_parent", childrenUIDs: []string{"some_folder_child"}},
|
|
"some_folder_child": {uid: "some_folder_child", parentUID: strPtr("some_folder_parent"), childrenUIDs: []string{"some_folder_subchild"}},
|
|
"some_folder_subchild": {uid: "some_folder_subchild", parentUID: strPtr("some_folder_child")},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
expectedFolders: []string{"some_folder_parent", "some_folder_child", "some_folder_subchild"},
|
|
},
|
|
{
|
|
name: "return no dashboards and folders if the user doesn't have access to any resources",
|
|
permissions: []accesscontrol.Permission{},
|
|
folderTree: map[string]FolderNode{
|
|
"some_folder_1": {uid: "some_folder_1"},
|
|
},
|
|
list: ListRequest{
|
|
Action: "dashboards:read",
|
|
Group: "dashboard.grafana.app",
|
|
Resource: "dashboards",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
folderCache := localcache.New(shortCacheTTL, shortCleanupInterval)
|
|
if tc.folderTree != nil {
|
|
folderCache.Set(folderCacheKey("default"), tc.folderTree, 0)
|
|
}
|
|
s := &Service{
|
|
logger: log.New("test"),
|
|
actionMapper: mappers.NewK8sRbacMapper(),
|
|
folderCache: folderCache,
|
|
tracer: tracing.NewNoopTracerService(),
|
|
}
|
|
tc.list.Namespace = claims.NamespaceInfo{Value: "default", OrgID: 1}
|
|
got, err := s.listPermission(context.Background(), getScopeMap(tc.permissions), &tc.list)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expectedAll, got.All)
|
|
assert.ElementsMatch(t, tc.expectedDashboards, got.Items)
|
|
assert.ElementsMatch(t, tc.expectedFolders, got.Folders)
|
|
})
|
|
}
|
|
}
|
|
|
|
func strPtr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
type fakeStore struct {
|
|
store.Store
|
|
folders []store.Folder
|
|
basicRole *store.BasicRole
|
|
userID *store.UserIdentifiers
|
|
userPermissions []accesscontrol.Permission
|
|
err bool
|
|
calls int
|
|
}
|
|
|
|
func (f *fakeStore) GetBasicRoles(ctx context.Context, namespace claims.NamespaceInfo, query store.BasicRoleQuery) (*store.BasicRole, error) {
|
|
f.calls++
|
|
if f.err {
|
|
return nil, fmt.Errorf("store error")
|
|
}
|
|
return f.basicRole, nil
|
|
}
|
|
|
|
func (f *fakeStore) GetUserIdentifiers(ctx context.Context, query store.UserIdentifierQuery) (*store.UserIdentifiers, error) {
|
|
f.calls++
|
|
if f.err {
|
|
return nil, fmt.Errorf("store error")
|
|
}
|
|
return f.userID, nil
|
|
}
|
|
|
|
func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace claims.NamespaceInfo, query store.PermissionsQuery) ([]accesscontrol.Permission, error) {
|
|
f.calls++
|
|
if f.err {
|
|
return nil, fmt.Errorf("store error")
|
|
}
|
|
return f.userPermissions, nil
|
|
}
|
|
|
|
func (f *fakeStore) GetFolders(ctx context.Context, namespace claims.NamespaceInfo) ([]store.Folder, error) {
|
|
f.calls++
|
|
if f.err {
|
|
return nil, fmt.Errorf("store error")
|
|
}
|
|
return f.folders, nil
|
|
}
|
|
|
|
type fakeIdentityStore struct {
|
|
legacy.LegacyIdentityStore
|
|
teams []int64
|
|
err bool
|
|
calls int
|
|
}
|
|
|
|
func (f *fakeIdentityStore) ListUserTeams(ctx context.Context, namespace claims.NamespaceInfo, query legacy.ListUserTeamsQuery) (*legacy.ListUserTeamsResult, error) {
|
|
f.calls++
|
|
if f.err {
|
|
return nil, fmt.Errorf("identity store error")
|
|
}
|
|
items := make([]legacy.UserTeam, 0, len(f.teams))
|
|
for _, teamID := range f.teams {
|
|
items = append(items, legacy.UserTeam{ID: teamID})
|
|
}
|
|
return &legacy.ListUserTeamsResult{
|
|
Items: items,
|
|
Continue: 0,
|
|
}, nil
|
|
}
|