mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 14:52:26 +08:00
Authz: Account for fixed roles when running oss and using authz service (#99244)
* Extract "PermissionStore" from general store interface * Add static and union permission stores * Add GetStaticRoles * Use accesscontrol.Service for inproc to provide static permissions
This commit is contained in:
@ -59,6 +59,8 @@ type Service interface {
|
|||||||
DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error
|
DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error
|
||||||
// SyncUserRoles adds provided roles to user
|
// SyncUserRoles adds provided roles to user
|
||||||
SyncUserRoles(ctx context.Context, orgID int64, cmd SyncUserRolesCommand) error
|
SyncUserRoles(ctx context.Context, orgID int64, cmd SyncUserRolesCommand) error
|
||||||
|
// GetStaicRoles returns a map where key organization role and value is a static rbac role.
|
||||||
|
GetStaticRoles(ctx context.Context) map[string]*RoleDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate mockery --name Store --structname MockStore --outpkg actest --filename store_mock.go --output ./actest/
|
//go:generate mockery --name Store --structname MockStore --outpkg actest --filename store_mock.go --output ./actest/
|
||||||
|
@ -464,7 +464,15 @@ func (s *Service) RegisterFixedRoles(ctx context.Context) error {
|
|||||||
s.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
|
s.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
|
||||||
for br := range accesscontrol.BuiltInRolesWithParents(registration.Grants) {
|
for br := range accesscontrol.BuiltInRolesWithParents(registration.Grants) {
|
||||||
if basicRole, ok := s.roles[br]; ok {
|
if basicRole, ok := s.roles[br]; ok {
|
||||||
basicRole.Permissions = append(basicRole.Permissions, registration.Role.Permissions...)
|
for _, p := range registration.Role.Permissions {
|
||||||
|
perm := accesscontrol.Permission{
|
||||||
|
Action: p.Action,
|
||||||
|
Scope: p.Scope,
|
||||||
|
}
|
||||||
|
|
||||||
|
perm.Kind, perm.Attribute, perm.Identifier = accesscontrol.SplitScope(perm.Scope)
|
||||||
|
basicRole.Permissions = append(basicRole.Permissions, perm)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
s.log.Error("Unknown builtin role", "builtInRole", br)
|
s.log.Error("Unknown builtin role", "builtInRole", br)
|
||||||
}
|
}
|
||||||
@ -770,10 +778,14 @@ func (s *Service) DeleteExternalServiceRole(ctx context.Context, externalService
|
|||||||
return s.store.DeleteExternalServiceRole(ctx, slug)
|
return s.store.DeleteExternalServiceRole(ctx, slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Service) SyncUserRoles(ctx context.Context, orgID int64, cmd accesscontrol.SyncUserRolesCommand) error {
|
func (s *Service) SyncUserRoles(ctx context.Context, orgID int64, cmd accesscontrol.SyncUserRolesCommand) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetStaticRoles(ctx context.Context) map[string]*accesscontrol.RoleDTO {
|
||||||
|
return s.roles
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GetRoleByName(ctx context.Context, orgID int64, roleName string) (*accesscontrol.RoleDTO, error) {
|
func (s *Service) GetRoleByName(ctx context.Context, orgID int64, roleName string) (*accesscontrol.RoleDTO, error) {
|
||||||
_, span := tracer.Start(ctx, "accesscontrol.acimpl.GetRoleByName")
|
_, span := tracer.Start(ctx, "accesscontrol.acimpl.GetRoleByName")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -373,6 +373,7 @@ func TestService_RegisterFixedRoles(t *testing.T) {
|
|||||||
builtinRole, ok := ac.roles[br]
|
builtinRole, ok := ac.roles[br]
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
for _, expectedPermission := range registration.Role.Permissions {
|
for _, expectedPermission := range registration.Role.Permissions {
|
||||||
|
expectedPermission.Kind, expectedPermission.Attribute, expectedPermission.Identifier = accesscontrol.SplitScope(expectedPermission.Scope)
|
||||||
assert.Contains(t, builtinRole.Permissions, expectedPermission)
|
assert.Contains(t, builtinRole.Permissions, expectedPermission)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,9 @@ type Calls struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mock struct {
|
type Mock struct {
|
||||||
|
accesscontrol.Service
|
||||||
|
accesscontrol.AccessControl
|
||||||
|
|
||||||
// Unless an override is provided, permissions will be returned by GetUserPermissions
|
// Unless an override is provided, permissions will be returned by GetUserPermissions
|
||||||
permissions []accesscontrol.Permission
|
permissions []accesscontrol.Permission
|
||||||
// Unless an override is provided, builtInRoles will be returned by GetUserBuiltInRoles
|
// Unless an override is provided, builtInRoles will be returned by GetUserBuiltInRoles
|
||||||
|
@ -18,7 +18,9 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -30,8 +32,12 @@ const authzServiceAudience = "authzService"
|
|||||||
|
|
||||||
// ProvideAuthZClient provides an AuthZ client and creates the AuthZ service.
|
// ProvideAuthZClient provides an AuthZ client and creates the AuthZ service.
|
||||||
func ProvideAuthZClient(
|
func ProvideAuthZClient(
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, grpcServer grpcserver.Provider,
|
cfg *setting.Cfg,
|
||||||
tracer tracing.Tracer, db db.DB,
|
features featuremgmt.FeatureToggles,
|
||||||
|
grpcServer grpcserver.Provider,
|
||||||
|
tracer tracing.Tracer,
|
||||||
|
db db.DB,
|
||||||
|
acService accesscontrol.Service,
|
||||||
) (authzlib.AccessClient, error) {
|
) (authzlib.AccessClient, error) {
|
||||||
authCfg, err := ReadCfg(cfg)
|
authCfg, err := ReadCfg(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -43,16 +49,25 @@ func ProvideAuthZClient(
|
|||||||
return nil, errors.New("authZGRPCServer feature toggle is required for cloud and grpc mode")
|
return nil, errors.New("authZGRPCServer feature toggle is required for cloud and grpc mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the server
|
|
||||||
sql := legacysql.NewDatabaseProvider(db)
|
|
||||||
server := rbac.NewService(sql, legacy.NewLegacySQLStores(sql), log.New("authz-grpc-server"), tracer)
|
|
||||||
|
|
||||||
switch authCfg.mode {
|
switch authCfg.mode {
|
||||||
case ModeGRPC:
|
case ModeGRPC:
|
||||||
return newGrpcLegacyClient(authCfg, tracer)
|
return newGrpcLegacyClient(authCfg, tracer)
|
||||||
case ModeCloud:
|
case ModeCloud:
|
||||||
return newCloudLegacyClient(authCfg, tracer)
|
return newCloudLegacyClient(authCfg, tracer)
|
||||||
default:
|
default:
|
||||||
|
sql := legacysql.NewDatabaseProvider(db)
|
||||||
|
|
||||||
|
// Register the server
|
||||||
|
server := rbac.NewService(
|
||||||
|
sql,
|
||||||
|
legacy.NewLegacySQLStores(sql),
|
||||||
|
store.NewUnionPermissionStore(
|
||||||
|
store.NewStaticPermissionStore(acService),
|
||||||
|
store.NewSQLPermissionStore(sql, tracer),
|
||||||
|
),
|
||||||
|
log.New("authz-grpc-server"),
|
||||||
|
tracer,
|
||||||
|
)
|
||||||
return newInProcLegacyClient(server, tracer)
|
return newInProcLegacyClient(server, tracer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,10 @@ type Service struct {
|
|||||||
authzv1.UnimplementedAuthzServiceServer
|
authzv1.UnimplementedAuthzServiceServer
|
||||||
authzextv1.UnimplementedAuthzExtentionServiceServer
|
authzextv1.UnimplementedAuthzExtentionServiceServer
|
||||||
|
|
||||||
store store.Store
|
store store.Store
|
||||||
identityStore legacy.LegacyIdentityStore
|
permissionStore store.PermissionStore
|
||||||
actionMapper *mappers.K8sRbacMapper
|
identityStore legacy.LegacyIdentityStore
|
||||||
|
actionMapper *mappers.K8sRbacMapper
|
||||||
|
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
@ -58,19 +59,27 @@ type Service struct {
|
|||||||
sf *singleflight.Group
|
sf *singleflight.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.LegacyIdentityStore, logger log.Logger, tracer tracing.Tracer) *Service {
|
func NewService(
|
||||||
|
sql legacysql.LegacyDatabaseProvider,
|
||||||
|
identityStore legacy.LegacyIdentityStore,
|
||||||
|
permissionStore store.PermissionStore,
|
||||||
|
logger log.Logger,
|
||||||
|
tracer tracing.Tracer,
|
||||||
|
|
||||||
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
store: store.NewStore(sql, tracer),
|
store: store.NewStore(sql, tracer),
|
||||||
identityStore: identityStore,
|
permissionStore: permissionStore,
|
||||||
actionMapper: mappers.NewK8sRbacMapper(),
|
identityStore: identityStore,
|
||||||
logger: logger,
|
actionMapper: mappers.NewK8sRbacMapper(),
|
||||||
tracer: tracer,
|
logger: logger,
|
||||||
idCache: localcache.New(longCacheTTL, longCleanupInterval),
|
tracer: tracer,
|
||||||
permCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
idCache: localcache.New(longCacheTTL, longCleanupInterval),
|
||||||
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
permCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||||
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
|
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||||
folderCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
|
||||||
sf: new(singleflight.Group),
|
folderCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||||
|
sf: new(singleflight.Group),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +291,7 @@ func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInf
|
|||||||
IsServerAdmin: basicRoles.IsAdmin,
|
IsServerAdmin: basicRoles.IsAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions, err := s.store.GetUserPermissions(ctx, ns, userPermQuery)
|
permissions, err := s.permissionStore.GetUserPermissions(ctx, ns, userPermQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -311,7 +320,7 @@ func (s *Service) getAnonymousPermissions(ctx context.Context, ns claims.Namespa
|
|||||||
}
|
}
|
||||||
|
|
||||||
res, err, _ := s.sf.Do(anonPermKey+"_getAnonymousPermissions", func() (interface{}, error) {
|
res, err, _ := s.sf.Do(anonPermKey+"_getAnonymousPermissions", func() (interface{}, error) {
|
||||||
permissions, err := s.store.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: "Viewer"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -286,10 +286,11 @@ func TestService_getUserBasicRole(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
basicRoleCache: cacheService,
|
basicRoleCache: cacheService,
|
||||||
store: store,
|
store: store,
|
||||||
logger: log.New("test"),
|
permissionStore: store,
|
||||||
tracer: tracing.NewNoopTracerService(),
|
logger: log.New("test"),
|
||||||
|
tracer: tracing.NewNoopTracerService(),
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := s.getUserBasicRole(ctx, ns, userIdentifiers)
|
role, err := s.getUserBasicRole(ctx, ns, userIdentifiers)
|
||||||
@ -361,16 +362,17 @@ func TestService_getUserPermissions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
store: store,
|
store: store,
|
||||||
identityStore: &fakeIdentityStore{teams: []int64{1, 2}},
|
permissionStore: store,
|
||||||
actionMapper: mappers.NewK8sRbacMapper(),
|
identityStore: &fakeIdentityStore{teams: []int64{1, 2}},
|
||||||
logger: log.New("test"),
|
actionMapper: mappers.NewK8sRbacMapper(),
|
||||||
tracer: tracing.NewNoopTracerService(),
|
logger: log.New("test"),
|
||||||
idCache: localcache.New(longCacheTTL, longCleanupInterval),
|
tracer: tracing.NewNoopTracerService(),
|
||||||
permCache: cacheService,
|
idCache: localcache.New(longCacheTTL, longCleanupInterval),
|
||||||
sf: new(singleflight.Group),
|
permCache: cacheService,
|
||||||
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
|
sf: new(singleflight.Group),
|
||||||
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
|
||||||
|
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||||
}
|
}
|
||||||
|
|
||||||
perms, err := s.getUserPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
perms, err := s.getUserPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
||||||
@ -437,11 +439,12 @@ func TestService_buildFolderTree(t *testing.T) {
|
|||||||
store := &fakeStore{folders: tc.folders}
|
store := &fakeStore{folders: tc.folders}
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
store: store,
|
store: store,
|
||||||
folderCache: cacheService,
|
permissionStore: store,
|
||||||
logger: log.New("test"),
|
folderCache: cacheService,
|
||||||
sf: new(singleflight.Group),
|
logger: log.New("test"),
|
||||||
tracer: tracing.NewNoopTracerService(),
|
sf: new(singleflight.Group),
|
||||||
|
tracer: tracing.NewNoopTracerService(),
|
||||||
}
|
}
|
||||||
|
|
||||||
tree, err := s.buildFolderTree(ctx, ns)
|
tree, err := s.buildFolderTree(ctx, ns)
|
||||||
|
@ -10,16 +10,6 @@ type BasicRole struct {
|
|||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type PermissionsQuery struct {
|
|
||||||
OrgID int64
|
|
||||||
UserID int64
|
|
||||||
Action string
|
|
||||||
ActionSets []string
|
|
||||||
TeamIDs []int64
|
|
||||||
Role string
|
|
||||||
IsServerAdmin bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type BasicRoleQuery struct {
|
type BasicRoleQuery struct {
|
||||||
UserID int64
|
UserID int64
|
||||||
OrgID int64
|
OrgID int64
|
||||||
|
159
pkg/services/authz/rbac/store/permission_store.go
Normal file
159
pkg/services/authz/rbac/store/permission_store.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/authlib/claims"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||||
|
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PermissionStore interface {
|
||||||
|
GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PermissionsQuery struct {
|
||||||
|
OrgID int64
|
||||||
|
UserID int64
|
||||||
|
Action string
|
||||||
|
ActionSets []string
|
||||||
|
TeamIDs []int64
|
||||||
|
Role string
|
||||||
|
IsServerAdmin bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSQLPermissionStore(sql legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) *SQLPermissionsStore {
|
||||||
|
return &SQLPermissionsStore{sql, tracer}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PermissionStore = (*SQLPermissionsStore)(nil)
|
||||||
|
|
||||||
|
type SQLPermissionsStore struct {
|
||||||
|
sql legacysql.LegacyDatabaseProvider
|
||||||
|
tracer tracing.Tracer
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqlUserPerms = mustTemplate("permission_query.sql")
|
||||||
|
|
||||||
|
type getPermissionsQuery struct {
|
||||||
|
sqltemplate.SQLTemplate
|
||||||
|
Query *PermissionsQuery
|
||||||
|
|
||||||
|
PermissionTable string
|
||||||
|
UserRoleTable string
|
||||||
|
TeamRoleTable string
|
||||||
|
BuiltinRoleTable string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r getPermissionsQuery) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGetPermissions(sql *legacysql.LegacyDatabaseHelper, q *PermissionsQuery) getPermissionsQuery {
|
||||||
|
return getPermissionsQuery{
|
||||||
|
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||||
|
Query: q,
|
||||||
|
PermissionTable: sql.Table("permission"),
|
||||||
|
UserRoleTable: sql.Table("user_role"),
|
||||||
|
TeamRoleTable: sql.Table("team_role"),
|
||||||
|
BuiltinRoleTable: sql.Table("builtin_role"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLPermissionsStore) GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error) {
|
||||||
|
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.GetUserPermissions")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
sql, err := s.sql(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
query.OrgID = ns.OrgID
|
||||||
|
req := newGetPermissions(sql, &query)
|
||||||
|
q, err := sqltemplate.Execute(sqlUserPerms, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if res != nil {
|
||||||
|
_ = res.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var perms []accesscontrol.Permission
|
||||||
|
for res.Next() {
|
||||||
|
var perm accesscontrol.Permission
|
||||||
|
if err := res.Scan(&perm.Kind, &perm.Attribute, &perm.Identifier, &perm.Scope); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
perms = append(perms, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PermissionStore = (*StaticPermissionStore)(nil)
|
||||||
|
|
||||||
|
func NewStaticPermissionStore(ac accesscontrol.Service) *StaticPermissionStore {
|
||||||
|
return &StaticPermissionStore{ac}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaticPermissionStore struct {
|
||||||
|
ac accesscontrol.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticPermissionStore) GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error) {
|
||||||
|
roles := []string{query.Role}
|
||||||
|
if query.IsServerAdmin {
|
||||||
|
roles = append(roles, "Grafana Admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
static := s.ac.GetStaticRoles(ctx)
|
||||||
|
|
||||||
|
var permissions []accesscontrol.Permission
|
||||||
|
for _, name := range roles {
|
||||||
|
r, ok := static[name]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range r.Permissions {
|
||||||
|
if p.Action == query.Action {
|
||||||
|
permissions = append(permissions, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PermissionStore = (*UnionPermissionStore)(nil)
|
||||||
|
|
||||||
|
func NewUnionPermissionStore(stores ...PermissionStore) *UnionPermissionStore {
|
||||||
|
return &UnionPermissionStore{stores}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionPermissionStore struct {
|
||||||
|
stores []PermissionStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UnionPermissionStore) GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error) {
|
||||||
|
var permissions []accesscontrol.Permission
|
||||||
|
for _, s := range u.stores {
|
||||||
|
result, err := s.GetUserPermissions(ctx, ns, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
permissions = append(permissions, result...)
|
||||||
|
}
|
||||||
|
return permissions, nil
|
||||||
|
}
|
@ -14,7 +14,6 @@ var (
|
|||||||
sqlTemplatesFS embed.FS
|
sqlTemplatesFS embed.FS
|
||||||
sqlTemplates = template.Must(template.New("sql").ParseFS(sqlTemplatesFS, `*.sql`))
|
sqlTemplates = template.Must(template.New("sql").ParseFS(sqlTemplatesFS, `*.sql`))
|
||||||
|
|
||||||
sqlUserPerms = mustTemplate("permission_query.sql")
|
|
||||||
sqlQueryBasicRoles = mustTemplate("basic_role_query.sql")
|
sqlQueryBasicRoles = mustTemplate("basic_role_query.sql")
|
||||||
sqlUserIdentifiers = mustTemplate("user_identifier_query.sql")
|
sqlUserIdentifiers = mustTemplate("user_identifier_query.sql")
|
||||||
sqlFolders = mustTemplate("folder_query.sql")
|
sqlFolders = mustTemplate("folder_query.sql")
|
||||||
@ -67,31 +66,6 @@ func newGetBasicRoles(sql *legacysql.LegacyDatabaseHelper, q *BasicRoleQuery) ge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type getPermissionsQuery struct {
|
|
||||||
sqltemplate.SQLTemplate
|
|
||||||
Query *PermissionsQuery
|
|
||||||
|
|
||||||
PermissionTable string
|
|
||||||
UserRoleTable string
|
|
||||||
TeamRoleTable string
|
|
||||||
BuiltinRoleTable string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r getPermissionsQuery) Validate() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newGetPermissions(sql *legacysql.LegacyDatabaseHelper, q *PermissionsQuery) getPermissionsQuery {
|
|
||||||
return getPermissionsQuery{
|
|
||||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
|
||||||
Query: q,
|
|
||||||
PermissionTable: sql.Table("permission"),
|
|
||||||
UserRoleTable: sql.Table("user_role"),
|
|
||||||
TeamRoleTable: sql.Table("team_role"),
|
|
||||||
BuiltinRoleTable: sql.Table("builtin_role"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type getFoldersQuery struct {
|
type getFoldersQuery struct {
|
||||||
sqltemplate.SQLTemplate
|
sqltemplate.SQLTemplate
|
||||||
Query *FolderQuery
|
Query *FolderQuery
|
||||||
|
@ -7,13 +7,11 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
||||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error)
|
|
||||||
GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error)
|
GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error)
|
||||||
GetBasicRoles(ctx context.Context, ns claims.NamespaceInfo, query BasicRoleQuery) (*BasicRole, error)
|
GetBasicRoles(ctx context.Context, ns claims.NamespaceInfo, query BasicRoleQuery) (*BasicRole, error)
|
||||||
GetFolders(ctx context.Context, ns claims.NamespaceInfo) ([]Folder, error)
|
GetFolders(ctx context.Context, ns claims.NamespaceInfo) ([]Folder, error)
|
||||||
@ -31,44 +29,6 @@ func NewStore(sql legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) *Stor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StoreImpl) GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error) {
|
|
||||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.GetUserPermissions")
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
sql, err := s.sql(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
query.OrgID = ns.OrgID
|
|
||||||
req := newGetPermissions(sql, &query)
|
|
||||||
q, err := sqltemplate.Execute(sqlUserPerms, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if res != nil {
|
|
||||||
_ = res.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var perms []accesscontrol.Permission
|
|
||||||
for res.Next() {
|
|
||||||
var perm accesscontrol.Permission
|
|
||||||
if err := res.Scan(&perm.Kind, &perm.Attribute, &perm.Identifier, &perm.Scope); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
perms = append(perms, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return perms, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StoreImpl) GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error) {
|
func (s *StoreImpl) GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.GetUserIdentifiers")
|
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.GetUserIdentifiers")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -8,12 +8,19 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||||
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRBACAuthZService(handler grpcserver.Provider, db legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) {
|
func RegisterRBACAuthZService(handler grpcserver.Provider, db legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) {
|
||||||
server := rbac.NewService(db, legacy.NewLegacySQLStores(db), log.New("authz-grpc-server"), tracer)
|
server := rbac.NewService(
|
||||||
|
db,
|
||||||
|
legacy.NewLegacySQLStores(db),
|
||||||
|
store.NewSQLPermissionStore(db, tracer),
|
||||||
|
log.New("authz-grpc-server"),
|
||||||
|
tracer,
|
||||||
|
)
|
||||||
|
|
||||||
srv := handler.GetServer()
|
srv := handler.GetServer()
|
||||||
authzv1.RegisterAuthzServiceServer(srv, server)
|
authzv1.RegisterAuthzServiceServer(srv, server)
|
||||||
|
@ -442,14 +442,7 @@ func (c *K8sTestHelper) LoadYAMLOrJSON(body string) *unstructured.Unstructured {
|
|||||||
func (c *K8sTestHelper) createTestUsers(orgName string) OrgUsers {
|
func (c *K8sTestHelper) createTestUsers(orgName string) OrgUsers {
|
||||||
c.t.Helper()
|
c.t.Helper()
|
||||||
users := OrgUsers{
|
users := OrgUsers{
|
||||||
Admin: c.CreateUser("admin", orgName, org.RoleAdmin, []resourcepermissions.SetResourcePermissionCommand{
|
Admin: c.CreateUser("admin", orgName, org.RoleAdmin, nil),
|
||||||
{
|
|
||||||
Actions: []string{"dashboards:read", "dashboards:write", "dashboards:create", "dashboards:delete"},
|
|
||||||
Resource: "dashboards",
|
|
||||||
ResourceAttribute: "uid",
|
|
||||||
ResourceID: "*",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
Editor: c.CreateUser("editor", orgName, org.RoleEditor, nil),
|
Editor: c.CreateUser("editor", orgName, org.RoleEditor, nil),
|
||||||
Viewer: c.CreateUser("viewer", orgName, org.RoleViewer, nil),
|
Viewer: c.CreateUser("viewer", orgName, org.RoleViewer, nil),
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user