From 49ccae74894dce5cf82aa6e5395a5b330e104241 Mon Sep 17 00:00:00 2001 From: Ieva Date: Mon, 6 Feb 2023 17:38:15 +0000 Subject: [PATCH] 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 --- .../manager/manager_integration_test.go | 2 +- pkg/services/searchV2/allowed_actions_test.go | 2 +- pkg/services/searchV2/auth.go | 27 ++++++++++++++----- pkg/services/searchV2/service.go | 14 ++++++---- pkg/services/searchV2/service_bench_test.go | 2 +- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/pkg/plugins/manager/manager_integration_test.go b/pkg/plugins/manager/manager_integration_test.go index 5fcc288e917..96de51f2ca1 100644 --- a/pkg/plugins/manager/manager_integration_test.go +++ b/pkg/plugins/manager/manager_integration_test.go @@ -104,7 +104,7 @@ func TestIntegrationPluginManager(t *testing.T) { pg := postgres.ProvideService(cfg) my := mysql.ProvideService(cfg, hcp) 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) phlare := phlare.ProvideService(hcp) parca := parca.ProvideService(hcp) diff --git a/pkg/services/searchV2/allowed_actions_test.go b/pkg/services/searchV2/allowed_actions_test.go index 933fd6fcefe..b73adc5c23a 100644 --- a/pkg/services/searchV2/allowed_actions_test.go +++ b/pkg/services/searchV2/allowed_actions_test.go @@ -86,7 +86,7 @@ var ( func service(t *testing.T) *StandardSearchService { service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}}, nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), - nil, nil, nil).(*StandardSearchService) + nil, nil, nil, nil).(*StandardSearchService) require.True(t, ok) return service } diff --git a/pkg/services/searchV2/auth.go b/pkg/services/searchV2/auth.go index 60437f543cc..fab9913b217 100644 --- a/pkg/services/searchV2/auth.go +++ b/pkg/services/searchV2/auth.go @@ -4,8 +4,10 @@ import ( "context" "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/dashboards" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/sqlstore/permissions" "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 type FutureAuthService interface { - GetDashboardReadFilter(user *user.SignedInUser) (ResourceFilter, error) + GetDashboardReadFilter(ctx context.Context, orgID int64, user *user.SignedInUser) (ResourceFilter, error) } var _ FutureAuthService = (*simpleAuthService)(nil) type simpleAuthService struct { - sql db.DB - ac accesscontrol.Service + sql db.DB + ac accesscontrol.Service + folderService folder.Service + logger log.Logger } type dashIdQueryResult struct { 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() { canReadDashboard, canReadFolder := accesscontrol.Checker(user, dashboards.ActionDashboardsRead), accesscontrol.Checker(user, dashboards.ActionFoldersRead) return func(kind entityKind, uid, parent string) bool { 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 { - 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 }, nil diff --git a/pkg/services/searchV2/service.go b/pkg/services/searchV2/service.go index c8fe32896f6..4c8ad4d248a 100644 --- a/pkg/services/searchV2/service.go +++ b/pkg/services/searchV2/service.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/accesscontrol" "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/querylibrary" "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, 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{} + logger := log.New("searchV2") s := &StandardSearchService{ cfg: cfg, sql: sql, ac: ac, auth: &simpleAuthService{ - sql: sql, - ac: ac, + sql: sql, + ac: ac, + folderService: folderService, + logger: logger, }, dashboardIndex: newSearchIndex( newSQLDashboardLoader(sql, tracer, cfg.Search), @@ -103,7 +107,7 @@ func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEv features, cfg.Search, ), - logger: log.New("searchV2"), + logger: logger, extender: extender, reIndexCh: make(chan struct{}, 1), orgService: orgService, @@ -244,7 +248,7 @@ func (s *StandardSearchService) doDashboardQuery(ctx context.Context, signedInUs rsp := &backend.DataResponse{} - filter, err := s.auth.GetDashboardReadFilter(signedInUser) + filter, err := s.auth.GetDashboardReadFilter(ctx, orgID, signedInUser) if err != nil { dashboardSearchFailureRequestsCounter.With(prometheus.Labels{ "reason": "get_dashboard_filter_error", diff --git a/pkg/services/searchV2/service_bench_test.go b/pkg/services/searchV2/service_bench_test.go index 989542d5aa7..fee2167c271 100644 --- a/pkg/services/searchV2/service_bench_test.go +++ b/pkg/services/searchV2/service_bench_test.go @@ -39,7 +39,7 @@ func setupBenchEnv(b *testing.B, folderCount, dashboardsPerFolder int) (*Standar } querySvc := querylibraryimpl.ProvideService(cfg, features) 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) err = runSearchService(searchService)