ext jwt client: map k8s-style to rbac permissions (#106279)
* initial commit * Proposal Co-Authored-By: mohammad-hamid <mohammad.hamid@grafana.com> * extend k8s-style mapper - add tests * address comments * cleanup * address comments --------- Co-authored-by: Gabriel Mabille <gabriel.mabille@grafana.com>
This commit is contained in:
co-authored by
Gabriel Mabille
parent
67f50478d9
commit
936dd05eac
@@ -73,9 +73,11 @@ type FetchPermissionsParams struct {
|
||||
RestrictedActions []string
|
||||
// AllowedActions will be added to the identity permissions
|
||||
AllowedActions []string
|
||||
// Note: Kept for backwards compatibility, use AllowedActions instead
|
||||
// Note: Kept for backwards compatibility, use K8s style instead
|
||||
// Roles permissions will be directly added to the identity permissions
|
||||
Roles []string
|
||||
// K8s stores Kubernetes-style permissions in the format "resource:action"
|
||||
K8s []string
|
||||
}
|
||||
|
||||
type (
|
||||
|
||||
@@ -3,6 +3,7 @@ package sync
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/permreg"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
rbac "github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
)
|
||||
@@ -28,6 +30,7 @@ func ProvideRBACSync(acService accesscontrol.Service, tracer tracing.Tracer, per
|
||||
log: log.New("permissions.sync"),
|
||||
permRegistry: permRegistry,
|
||||
tracer: tracer,
|
||||
mapper: rbac.NewMapperRegistry(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +39,7 @@ type RBACSync struct {
|
||||
permRegistry permreg.PermissionRegistry
|
||||
log log.Logger
|
||||
tracer tracing.Tracer
|
||||
mapper rbac.MapperRegistry
|
||||
}
|
||||
|
||||
func (s *RBACSync) SyncPermissionsHook(ctx context.Context, ident *authn.Identity, _ *authn.Request) error {
|
||||
@@ -81,7 +85,8 @@ func (s *RBACSync) fetchPermissions(ctx context.Context, ident *authn.Identity)
|
||||
permissions := make([]accesscontrol.Permission, 0, 8)
|
||||
roles := ident.ClientParams.FetchPermissionsParams.Roles
|
||||
actions := ident.ClientParams.FetchPermissionsParams.AllowedActions
|
||||
if len(roles) > 0 || len(actions) > 0 {
|
||||
k8s := ident.ClientParams.FetchPermissionsParams.K8s
|
||||
if len(roles) > 0 || len(actions) > 0 || len(k8s) > 0 {
|
||||
for _, role := range roles {
|
||||
roleDTO, err := s.ac.GetRoleByName(ctx, ident.GetOrgID(), role)
|
||||
if err != nil && !errors.Is(err, accesscontrol.ErrRoleNotFound) {
|
||||
@@ -93,19 +98,11 @@ func (s *RBACSync) fetchPermissions(ctx context.Context, ident *authn.Identity)
|
||||
}
|
||||
}
|
||||
for _, action := range actions {
|
||||
scopes, ok := s.permRegistry.GetScopePrefixes(action)
|
||||
if !ok {
|
||||
s.log.Warn("Unknown action scopes", "action", action)
|
||||
continue
|
||||
}
|
||||
if len(scopes) == 0 {
|
||||
permissions = append(permissions, accesscontrol.Permission{Action: action})
|
||||
continue
|
||||
}
|
||||
for scope := range scopes {
|
||||
permissions = append(permissions, accesscontrol.Permission{Action: action, Scope: scope + "*"})
|
||||
}
|
||||
s.addPermissionsForAction(action, &permissions)
|
||||
}
|
||||
// Add K8s permissions
|
||||
k8sPermissions := s.translateK8sPermissions(ctx, k8s)
|
||||
permissions = append(permissions, k8sPermissions...)
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
@@ -117,6 +114,88 @@ func (s *RBACSync) fetchPermissions(ctx context.Context, ident *authn.Identity)
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// addPermissionsForAction is a helper method that handles the common pattern of:
|
||||
// 1. Getting scope prefixes for an action
|
||||
// 2. Adding permissions with appropriate scopes
|
||||
// 3. Logging warnings for unknown actions
|
||||
func (s *RBACSync) addPermissionsForAction(action string, permissions *[]accesscontrol.Permission) {
|
||||
scopes, ok := s.permRegistry.GetScopePrefixes(action)
|
||||
if !ok {
|
||||
s.log.Warn("Unknown action scopes", "action", action)
|
||||
return
|
||||
}
|
||||
if len(scopes) == 0 {
|
||||
*permissions = append(*permissions, accesscontrol.Permission{Action: action})
|
||||
return
|
||||
}
|
||||
for scope := range scopes {
|
||||
*permissions = append(*permissions, accesscontrol.Permission{Action: action, Scope: scope + "*"})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RBACSync) translateK8sPermissions(_ context.Context, k8sPerms []string) []accesscontrol.Permission {
|
||||
permissions := make([]accesscontrol.Permission, 0, len(k8sPerms))
|
||||
for _, k8sPerm := range k8sPerms {
|
||||
parts := strings.Split(k8sPerm, ":")
|
||||
if len(parts) != 2 {
|
||||
s.log.Warn("Invalid K8s permission format", "permission", k8sPerm)
|
||||
continue
|
||||
}
|
||||
groupResource := strings.Split(parts[0], "/")
|
||||
group := groupResource[0]
|
||||
verb := parts[1]
|
||||
switch {
|
||||
case len(groupResource) == 1:
|
||||
// Case group:verb
|
||||
resourceMappings := s.mapper.GetAll(group)
|
||||
if len(resourceMappings) == 0 {
|
||||
s.log.Warn("No mappings found for group", "group", group)
|
||||
continue
|
||||
}
|
||||
for _, mapping := range resourceMappings {
|
||||
if verb == "*" {
|
||||
actions := mapping.AllActions()
|
||||
for _, action := range actions {
|
||||
s.addPermissionsForAction(action, &permissions)
|
||||
}
|
||||
continue
|
||||
}
|
||||
action, ok := mapping.Action(verb)
|
||||
if !ok {
|
||||
s.log.Warn("Unknown K8s verb for group", "group", group, "verb", verb)
|
||||
continue
|
||||
}
|
||||
s.addPermissionsForAction(action, &permissions)
|
||||
}
|
||||
case len(groupResource) == 2:
|
||||
// Case group/resource:verb
|
||||
resource := groupResource[1]
|
||||
resourceMappings, ok := s.mapper.Get(group, resource)
|
||||
if !ok {
|
||||
s.log.Warn("Unknown K8s resource", "group", group, "resource", resource)
|
||||
continue
|
||||
}
|
||||
if verb == "*" {
|
||||
actions := resourceMappings.AllActions()
|
||||
for _, action := range actions {
|
||||
s.addPermissionsForAction(action, &permissions)
|
||||
}
|
||||
continue
|
||||
}
|
||||
action, ok := resourceMappings.Action(verb)
|
||||
if !ok {
|
||||
s.log.Warn("Unknown K8s verb", "group", group, "resource", resource, "verb", verb)
|
||||
continue
|
||||
}
|
||||
s.addPermissionsForAction(action, &permissions)
|
||||
default:
|
||||
s.log.Warn("Invalid K8s permission format", "permission", k8sPerm)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return permissions
|
||||
}
|
||||
|
||||
func cloudRolesToAddAndRemove(ident *authn.Identity) ([]string, []string, error) {
|
||||
// Since Cloud Admin/Editor/Viewer roles are not yet implemented one-to-one in the Grafana, it becomes a confusing experience for users,
|
||||
// therefore we are doing granular mapping of all available functionality in the Grafana temporary.
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
permreg "github.com/grafana/grafana/pkg/services/accesscontrol/permreg/test"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
rbac "github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
)
|
||||
@@ -421,6 +422,135 @@ func TestRBACSync_ClearUserPermissionCacheHook(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRBACSync_translateK8sPermissions(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
k8sPerms []string
|
||||
expectedPerms []accesscontrol.Permission
|
||||
expectedError error
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "should translate folder.grafana.app/folders:get to folders:read",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app/folders:get",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{
|
||||
{Action: "folders:read", Scope: "folders:uid:*"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should translate resource with wildcard verb",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app/folders:*",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{
|
||||
{Action: "folders:read", Scope: "folders:uid:*"},
|
||||
{Action: "folders:write", Scope: "folders:uid:*"},
|
||||
{Action: "folders:delete", Scope: "folders:uid:*"},
|
||||
{Action: "folders:create", Scope: "folders:uid:*"},
|
||||
{Action: "folders.permissions:read", Scope: "folders:uid:*"},
|
||||
{Action: "folders.permissions:write", Scope: "folders:uid:*"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle invalid permission format",
|
||||
k8sPerms: []string{
|
||||
"invalid-format",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle unknown resource",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app/unknown:get",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle unknown verb",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app/folders:unknown",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle group:verb format",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app:get",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{
|
||||
{Action: "folders:read", Scope: "folders:uid:*"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle group:* format",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app:*",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{
|
||||
{Action: "folders:read", Scope: "folders:uid:*"},
|
||||
{Action: "folders:write", Scope: "folders:uid:*"},
|
||||
{Action: "folders:delete", Scope: "folders:uid:*"},
|
||||
{Action: "folders:create", Scope: "folders:uid:*"},
|
||||
{Action: "folders.permissions:read", Scope: "folders:uid:*"},
|
||||
{Action: "folders.permissions:write", Scope: "folders:uid:*"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle unknown group in group:verb format",
|
||||
k8sPerms: []string{
|
||||
"unknown.grafana.app:get",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle unknown verb in group:verb format",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app:unknownverb",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should handle multiple group:verb permissions",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app:get",
|
||||
"folder.grafana.app:create",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{
|
||||
{Action: "folders:read", Scope: "folders:uid:*"},
|
||||
{Action: "folders:create", Scope: "folders:uid:*"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "should skip invalid permissions group/resource/additional/part:verb",
|
||||
k8sPerms: []string{
|
||||
"folder.grafana.app/folder/fold1/subresource:get",
|
||||
},
|
||||
expectedPerms: []accesscontrol.Permission{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := setupTestEnv(t)
|
||||
perms := s.translateK8sPermissions(context.Background(), tt.k8sPerms)
|
||||
assert.ElementsMatch(t, tt.expectedPerms, perms)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestEnv(t *testing.T) *RBACSync {
|
||||
acMock := &acmock.Mock{
|
||||
GetUserPermissionsFunc: func(ctx context.Context, siu identity.Requester, o accesscontrol.Options) ([]accesscontrol.Permission, error) {
|
||||
@@ -446,11 +576,19 @@ func setupTestEnv(t *testing.T) *RBACSync {
|
||||
},
|
||||
}
|
||||
permRegistry := permreg.ProvidePermissionRegistry(t)
|
||||
|
||||
require.NoError(t, permRegistry.RegisterPermission("folders:write", "folders:uid:"))
|
||||
require.NoError(t, permRegistry.RegisterPermission("folders:create", "folders:uid:"))
|
||||
require.NoError(t, permRegistry.RegisterPermission("folders:delete", "folders:uid:"))
|
||||
require.NoError(t, permRegistry.RegisterPermission("folders.permissions:read", "folders:uid:"))
|
||||
require.NoError(t, permRegistry.RegisterPermission("folders.permissions:write", "folders:uid:"))
|
||||
|
||||
s := &RBACSync{
|
||||
ac: acMock,
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
permRegistry: permRegistry,
|
||||
mapper: rbac.NewMapperRegistry(),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -167,9 +167,14 @@ func (s *ExtendedJWT) authenticateAsService(accessTokenClaims authlib.Claims[aut
|
||||
if len(permissions) > 0 {
|
||||
fetchPermissionsParams.Roles = make([]string, 0, len(permissions))
|
||||
fetchPermissionsParams.AllowedActions = make([]string, 0, len(permissions))
|
||||
fetchPermissionsParams.K8s = make([]string, 0, len(permissions))
|
||||
|
||||
for i := range permissions {
|
||||
if strings.HasPrefix(permissions[i], "fixed:") {
|
||||
fetchPermissionsParams.Roles = append(fetchPermissionsParams.Roles, permissions[i])
|
||||
} else if strings.Contains(permissions[i], "grafana.app") {
|
||||
// Check for pattern <resource>.grafana.app/<resource>:<action>
|
||||
fetchPermissionsParams.K8s = append(fetchPermissionsParams.K8s, permissions[i])
|
||||
} else {
|
||||
fetchPermissionsParams.AllowedActions = append(fetchPermissionsParams.AllowedActions, permissions[i])
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
|
||||
AuthID: "access-policy:this-uid",
|
||||
ClientParams: authn.ClientParams{
|
||||
SyncPermissions: true,
|
||||
FetchPermissionsParams: authn.FetchPermissionsParams{Roles: []string{"fixed:folders:reader"}, AllowedActions: []string{"folders:read"}}},
|
||||
FetchPermissionsParams: authn.FetchPermissionsParams{Roles: []string{"fixed:folders:reader"}, AllowedActions: []string{"folders:read"}, K8s: []string{}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,6 +6,21 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
)
|
||||
|
||||
// Mapping maps a verb to a RBAC action and a resource name to a RBAC scope.
|
||||
type Mapping interface {
|
||||
// action returns the action for the given verb.
|
||||
// If no action is found, it returns false.
|
||||
Action(verb string) (string, bool)
|
||||
// scope returns the scope for the given resource name.
|
||||
Scope(name string) string
|
||||
// prefix returns the scope prefix for the translation.
|
||||
Prefix() string
|
||||
// AllActions returns all the actions for the translation.
|
||||
AllActions() []string
|
||||
// HasFolderSupport returns true if the translation supports folders.
|
||||
HasFolderSupport() bool
|
||||
}
|
||||
|
||||
type translation struct {
|
||||
resource string
|
||||
attribute string
|
||||
@@ -13,19 +28,47 @@ type translation struct {
|
||||
folderSupport bool
|
||||
}
|
||||
|
||||
func (t translation) action(verb string) (string, bool) {
|
||||
func (t translation) Action(verb string) (string, bool) {
|
||||
action, ok := t.verbMapping[verb]
|
||||
return action, ok
|
||||
}
|
||||
|
||||
func (t translation) scope(name string) string {
|
||||
func (t translation) Scope(name string) string {
|
||||
return t.resource + ":" + t.attribute + ":" + name
|
||||
}
|
||||
|
||||
func (t translation) prefix() string {
|
||||
func (t translation) Prefix() string {
|
||||
return t.resource + ":" + t.attribute + ":"
|
||||
}
|
||||
|
||||
func (t translation) AllActions() []string {
|
||||
actions := make([]string, 0, len(t.verbMapping))
|
||||
actionsMap := make(map[string]bool)
|
||||
for _, action := range t.verbMapping {
|
||||
if actionsMap[action] {
|
||||
continue
|
||||
}
|
||||
actionsMap[action] = true
|
||||
actions = append(actions, action)
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func (t translation) HasFolderSupport() bool {
|
||||
return t.folderSupport
|
||||
}
|
||||
|
||||
// MapperRegistry is a registry of mappers that maps a group and resource to a translation.
|
||||
type MapperRegistry interface {
|
||||
// Get returns the permission mapper for the given group and resource.
|
||||
// If no translation is found, it returns false.
|
||||
Get(group, resource string) (Mapping, bool)
|
||||
// GetAll returns all the translations for the given group
|
||||
GetAll(group string) []Mapping
|
||||
}
|
||||
|
||||
type mapper map[string]map[string]translation
|
||||
|
||||
func newResourceTranslation(resource string, attribute string, folderSupport bool) translation {
|
||||
defaultMapping := func(r string) map[string]string {
|
||||
return map[string]string{
|
||||
@@ -50,10 +93,8 @@ func newResourceTranslation(resource string, attribute string, folderSupport boo
|
||||
}
|
||||
}
|
||||
|
||||
type mapper map[string]map[string]translation
|
||||
|
||||
func newMapper() mapper {
|
||||
return map[string]map[string]translation{
|
||||
func NewMapperRegistry() MapperRegistry {
|
||||
mapper := mapper(map[string]map[string]translation{
|
||||
"dashboard.grafana.app": {
|
||||
"dashboards": newResourceTranslation("dashboards", "uid", true),
|
||||
},
|
||||
@@ -77,19 +118,35 @@ func newMapper() mapper {
|
||||
folderSupport: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return mapper
|
||||
}
|
||||
|
||||
func (m mapper) translation(group, resource string) (translation, bool) {
|
||||
func (m mapper) Get(group, resource string) (Mapping, bool) {
|
||||
resources, ok := m[group]
|
||||
if !ok {
|
||||
return translation{}, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
t, ok := resources[resource]
|
||||
if !ok {
|
||||
return translation{}, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return t, true
|
||||
return &t, true
|
||||
}
|
||||
|
||||
func (m mapper) GetAll(group string) []Mapping {
|
||||
resources, ok := m[group]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
translations := make([]Mapping, 0, len(resources))
|
||||
for _, t := range resources {
|
||||
translations = append(translations, &t)
|
||||
}
|
||||
|
||||
return translations
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ type Service struct {
|
||||
identityStore legacy.LegacyIdentityStore
|
||||
settings Settings
|
||||
|
||||
mapper mapper
|
||||
mapper MapperRegistry
|
||||
|
||||
logger log.Logger
|
||||
tracer tracing.Tracer
|
||||
@@ -93,7 +93,7 @@ func NewService(
|
||||
logger: logger,
|
||||
tracer: tracer,
|
||||
metrics: newMetrics(reg),
|
||||
mapper: newMapper(),
|
||||
mapper: NewMapperRegistry(),
|
||||
idCache: newCacheWrap[store.UserIdentifiers](cache, logger, tracer, longCacheTTL),
|
||||
permCache: newCacheWrap[map[string]bool](cache, logger, tracer, settings.CacheTTL),
|
||||
permDenialCache: newCacheWrap[bool](cache, logger, tracer, settings.CacheTTL),
|
||||
@@ -334,13 +334,13 @@ func (s *Service) validateSubject(ctx context.Context, subject string) (string,
|
||||
func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, error) {
|
||||
ctxLogger := s.logger.FromContext(ctx)
|
||||
|
||||
t, ok := s.mapper.translation(group, resource)
|
||||
t, ok := s.mapper.Get(group, resource)
|
||||
if !ok {
|
||||
ctxLogger.Error("unsupport resource", "group", group, "resource", resource)
|
||||
return "", status.Error(codes.NotFound, "unsupported resource")
|
||||
}
|
||||
|
||||
action, ok := t.action(verb)
|
||||
action, ok := t.Action(verb)
|
||||
if !ok {
|
||||
ctxLogger.Error("unsupport verb", "group", group, "resource", resource, "verb", verb)
|
||||
return "", status.Error(codes.NotFound, "unsupported verb")
|
||||
@@ -585,17 +585,17 @@ func (s *Service) checkPermission(ctx context.Context, scopeMap map[string]bool,
|
||||
return true, nil
|
||||
}
|
||||
|
||||
t, ok := s.mapper.translation(req.Group, req.Resource)
|
||||
t, ok := s.mapper.Get(req.Group, req.Resource)
|
||||
if !ok {
|
||||
ctxLogger.Error("unsupport resource", "group", req.Group, "resource", req.Resource)
|
||||
return false, status.Error(codes.NotFound, "unsupported resource")
|
||||
}
|
||||
|
||||
if req.Name != "" && scopeMap[t.scope(req.Name)] {
|
||||
if req.Name != "" && scopeMap[t.Scope(req.Name)] {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if !t.folderSupport {
|
||||
if !t.HasFolderSupport() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -679,14 +679,14 @@ func (s *Service) listPermission(ctx context.Context, scopeMap map[string]bool,
|
||||
defer span.End()
|
||||
ctxLogger := s.logger.FromContext(ctx)
|
||||
|
||||
t, ok := s.mapper.translation(req.Group, req.Resource)
|
||||
t, ok := s.mapper.Get(req.Group, req.Resource)
|
||||
if !ok {
|
||||
ctxLogger.Error("unsupport resource", "group", req.Group, "resource", req.Resource)
|
||||
return nil, status.Error(codes.NotFound, "unsupported resource")
|
||||
}
|
||||
|
||||
var tree folderTree
|
||||
if t.folderSupport {
|
||||
if t.HasFolderSupport() {
|
||||
var err error
|
||||
tree, err = s.buildFolderTree(ctx, req.Namespace)
|
||||
if err != nil {
|
||||
@@ -699,7 +699,7 @@ func (s *Service) listPermission(ctx context.Context, scopeMap map[string]bool,
|
||||
if strings.HasPrefix(req.Action, "folders:") {
|
||||
res = buildFolderList(scopeMap, tree)
|
||||
} else {
|
||||
res = buildItemList(scopeMap, tree, t.prefix())
|
||||
res = buildItemList(scopeMap, tree, t.Prefix())
|
||||
}
|
||||
|
||||
span.SetAttributes(attribute.Int("num_folders", len(res.Folders)), attribute.Int("num_items", len(res.Items)))
|
||||
|
||||
@@ -1519,7 +1519,7 @@ func setupService() *Service {
|
||||
tracer := tracing.NewNoopTracerService()
|
||||
return &Service{
|
||||
logger: logger,
|
||||
mapper: newMapper(),
|
||||
mapper: NewMapperRegistry(),
|
||||
tracer: tracer,
|
||||
metrics: newMetrics(nil),
|
||||
idCache: newCacheWrap[store.UserIdentifiers](cache, logger, tracer, longCacheTTL),
|
||||
|
||||
Reference in New Issue
Block a user