diff --git a/pkg/api/annotations_test.go b/pkg/api/annotations_test.go index 977f3307ca1..83e11eb91cf 100644 --- a/pkg/api/annotations_test.go +++ b/pkg/api/annotations_test.go @@ -403,7 +403,7 @@ func TestAPI_Annotations(t *testing.T) { hs.folderService = folderService hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures()) hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo, hs.Features, dashService, folderService)) - hs.AccessControl.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderDB, dashService, folderService)) + hs.AccessControl.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashService, folderService)) }) var body io.Reader if tt.body != "" { diff --git a/pkg/apimachinery/identity/context.go b/pkg/apimachinery/identity/context.go index e71ec51d376..627cace5d61 100644 --- a/pkg/apimachinery/identity/context.go +++ b/pkg/apimachinery/identity/context.go @@ -32,7 +32,7 @@ func checkNilRequester(r Requester) bool { const serviceName = "service" -// WithServiceIdentity sets creates an identity representing the service itself in provided org and store it in context. +// WithServiceIdentity sets an identity representing the service itself in provided org and store it in context. // This is useful for background tasks that has to communicate with unfied storage. It also returns a Requester with // static permissions so it can be used in legacy code paths. func WithServiceIdentity(ctx context.Context, orgID int64) (context.Context, Requester) { @@ -53,6 +53,17 @@ func WithServiceIdentity(ctx context.Context, orgID int64) (context.Context, Req return WithRequester(ctx, r), r } +// WithServiceIdentityContext sets an identity representing the service itself in context. +func WithServiceIdentityContext(ctx context.Context, orgID int64) context.Context { + ctx, _ = WithServiceIdentity(ctx, orgID) + return ctx +} + +// WithServiceIdentityFN calls provided closure with an context contaning the identity of the service. +func WithServiceIdentityFn[T any](ctx context.Context, orgID int64, fn func(ctx context.Context) (T, error)) (T, error) { + return fn(WithServiceIdentityContext(ctx, orgID)) +} + func getWildcardPermissions(actions ...string) map[string][]string { permissions := make(map[string][]string, len(actions)) for _, a := range actions { diff --git a/pkg/services/dashboards/accesscontrol.go b/pkg/services/dashboards/accesscontrol.go index 759c60b2e4a..664874d872e 100644 --- a/pkg/services/dashboards/accesscontrol.go +++ b/pkg/services/dashboards/accesscontrol.go @@ -5,7 +5,7 @@ import ( "errors" "strings" - "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/folder" "go.opentelemetry.io/otel" @@ -65,18 +65,19 @@ func NewFolderIDScopeResolver(folderDB folder.FolderStore, folderSvc folder.Serv return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil } - folder, err := folderDB.GetFolderByID(ctx, orgID, id) - if err != nil { - return nil, err - } + return identity.WithServiceIdentityFn(ctx, orgID, func(ctx context.Context) ([]string, error) { + folder, err := folderDB.GetFolderByID(ctx, orgID, id) + if err != nil { + return nil, err + } - result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderSvc) - if err != nil { - return nil, err - } + result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderSvc) + if err != nil { + return nil, err + } - result = append([]string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, result...) - return result, nil + return append([]string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, result...), nil + }) }) } @@ -97,17 +98,19 @@ func NewFolderUIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttrib return nil, err } - inheritedScopes, err := GetInheritedScopes(ctx, orgID, uid, folderSvc) - if err != nil { - return nil, err - } - return append(inheritedScopes, ScopeFoldersProvider.GetResourceScopeUID(uid)), nil + return identity.WithServiceIdentityFn(ctx, orgID, func(ctx context.Context) ([]string, error) { + inheritedScopes, err := GetInheritedScopes(ctx, orgID, uid, folderSvc) + if err != nil { + return nil, err + } + return append(inheritedScopes, ScopeFoldersProvider.GetResourceScopeUID(uid)), nil + }) }) } // NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:" // into uid based scopes for both dashboard and folder -func NewDashboardIDScopeResolver(folderDB folder.FolderStore, ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewDashboardIDScopeResolver(ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { prefix := ScopeDashboardsProvider.GetResourceScope("") return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { ctx, span := tracer.Start(ctx, "dashboards.NewDashboardIDScopeResolver") @@ -122,18 +125,20 @@ func NewDashboardIDScopeResolver(folderDB folder.FolderStore, ds DashboardServic return nil, err } - dashboard, err := ds.GetDashboard(ctx, &GetDashboardQuery{ID: id, OrgID: orgID}) - if err != nil { - return nil, err - } + return identity.WithServiceIdentityFn(ctx, orgID, func(ctx context.Context) ([]string, error) { + dashboard, err := ds.GetDashboard(ctx, &GetDashboardQuery{ID: id, OrgID: orgID}) + if err != nil { + return nil, err + } - return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderSvc) + return resolveDashboardScope(ctx, orgID, dashboard, folderSvc) + }) }) } // NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:" // into uid based scopes for both dashboard and folder -func NewDashboardUIDScopeResolver(folderDB folder.FolderStore, ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewDashboardUIDScopeResolver(ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { prefix := ScopeDashboardsProvider.GetResourceScopeUID("") return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { ctx, span := tracer.Start(ctx, "dashboards.NewDashboardUIDScopeResolver") @@ -148,36 +153,26 @@ func NewDashboardUIDScopeResolver(folderDB folder.FolderStore, ds DashboardServi return nil, err } - dashboard, err := ds.GetDashboard(ctx, &GetDashboardQuery{UID: uid, OrgID: orgID}) - if err != nil { - return nil, err - } + return identity.WithServiceIdentityFn(ctx, orgID, func(ctx context.Context) ([]string, error) { + dashboard, err := ds.GetDashboard(ctx, &GetDashboardQuery{UID: uid, OrgID: orgID}) + if err != nil { + return nil, err + } - return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderSvc) + return resolveDashboardScope(ctx, orgID, dashboard, folderSvc) + }) }) } -func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) { +func resolveDashboardScope(ctx context.Context, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) { ctx, span := tracer.Start(ctx, "dashboards.resolveDashboardScope") span.End() var folderUID string - metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc() - // nolint:staticcheck - if dashboard.FolderID < 0 { - return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil - } - - metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc() - // nolint:staticcheck - if dashboard.FolderID == 0 { + if dashboard.FolderUID == "" { folderUID = ac.GeneralFolderUID } else { - folder, err := folderDB.GetFolderByID(ctx, orgID, dashboard.FolderID) - if err != nil { - return nil, err - } - folderUID = folder.UID + folderUID = dashboard.FolderUID } result, err := GetInheritedScopes(ctx, orgID, folderUID, folderSvc) diff --git a/pkg/services/dashboards/accesscontrol_test.go b/pkg/services/dashboards/accesscontrol_test.go index 026081d0dd0..a08313052f8 100644 --- a/pkg/services/dashboards/accesscontrol_test.go +++ b/pkg/services/dashboards/accesscontrol_test.go @@ -60,12 +60,12 @@ func TestNewFolderIDScopeResolver(t *testing.T) { func TestNewDashboardIDScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) + prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) require.Equal(t, "dashboards:id:", prefix) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) + _, resolver := NewDashboardIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123") require.ErrorIs(t, err, ac.ErrInvalidScope) }) @@ -73,12 +73,12 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { func TestNewDashboardUIDScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) + prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) require.Equal(t, "dashboards:uid:", prefix) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) + _, resolver := NewDashboardUIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123") require.ErrorIs(t, err, ac.ErrInvalidScope) }) diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index d4abb3627fc..efbd0787a45 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -123,8 +123,8 @@ func ProvideDashboardServiceImpl( return nil, err } - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderStore, dashSvc, folderSvc)) - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, dashSvc, folderSvc)) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashSvc, folderSvc)) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashSvc, folderSvc)) if err := folderSvc.RegisterService(dashSvc); err != nil { return nil, err diff --git a/pkg/services/guardian/accesscontrol_guardian_test.go b/pkg/services/guardian/accesscontrol_guardian_test.go index 59ac7d49828..5de4e91da24 100644 --- a/pkg/services/guardian/accesscontrol_guardian_test.go +++ b/pkg/services/guardian/accesscontrol_guardian_test.go @@ -962,7 +962,7 @@ func setupAccessControlGuardianTest( folderStore := foldertest.NewFakeFolderStore(t) - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, fakeDashboardService, folderSvc)) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(fakeDashboardService, folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, folderSvc))