From a52999a8863500690aed49ff8506eba1ce9be80e Mon Sep 17 00:00:00 2001 From: Ieva Date: Fri, 3 Mar 2023 15:56:33 +0000 Subject: [PATCH] Access Control: revert to using folder store from the scope resolvers (#64132) * revert to using folder store from the resolvers * fixing tests after revert * api test fixes --------- Co-authored-by: Kristin Laemmert --- pkg/api/dashboard_permission_test.go | 2 +- pkg/api/dashboard_test.go | 4 +- pkg/api/folder_permission_test.go | 2 +- pkg/services/dashboards/accesscontrol.go | 20 +-- pkg/services/dashboards/accesscontrol_test.go | 114 ++++++++++-------- .../dashboards/service/dashboard_service.go | 10 +- .../dashboard_service_integration_test.go | 16 ++- pkg/services/folder/folderimpl/folder.go | 4 +- .../guardian/accesscontrol_guardian_test.go | 2 +- .../libraryelements/libraryelements_test.go | 5 +- .../librarypanels/librarypanels_test.go | 3 +- pkg/services/ngalert/tests/util.go | 2 +- 12 files changed, 103 insertions(+), 81 deletions(-) diff --git a/pkg/api/dashboard_permission_test.go b/pkg/api/dashboard_permission_test.go index 5b28f1352df..7bbfe2e481e 100644 --- a/pkg/api/dashboard_permission_test.go +++ b/pkg/api/dashboard_permission_test.go @@ -47,7 +47,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) { SQLStore: mockSQLStore, Features: features, DashboardService: dashboardservice.ProvideDashboardService( - settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac, + settings, dashboardStore, foldertest.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac, folderSvc, ), AccessControl: accesscontrolmock.New().WithDisabled(), diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index b79b4a6945c..80f4f151375 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -987,7 +987,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr if dashboardService == nil { dashboardService = service.ProvideDashboardService( - cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, + cfg, dashboardStore, folderStore, nil, features, folderPermissions, dashboardPermissions, ac, folderSvc, ) } @@ -1000,7 +1000,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr ProvisioningService: provisioningService, AccessControl: accesscontrolmock.New(), dashboardProvisioningService: service.ProvideDashboardService( - cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, + cfg, dashboardStore, folderStore, nil, features, folderPermissions, dashboardPermissions, ac, folderSvc, ), DashboardService: dashboardService, diff --git a/pkg/api/folder_permission_test.go b/pkg/api/folder_permission_test.go index 371242d1c59..9f2190aa1f0 100644 --- a/pkg/api/folder_permission_test.go +++ b/pkg/api/folder_permission_test.go @@ -45,7 +45,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) { folderPermissionsService: folderPermissions, dashboardPermissionsService: dashboardPermissions, DashboardService: service.ProvideDashboardService( - settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac, + settings, dashboardStore, foldertest.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac, folderService, ), AccessControl: accesscontrolmock.New().WithDisabled(), diff --git a/pkg/services/dashboards/accesscontrol.go b/pkg/services/dashboards/accesscontrol.go index d1b314adc89..2451fac0c81 100644 --- a/pkg/services/dashboards/accesscontrol.go +++ b/pkg/services/dashboards/accesscontrol.go @@ -39,7 +39,7 @@ var ( ) // NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope. -func NewFolderNameScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewFolderNameScopeResolver(folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { prefix := ScopeFoldersProvider.GetResourceScopeName("") return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { if !strings.HasPrefix(scope, prefix) { @@ -49,7 +49,7 @@ func NewFolderNameScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttri if len(nsName) == 0 { return nil, ac.ErrInvalidScope } - folder, err := folderSvc.Get(ctx, &folder.GetFolderQuery{Title: &nsName}) + folder, err := folderDB.GetFolderByTitle(ctx, orgID, nsName) if err != nil { return nil, err } @@ -65,7 +65,7 @@ func NewFolderNameScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttri } // NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope. -func NewFolderIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewFolderIDScopeResolver(folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { prefix := ScopeFoldersProvider.GetResourceScope("") return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { if !strings.HasPrefix(scope, prefix) { @@ -81,7 +81,7 @@ func NewFolderIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttribu return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil } - folder, err := folderSvc.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, ID: &id}) + folder, err := folderDB.GetFolderByID(ctx, orgID, id) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func NewFolderUIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttrib // 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(ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewDashboardIDScopeResolver(folderDB folder.FolderStore, 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) { if !strings.HasPrefix(scope, prefix) { @@ -137,13 +137,13 @@ func NewDashboardIDScopeResolver(ds DashboardService, folderSvc folder.Service) return nil, err } - return resolveDashboardScope(ctx, orgID, dashboard, folderSvc) + return resolveDashboardScope(ctx, folderDB, 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(ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { +func NewDashboardUIDScopeResolver(folderDB folder.FolderStore, 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) { if !strings.HasPrefix(scope, prefix) { @@ -160,11 +160,11 @@ func NewDashboardUIDScopeResolver(ds DashboardService, folderSvc folder.Service) return nil, err } - return resolveDashboardScope(ctx, orgID, dashboard, folderSvc) + return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderSvc) }) } -func resolveDashboardScope(ctx context.Context, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) { +func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) { var folderUID string if dashboard.FolderID < 0 { return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil @@ -173,7 +173,7 @@ func resolveDashboardScope(ctx context.Context, orgID int64, dashboard *Dashboar if dashboard.FolderID == 0 { folderUID = ac.GeneralFolderUID } else { - folder, err := folderSvc.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, ID: &dashboard.FolderID}) + folder, err := folderDB.GetFolderByID(ctx, orgID, dashboard.FolderID) if err != nil { return nil, err } diff --git a/pkg/services/dashboards/accesscontrol_test.go b/pkg/services/dashboards/accesscontrol_test.go index 87f63133b6c..267778d0cbd 100644 --- a/pkg/services/dashboards/accesscontrol_test.go +++ b/pkg/services/dashboards/accesscontrol_test.go @@ -19,30 +19,36 @@ import ( func TestNewFolderNameScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewFolderNameScopeResolver(foldertest.NewFakeService()) + prefix, _ := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) require.Equal(t, "folders:name:", prefix) }) t.Run("resolver should convert to uid scope", func(t *testing.T) { - folderService := foldertest.NewFakeService() orgId := rand.Int63() title := "Very complex :title with: and /" + util.GenerateShortUID() db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()} - folderService.ExpectedFolder = db + folderStore := foldertest.NewFakeFolderStore(t) + folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() scope := "folders:name:" + title - _, resolver := NewFolderNameScopeResolver(folderService) + _, resolver := NewFolderNameScopeResolver(folderStore, foldertest.NewFakeService()) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 1) require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) + folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title) }) t.Run("resolver should include inherited scopes if any", func(t *testing.T) { orgId := rand.Int63() title := "Very complex :title with: and /" + util.GenerateShortUID() + + db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()} + + folderStore := foldertest.NewFakeFolderStore(t) + folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() + scope := "folders:name:" + title - uid := util.GenerateShortUID() folderSvc := foldertest.NewFakeService() folderSvc.ExpectedFolders = []*folder.Folder{ @@ -53,39 +59,40 @@ func TestNewFolderNameScopeResolver(t *testing.T) { UID: "grandparent", }, } - folderSvc.ExpectedFolder = &folder.Folder{Title: title, ID: rand.Int63(), UID: uid} - _, resolver := NewFolderNameScopeResolver(folderSvc) + _, resolver := NewFolderNameScopeResolver(folderStore, folderSvc) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 3) if diff := cmp.Diff([]string{ - fmt.Sprintf("folders:uid:%v", uid), + fmt.Sprintf("folders:uid:%v", db.UID), "folders:uid:parent", "folders:uid:grandparent", }, resolvedScopes); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } + + folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeService()) + _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123") require.ErrorIs(t, err, ac.ErrInvalidScope) }) t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { - _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeService()) + _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:") require.ErrorIs(t, err, ac.ErrInvalidScope) }) t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { - folderService := foldertest.NewFakeService() - _, resolver := NewFolderNameScopeResolver(folderService) + folderStore := foldertest.NewFakeFolderStore(t) + _, resolver := NewFolderNameScopeResolver(folderStore, foldertest.NewFakeService()) orgId := rand.Int63() - folderService.ExpectedError = ErrDashboardNotFound + folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() scope := "folders:name:" + util.GenerateShortUID() @@ -97,26 +104,29 @@ func TestNewFolderNameScopeResolver(t *testing.T) { func TestNewFolderIDScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewFolderIDScopeResolver(foldertest.NewFakeService()) + prefix, _ := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) require.Equal(t, "folders:id:", prefix) }) t.Run("resolver should convert to uid scope", func(t *testing.T) { - folderSvc := foldertest.NewFakeService() + folderStore := foldertest.NewFakeFolderStore(t) + _, resolver := NewFolderIDScopeResolver(folderStore, foldertest.NewFakeService()) + orgId := rand.Int63() uid := util.GenerateShortUID() db := &folder.Folder{ID: rand.Int63(), UID: uid} - folderSvc.ExpectedFolder = db - - _, resolver := NewFolderIDScopeResolver(folderSvc) + folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() scope := "folders:id:" + strconv.FormatInt(db.ID, 10) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 1) require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) + + folderStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID) }) t.Run("resolver should should include inherited scopes if any", func(t *testing.T) { + folderStore := foldertest.NewFakeFolderStore(t) folderSvc := foldertest.NewFakeService() folderSvc.ExpectedFolders = []*folder.Folder{ { @@ -126,12 +136,12 @@ func TestNewFolderIDScopeResolver(t *testing.T) { UID: "grandparent", }, } - _, resolver := NewFolderIDScopeResolver(folderSvc) + _, resolver := NewFolderIDScopeResolver(folderStore, folderSvc) orgId := rand.Int63() uid := util.GenerateShortUID() db := &folder.Folder{ID: rand.Int63(), UID: uid} - folderSvc.ExpectedFolder = db + folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() scope := "folders:id:" + strconv.FormatInt(db.ID, 10) @@ -146,9 +156,11 @@ func TestNewFolderIDScopeResolver(t *testing.T) { }, resolvedScopes); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } + + folderStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeService()) + _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123") require.ErrorIs(t, err, ac.ErrInvalidScope) @@ -158,7 +170,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) { var ( orgId = rand.Int63() scope = "folders:id:0" - _, resolver = NewFolderIDScopeResolver(foldertest.NewFakeService()) + _, resolver = NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) ) resolved, err := resolver.Resolve(context.Background(), orgId, scope) @@ -169,18 +181,17 @@ func TestNewFolderIDScopeResolver(t *testing.T) { }) t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { - _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeService()) + _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:") require.ErrorIs(t, err, ac.ErrInvalidScope) }) t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { - svc := foldertest.NewFakeService() - svc.ExpectedError = ErrDashboardNotFound - _, resolver := NewFolderIDScopeResolver(svc) + folderStore := foldertest.NewFakeFolderStore(t) + folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() + _, resolver := NewFolderIDScopeResolver(folderStore, foldertest.NewFakeService()) orgId := rand.Int63() - scope := "folders:id:10" resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) require.ErrorIs(t, err, ErrDashboardNotFound) @@ -190,22 +201,21 @@ func TestNewFolderIDScopeResolver(t *testing.T) { func TestNewDashboardIDScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) + prefix, _ := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) require.Equal(t, "dashboards:id:", prefix) }) t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) { - dashSvc := NewFakeDashboardService(t) - folderService := foldertest.NewFakeService() - _, resolver := NewDashboardIDScopeResolver(dashSvc, folderService) + folderStore := foldertest.NewFakeFolderStore(t) + dashSvc := &FakeDashboardService{} + _, resolver := NewDashboardIDScopeResolver(folderStore, dashSvc, foldertest.NewFakeService()) orgID := rand.Int63() folder := &folder.Folder{ID: 2, UID: "2"} dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} - + dashSvc.On("G") + folderStore.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() - folderService.ExpectedFolder = folder - scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.ID, 10)) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) require.NoError(t, err) @@ -216,6 +226,7 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { t.Run("resolver should inlude inherited scopes if any", func(t *testing.T) { dashSvc := &FakeDashboardService{} + folderStore := foldertest.NewFakeFolderStore(t) folderSvc := foldertest.NewFakeService() folderSvc.ExpectedFolders = []*folder.Folder{ { @@ -225,13 +236,14 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { UID: "grandparent", }, } - _, resolver := NewDashboardIDScopeResolver(dashSvc, folderSvc) + _, resolver := NewDashboardIDScopeResolver(folderStore, dashSvc, folderSvc) orgID := rand.Int63() folder := &folder.Folder{ID: 2, UID: "2"} dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} - folderSvc.ExpectedFolder = folder + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() + folderStore.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.ID, 10)) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) @@ -249,14 +261,14 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewDashboardIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) + _, resolver := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123") require.ErrorIs(t, err, ac.ErrInvalidScope) }) t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) { dashSvc := &FakeDashboardService{} - _, resolver := NewDashboardIDScopeResolver(dashSvc, foldertest.NewFakeService()) + _, resolver := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), dashSvc, foldertest.NewFakeService()) dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) @@ -271,21 +283,21 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { func TestNewDashboardUIDScopeResolver(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) { - prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) + prefix, _ := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) require.Equal(t, "dashboards:uid:", prefix) }) t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) { - service := &FakeDashboardService{} - folderSvc := foldertest.NewFakeService() - _, resolver := NewDashboardUIDScopeResolver(service, folderSvc) + folderStore := foldertest.NewFakeFolderStore(t) + dashSvc := &FakeDashboardService{} + _, resolver := NewDashboardUIDScopeResolver(folderStore, dashSvc, foldertest.NewFakeService()) orgID := rand.Int63() folder := &folder.Folder{ID: 2, UID: "2"} - folderSvc.ExpectedFolder = folder dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} - service.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() + folderStore.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() scope := ac.Scope("dashboards", "uid", dashboard.UID) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) require.NoError(t, err) @@ -295,7 +307,7 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) { }) t.Run("resolver should include inherited scopes if any", func(t *testing.T) { - svc := &FakeDashboardService{} + folderStore := foldertest.NewFakeFolderStore(t) folderSvc := foldertest.NewFakeService() folderSvc.ExpectedFolders = []*folder.Folder{ { @@ -305,14 +317,14 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) { UID: "grandparent", }, } - - _, resolver := NewDashboardUIDScopeResolver(svc, folderSvc) + dashSvc := &FakeDashboardService{} + _, resolver := NewDashboardUIDScopeResolver(folderStore, dashSvc, folderSvc) orgID := rand.Int63() folder := &folder.Folder{ID: 2, UID: "2"} - folderSvc.ExpectedFolder = folder dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} - svc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() + dashSvc.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() + folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(folder, nil).Once() scope := ac.Scope("dashboards", "uid", dashboard.UID) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) @@ -330,14 +342,14 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) { }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { - _, resolver := NewDashboardUIDScopeResolver(&FakeDashboardService{}, foldertest.NewFakeService()) + _, resolver := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123") require.ErrorIs(t, err, ac.ErrInvalidScope) }) t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) { service := &FakeDashboardService{} - _, resolver := NewDashboardUIDScopeResolver(service, foldertest.NewFakeService()) + _, resolver := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), service, foldertest.NewFakeService()) dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} service.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 95fc5a9ae73..ade2cca5048 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -40,6 +40,7 @@ type DashboardServiceImpl struct { cfg *setting.Cfg log log.Logger dashboardStore dashboards.Store + folderStore folder.FolderStore folderService folder.Service dashAlertExtractor alerting.DashAlertExtractor features featuremgmt.FeatureToggles @@ -50,7 +51,7 @@ type DashboardServiceImpl struct { // This is the uber service that implements a three smaller services func ProvideDashboardService( - cfg *setting.Cfg, dashboardStore dashboards.Store, dashAlertExtractor alerting.DashAlertExtractor, + cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore folder.FolderStore, dashAlertExtractor alerting.DashAlertExtractor, features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl, folderSvc folder.Service, @@ -64,11 +65,12 @@ func ProvideDashboardService( folderPermissions: folderPermissionsService, dashboardPermissions: dashboardPermissionsService, ac: ac, + folderStore: folderStore, folderService: folderSvc, } - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashSvc, folderSvc)) - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashSvc, folderSvc)) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderStore, dashSvc, folderSvc)) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, dashSvc, folderSvc)) return dashSvc } @@ -631,7 +633,7 @@ func (dr DashboardServiceImpl) CountDashboardsInFolder(ctx context.Context, quer return 0, err } - folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{UID: &query.FolderUID, OrgID: u.OrgID}) + folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{UID: &query.FolderUID, OrgID: u.OrgID, SignedInUser: u}) if err != nil { return 0, err } diff --git a/pkg/services/dashboards/service/dashboard_service_integration_test.go b/pkg/services/dashboards/service/dashboard_service_integration_test.go index 33059f97ad5..f91ffbe27c3 100644 --- a/pkg/services/dashboards/service/dashboard_service_integration_test.go +++ b/pkg/services/dashboards/service/dashboard_service_integration_test.go @@ -15,6 +15,7 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/folder/folderimpl" "github.com/grafana/grafana/pkg/services/folder/foldertest" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/org" @@ -826,8 +827,9 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc quotaService := quotatest.New(false, nil) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) require.NoError(t, err) + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := ProvideDashboardService( - cfg, dashboardStore, &dummyDashAlertExtractor{}, + cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{}, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), @@ -886,8 +888,9 @@ func callSaveWithResult(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSt quotaService := quotatest.New(false, nil) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) require.NoError(t, err) + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := ProvideDashboardService( - cfg, dashboardStore, &dummyDashAlertExtractor{}, + cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{}, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), @@ -908,8 +911,9 @@ func callSaveWithError(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSto quotaService := quotatest.New(false, nil) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) require.NoError(t, err) + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := ProvideDashboardService( - cfg, dashboardStore, &dummyDashAlertExtractor{}, + cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{}, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), @@ -948,8 +952,9 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto quotaService := quotatest.New(false, nil) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) require.NoError(t, err) + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := ProvideDashboardService( - cfg, dashboardStore, &dummyDashAlertExtractor{}, + cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{}, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), @@ -989,8 +994,9 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da quotaService := quotatest.New(false, nil) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) require.NoError(t, err) + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := ProvideDashboardService( - cfg, dashboardStore, &dummyDashAlertExtractor{}, + cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{}, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), diff --git a/pkg/services/folder/folderimpl/folder.go b/pkg/services/folder/folderimpl/folder.go index 5463a28b744..d6258f5d958 100644 --- a/pkg/services/folder/folderimpl/folder.go +++ b/pkg/services/folder/folderimpl/folder.go @@ -62,8 +62,8 @@ func ProvideService( srv.DBMigration(db) } - ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(srv)) - ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(srv)) + ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(folderStore, srv)) + ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, srv)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(srv)) return srv } diff --git a/pkg/services/guardian/accesscontrol_guardian_test.go b/pkg/services/guardian/accesscontrol_guardian_test.go index a9af4ff18eb..b5ddc437648 100644 --- a/pkg/services/guardian/accesscontrol_guardian_test.go +++ b/pkg/services/guardian/accesscontrol_guardian_test.go @@ -618,7 +618,7 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce ac := accesscontrolmock.New().WithPermissions(permissions) // TODO replace with actual folder store implementation after resolving import cycles - ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashboardSvc, foldertest.NewFakeService())) + ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), dashboardSvc, foldertest.NewFakeService())) license := licensingtest.NewFakeLicensing() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() teamSvc := teamimpl.ProvideService(store, store.Cfg) diff --git a/pkg/services/libraryelements/libraryelements_test.go b/pkg/services/libraryelements/libraryelements_test.go index 91097c89ff8..516484efe7f 100644 --- a/pkg/services/libraryelements/libraryelements_test.go +++ b/pkg/services/libraryelements/libraryelements_test.go @@ -294,8 +294,9 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash ac := acmock.New() folderPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService() + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := dashboardservice.ProvideDashboardService( - cfg, dashboardStore, dashAlertExtractor, + cfg, dashboardStore, folderStore, dashAlertExtractor, features, folderPermissions, dashboardPermissions, ac, foldertest.NewFakeService(), ) @@ -441,7 +442,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo dashboardPermissions := acmock.NewMockedPermissionsService() folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) dashboardService := dashboardservice.ProvideDashboardService( - sqlStore.Cfg, dashboardStore, nil, + sqlStore.Cfg, dashboardStore, folderStore, nil, features, folderPermissions, dashboardPermissions, ac, foldertest.NewFakeService(), ) diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index e54e64e8c49..57451f99261 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -706,8 +706,9 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash require.NoError(t, err) dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil, nil) ac := acmock.New() + folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) service := dashboardservice.ProvideDashboardService( - cfg, dashboardStore, dashAlertService, + cfg, dashboardStore, folderStore, dashAlertService, featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), acmock.NewMockedPermissionsService(), ac, foldertest.NewFakeService(), ) diff --git a/pkg/services/ngalert/tests/util.go b/pkg/services/ngalert/tests/util.go index e9bcc9d999c..7c8728a3485 100644 --- a/pkg/services/ngalert/tests/util.go +++ b/pkg/services/ngalert/tests/util.go @@ -82,7 +82,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG, folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) dashboardService := dashboardservice.ProvideDashboardService( - cfg, dashboardStore, nil, + cfg, dashboardStore, folderStore, nil, features, folderPermissions, dashboardPermissions, ac, foldertest.NewFakeService(), )