mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
RBAC: cascaded nested folder permissions for search v2 (#62608)
* fetch inherited folder scopes for search v2 * fix error and don't fetch parents for the general folder * remove feature toggle check and lower log level
This commit is contained in:
parent
161ff6d310
commit
49ccae7489
@ -104,7 +104,7 @@ func TestIntegrationPluginManager(t *testing.T) {
|
|||||||
pg := postgres.ProvideService(cfg)
|
pg := postgres.ProvideService(cfg)
|
||||||
my := mysql.ProvideService(cfg, hcp)
|
my := mysql.ProvideService(cfg, hcp)
|
||||||
ms := mssql.ProvideService(cfg)
|
ms := mssql.ProvideService(cfg)
|
||||||
sv2 := searchV2.ProvideService(cfg, db.InitTestDB(t), nil, nil, tracer, features, nil, nil, nil)
|
sv2 := searchV2.ProvideService(cfg, db.InitTestDB(t), nil, nil, tracer, features, nil, nil, nil, nil)
|
||||||
graf := grafanads.ProvideService(sv2, nil)
|
graf := grafanads.ProvideService(sv2, nil)
|
||||||
phlare := phlare.ProvideService(hcp)
|
phlare := phlare.ProvideService(hcp)
|
||||||
parca := parca.ProvideService(hcp)
|
parca := parca.ProvideService(hcp)
|
||||||
|
@ -86,7 +86,7 @@ var (
|
|||||||
func service(t *testing.T) *StandardSearchService {
|
func service(t *testing.T) *StandardSearchService {
|
||||||
service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}},
|
service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}},
|
||||||
nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(),
|
nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(),
|
||||||
nil, nil, nil).(*StandardSearchService)
|
nil, nil, nil, nil).(*StandardSearchService)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
@ -15,28 +17,41 @@ type ResourceFilter func(kind entityKind, uid, parentUID string) bool
|
|||||||
|
|
||||||
// FutureAuthService eventually implemented by the security service
|
// FutureAuthService eventually implemented by the security service
|
||||||
type FutureAuthService interface {
|
type FutureAuthService interface {
|
||||||
GetDashboardReadFilter(user *user.SignedInUser) (ResourceFilter, error)
|
GetDashboardReadFilter(ctx context.Context, orgID int64, user *user.SignedInUser) (ResourceFilter, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ FutureAuthService = (*simpleAuthService)(nil)
|
var _ FutureAuthService = (*simpleAuthService)(nil)
|
||||||
|
|
||||||
type simpleAuthService struct {
|
type simpleAuthService struct {
|
||||||
sql db.DB
|
sql db.DB
|
||||||
ac accesscontrol.Service
|
ac accesscontrol.Service
|
||||||
|
folderService folder.Service
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type dashIdQueryResult struct {
|
type dashIdQueryResult struct {
|
||||||
UID string `xorm:"uid"`
|
UID string `xorm:"uid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *simpleAuthService) GetDashboardReadFilter(user *user.SignedInUser) (ResourceFilter, error) {
|
func (a *simpleAuthService) GetDashboardReadFilter(ctx context.Context, orgID int64, user *user.SignedInUser) (ResourceFilter, error) {
|
||||||
if !a.ac.IsDisabled() {
|
if !a.ac.IsDisabled() {
|
||||||
canReadDashboard, canReadFolder := accesscontrol.Checker(user, dashboards.ActionDashboardsRead), accesscontrol.Checker(user, dashboards.ActionFoldersRead)
|
canReadDashboard, canReadFolder := accesscontrol.Checker(user, dashboards.ActionDashboardsRead), accesscontrol.Checker(user, dashboards.ActionFoldersRead)
|
||||||
return func(kind entityKind, uid, parent string) bool {
|
return func(kind entityKind, uid, parent string) bool {
|
||||||
if kind == entityKindFolder {
|
if kind == entityKindFolder {
|
||||||
return canReadFolder(dashboards.ScopeFoldersProvider.GetResourceScopeUID(uid))
|
scopes, err := dashboards.GetInheritedScopes(ctx, orgID, uid, a.folderService)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Debug("could not retrieve inherited folder scopes:", "err", err)
|
||||||
|
}
|
||||||
|
scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(uid))
|
||||||
|
return canReadFolder(scopes...)
|
||||||
} else if kind == entityKindDashboard {
|
} else if kind == entityKindDashboard {
|
||||||
return canReadDashboard(dashboards.ScopeDashboardsProvider.GetResourceScopeUID(uid), dashboards.ScopeFoldersProvider.GetResourceScopeUID(parent))
|
scopes, err := dashboards.GetInheritedScopes(ctx, orgID, parent, a.folderService)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Debug("could not retrieve inherited folder scopes:", "err", err)
|
||||||
|
}
|
||||||
|
scopes = append(scopes, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(uid))
|
||||||
|
scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(parent))
|
||||||
|
return canReadDashboard(scopes...)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/querylibrary"
|
"github.com/grafana/grafana/pkg/services/querylibrary"
|
||||||
"github.com/grafana/grafana/pkg/services/store"
|
"github.com/grafana/grafana/pkg/services/store"
|
||||||
@ -84,15 +85,18 @@ func (s *StandardSearchService) IsReady(ctx context.Context, orgId int64) IsSear
|
|||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEventsService,
|
func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEventsService,
|
||||||
ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service,
|
ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service,
|
||||||
userService user.Service, queries querylibrary.Service) SearchService {
|
userService user.Service, queries querylibrary.Service, folderService folder.Service) SearchService {
|
||||||
extender := &NoopExtender{}
|
extender := &NoopExtender{}
|
||||||
|
logger := log.New("searchV2")
|
||||||
s := &StandardSearchService{
|
s := &StandardSearchService{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
sql: sql,
|
sql: sql,
|
||||||
ac: ac,
|
ac: ac,
|
||||||
auth: &simpleAuthService{
|
auth: &simpleAuthService{
|
||||||
sql: sql,
|
sql: sql,
|
||||||
ac: ac,
|
ac: ac,
|
||||||
|
folderService: folderService,
|
||||||
|
logger: logger,
|
||||||
},
|
},
|
||||||
dashboardIndex: newSearchIndex(
|
dashboardIndex: newSearchIndex(
|
||||||
newSQLDashboardLoader(sql, tracer, cfg.Search),
|
newSQLDashboardLoader(sql, tracer, cfg.Search),
|
||||||
@ -103,7 +107,7 @@ func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEv
|
|||||||
features,
|
features,
|
||||||
cfg.Search,
|
cfg.Search,
|
||||||
),
|
),
|
||||||
logger: log.New("searchV2"),
|
logger: logger,
|
||||||
extender: extender,
|
extender: extender,
|
||||||
reIndexCh: make(chan struct{}, 1),
|
reIndexCh: make(chan struct{}, 1),
|
||||||
orgService: orgService,
|
orgService: orgService,
|
||||||
@ -244,7 +248,7 @@ func (s *StandardSearchService) doDashboardQuery(ctx context.Context, signedInUs
|
|||||||
|
|
||||||
rsp := &backend.DataResponse{}
|
rsp := &backend.DataResponse{}
|
||||||
|
|
||||||
filter, err := s.auth.GetDashboardReadFilter(signedInUser)
|
filter, err := s.auth.GetDashboardReadFilter(ctx, orgID, signedInUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dashboardSearchFailureRequestsCounter.With(prometheus.Labels{
|
dashboardSearchFailureRequestsCounter.With(prometheus.Labels{
|
||||||
"reason": "get_dashboard_filter_error",
|
"reason": "get_dashboard_filter_error",
|
||||||
|
@ -39,7 +39,7 @@ func setupBenchEnv(b *testing.B, folderCount, dashboardsPerFolder int) (*Standar
|
|||||||
}
|
}
|
||||||
querySvc := querylibraryimpl.ProvideService(cfg, features)
|
querySvc := querylibraryimpl.ProvideService(cfg, features)
|
||||||
searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{},
|
searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{},
|
||||||
tracing.InitializeTracerForTest(), features, orgSvc, nil, querySvc).(*StandardSearchService)
|
tracing.InitializeTracerForTest(), features, orgSvc, nil, querySvc, nil).(*StandardSearchService)
|
||||||
require.True(b, ok)
|
require.True(b, ok)
|
||||||
|
|
||||||
err = runSearchService(searchService)
|
err = runSearchService(searchService)
|
||||||
|
Loading…
Reference in New Issue
Block a user