Anonymous access: Allow setting org role in new authz service (#103669)

* Anonymous access: Allow setting org role in new authz service

* back out change that is not needed; rename struct

* cleanup

* Fix tests

---------

Co-authored-by: Gabriel Mabille <gabriel.mabille@grafana.com>
pull/103348/head
Stephanie Hingtgen 3 months ago committed by GitHub
parent 89c70fcdcf
commit 6eba5d74e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      pkg/services/authz/rbac.go
  2. 12
      pkg/services/authz/rbac/service.go
  3. 84
      pkg/services/authz/rbac/service_test.go

@ -70,6 +70,11 @@ func ProvideAuthZClient(
default:
sql := legacysql.NewDatabaseProvider(db)
rbacSettings := rbac.Settings{}
if cfg != nil {
rbacSettings.AnonOrgRole = cfg.Anonymous.OrgRole
}
// Register the server
server := rbac.NewService(
sql,
@ -86,6 +91,7 @@ func ProvideAuthZClient(
tracer,
reg,
cache.NewLocalCache(cache.Config{Expiry: 5 * time.Minute, CleanupInterval: 10 * time.Minute}),
rbacSettings,
)
channel := &inprocgrpc.Channel{}
@ -209,6 +215,7 @@ func RegisterRBACAuthZService(
tracer,
reg,
cache,
rbac.Settings{}, // anonymous org role can only be set in-proc
)
srv := handler.GetServer()

@ -42,6 +42,7 @@ type Service struct {
folderStore store.FolderStore
permissionStore store.PermissionStore
identityStore legacy.LegacyIdentityStore
settings Settings
mapper mapper
@ -61,6 +62,10 @@ type Service struct {
folderCache *cacheWrap[folderTree]
}
type Settings struct {
AnonOrgRole string
}
func NewService(
sql legacysql.LegacyDatabaseProvider,
folderStore store.FolderStore,
@ -70,12 +75,17 @@ func NewService(
tracer tracing.Tracer,
reg prometheus.Registerer,
cache cache.Cache,
settings Settings,
) *Service {
if settings.AnonOrgRole == "" {
settings.AnonOrgRole = "Viewer"
}
return &Service{
store: store.NewStore(sql, tracer),
folderStore: folderStore,
permissionStore: permissionStore,
identityStore: identityStore,
settings: settings,
logger: logger,
tracer: tracer,
metrics: newMetrics(reg),
@ -422,7 +432,7 @@ func (s *Service) getAnonymousPermissions(ctx context.Context, ns types.Namespac
anonPermKey := anonymousPermCacheKey(ns.Value, action)
res, err, _ := s.sf.Do(anonPermKey+"_getAnonymousPermissions", func() (interface{}, error) {
permissions, err := s.permissionStore.GetUserPermissions(ctx, ns, store.PermissionsQuery{Action: action, ActionSets: actionSets, Role: "Viewer"})
permissions, err := s.permissionStore.GetUserPermissions(ctx, ns, store.PermissionsQuery{Action: action, ActionSets: actionSets, Role: s.settings.AnonOrgRole})
if err != nil {
return nil, err
}

@ -1288,6 +1288,89 @@ func TestService_List(t *testing.T) {
})
}
func TestService_getAnonymousPermissions(t *testing.T) {
type testCase struct {
name string
permissions []accesscontrol.Permission
action string
expectedPerms map[string]bool
expectedError bool
anonRole string
}
testCases := []testCase{
{
name: "should return permissions from store if not in cache",
permissions: []accesscontrol.Permission{
{Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"},
},
action: "dashboards:read",
expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true},
expectedError: false,
anonRole: "Viewer",
},
{
name: "should return error if store fails",
permissions: nil,
action: "dashboards:read",
expectedPerms: nil,
expectedError: true,
anonRole: "Viewer",
},
{
name: "should handle wildcard permissions",
permissions: []accesscontrol.Permission{
{Action: "dashboards:read", Scope: "*", Kind: "*"},
},
action: "dashboards:read",
expectedPerms: map[string]bool{"*": true},
expectedError: false,
anonRole: "Viewer",
},
{
name: "should use custom anonymous role when specified",
permissions: []accesscontrol.Permission{
{Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"},
},
action: "dashboards:read",
expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true},
expectedError: false,
anonRole: "Editor",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
s := setupService()
if tc.anonRole != "" {
s.settings.AnonOrgRole = tc.anonRole
}
ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12}
store := &fakeStore{
userPermissions: tc.permissions,
err: tc.expectedError,
disableNsCheck: true,
}
s.store = store
s.permissionStore = store
perms, err := s.getAnonymousPermissions(ctx, ns, tc.action, []string{})
if tc.expectedError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.expectedPerms, perms)
// cache should then be set
cached, ok := s.permCache.Get(ctx, anonymousPermCacheKey(ns.Value, tc.action))
require.True(t, ok)
require.Equal(t, tc.expectedPerms, cached)
})
}
}
func TestService_CacheList(t *testing.T) {
callingService := authn.NewAccessTokenAuthInfo(authn.Claims[authn.AccessTokenClaims]{
Claims: jwt.Claims{
@ -1337,6 +1420,7 @@ func setupService() *Service {
teamCache: newCacheWrap[[]int64](cache, logger, shortCacheTTL),
basicRoleCache: newCacheWrap[store.BasicRole](cache, logger, longCacheTTL),
folderCache: newCacheWrap[folderTree](cache, logger, shortCacheTTL),
settings: Settings{AnonOrgRole: "Viewer"},
store: fStore,
permissionStore: fStore,
folderStore: fStore,

Loading…
Cancel
Save