Access control: Modify dashboard/folder resolvers so that return also the inherited scopes (#62025)

* Access Control: Add folder service dependency to the dashboard/folder resolvers

* Expose the function fetching parents to folder interface

* Add generic prepend utility

* Modify dashboard resolvers to return inherited scopes
This commit is contained in:
Sofia Papagiannaki 2023-01-26 10:21:10 +02:00 committed by GitHub
parent 8e3d22ca7a
commit cd27562c76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 276 additions and 35 deletions

View File

@ -40,6 +40,7 @@ import (
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion" dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/login" "github.com/grafana/grafana/pkg/services/login"
@ -392,6 +393,8 @@ func setupHTTPServerWithCfgDb(
folderPermissionsService := accesscontrolmock.NewMockedPermissionsService() folderPermissionsService := accesscontrolmock.NewMockedPermissionsService()
dashboardPermissionsService := accesscontrolmock.NewMockedPermissionsService() dashboardPermissionsService := accesscontrolmock.NewMockedPermissionsService()
folderSvc := foldertest.NewFakeService()
// Create minimal HTTP Server // Create minimal HTTP Server
hs := &HTTPServer{ hs := &HTTPServer{
Cfg: cfg, Cfg: cfg,
@ -408,6 +411,7 @@ func setupHTTPServerWithCfgDb(
DashboardService: dashboardservice.ProvideDashboardService( DashboardService: dashboardservice.ProvideDashboardService(
cfg, dashboardsStore, dashboardsStore, nil, features, cfg, dashboardsStore, dashboardsStore, nil, features,
folderPermissionsService, dashboardPermissionsService, ac, folderPermissionsService, dashboardPermissionsService, ac,
folderSvc,
), ),
preferenceService: preftest.NewPreferenceServiceFake(), preferenceService: preftest.NewPreferenceServiceFake(),
userService: userSvc, userService: userSvc,

View File

@ -12,12 +12,15 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/db/dbtest" "github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -37,12 +40,14 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
folderPermissions := accesscontrolmock.NewMockedPermissionsService() folderPermissions := accesscontrolmock.NewMockedPermissionsService()
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService() dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), settings, dashboardStore, dashboards.NewFakeFolderStore(t), mockSQLStore, featuremgmt.WithFeatures(), nil)
hs := &HTTPServer{ hs := &HTTPServer{
Cfg: settings, Cfg: settings,
SQLStore: mockSQLStore, SQLStore: mockSQLStore,
Features: features, Features: features,
DashboardService: dashboardservice.ProvideDashboardService( DashboardService: dashboardservice.ProvideDashboardService(
settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac, settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
folderSvc,
), ),
AccessControl: accesscontrolmock.New().WithDisabled(), AccessControl: accesscontrolmock.New().WithDisabled(),
} }

View File

@ -16,9 +16,11 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/db/dbtest" "github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
@ -35,6 +37,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest" "github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
"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/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/folder/foldertest" "github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/libraryelements"
@ -980,10 +983,13 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService() dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, folderStore, db.InitTestDB(t), featuremgmt.WithFeatures(), nil)
if dashboardService == nil { if dashboardService == nil {
dashboardService = service.ProvideDashboardService( dashboardService = service.ProvideDashboardService(
cfg, dashboardStore, folderStore, nil, features, cfg, dashboardStore, folderStore, nil, features,
folderPermissions, dashboardPermissions, ac, folderPermissions, dashboardPermissions, ac,
folderSvc,
) )
} }
@ -997,6 +1003,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
dashboardProvisioningService: service.ProvideDashboardService( dashboardProvisioningService: service.ProvideDashboardService(
cfg, dashboardStore, folderStore, nil, features, cfg, dashboardStore, folderStore, nil, features,
folderPermissions, dashboardPermissions, ac, folderPermissions, dashboardPermissions, ac,
folderSvc,
), ),
DashboardService: dashboardService, DashboardService: dashboardService,
Features: featuremgmt.WithFeatures(), Features: featuremgmt.WithFeatures(),

View File

@ -46,6 +46,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
dashboardPermissionsService: dashboardPermissions, dashboardPermissionsService: dashboardPermissions,
DashboardService: service.ProvideDashboardService( DashboardService: service.ProvideDashboardService(
settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac, settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
folderService,
), ),
AccessControl: accesscontrolmock.New().WithDisabled(), AccessControl: accesscontrolmock.New().WithDisabled(),
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/folder"
) )
const ( const (
@ -38,7 +39,7 @@ var (
) )
// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope. // NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
func NewFolderNameScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) { func NewFolderNameScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScopeName("") prefix := ScopeFoldersProvider.GetResourceScopeName("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -52,12 +53,19 @@ func NewFolderNameScopeResolver(db Store, folderDB FolderStore) (string, ac.Scop
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil
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
}) })
} }
// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope. // NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
func NewFolderIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) { func NewFolderIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScope("") prefix := ScopeFoldersProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -78,13 +86,19 @@ func NewFolderIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeA
return nil, err return nil, err
} }
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil 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
}) })
} }
// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:" // NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder // into uid based scopes for both dashboard and folder
func NewDashboardIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) { func NewDashboardIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScope("") prefix := ScopeDashboardsProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -101,13 +115,13 @@ func NewDashboardIDScopeResolver(db Store, folderDB FolderStore) (string, ac.Sco
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard) return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard, folderSvc)
}) })
} }
// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:" // NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder // into uid based scopes for both dashboard and folder
func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) { func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScopeUID("") prefix := ScopeDashboardsProvider.GetResourceScopeUID("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -124,11 +138,11 @@ func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore) (string, ac.Sc
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard) return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard, folderSvc)
}) })
} }
func resolveDashboardScope(ctx context.Context, db Store, folderDB FolderStore, orgID int64, dashboard *Dashboard) ([]string, error) { func resolveDashboardScope(ctx context.Context, db Store, folderDB FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) {
var folderUID string var folderUID string
if dashboard.FolderID < 0 { if dashboard.FolderID < 0 {
return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil
@ -144,8 +158,36 @@ func resolveDashboardScope(ctx context.Context, db Store, folderDB FolderStore,
folderUID = folder.UID folderUID = folder.UID
} }
return []string{ result, err := getInheritedScopes(ctx, orgID, folderUID, folderSvc)
if err != nil {
return nil, err
}
result = append([]string{
ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID), ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID),
ScopeFoldersProvider.GetResourceScopeUID(folderUID), ScopeFoldersProvider.GetResourceScopeUID(folderUID),
}, nil },
result...,
)
return result, nil
}
func getInheritedScopes(ctx context.Context, orgID int64, folderUID string, folderSvc folder.Service) ([]string, error) {
ancestors, err := folderSvc.GetParents(ctx, folder.GetParentsQuery{
UID: folderUID,
OrgID: orgID,
})
if err != nil {
// TODO return a specific error
return nil, err
}
result := make([]string, 0, len(ancestors))
for _, ff := range ancestors {
result = append(result, ScopeFoldersProvider.GetResourceScopeUID(ff.UID))
}
return result, nil
} }

View File

@ -7,17 +7,19 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
func TestNewFolderNameScopeResolver(t *testing.T) { func TestNewFolderNameScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "folders:name:", prefix) require.Equal(t, "folders:name:", prefix)
}) })
@ -33,7 +35,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
scope := "folders:name:" + title scope := "folders:name:" + title
_, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore) _, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
require.NoError(t, err) require.NoError(t, err)
@ -43,16 +45,53 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title) folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title)
}) })
t.Run("resolver should include inherited scopes if any", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
orgId := rand.Int63()
title := "Very complex :title with: and /" + util.GenerateShortUID()
db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()}
folderStore := NewFakeFolderStore(t)
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
scope := "folders:name:" + title
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
{
UID: "parent",
},
{
UID: "grandparent",
},
}
_, resolver := NewFolderNameScopeResolver(dashboardStore, 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", 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) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t)) _, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
}) })
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t)) _, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -61,7 +100,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t) folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore) _, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
orgId := rand.Int63() orgId := rand.Int63()
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
@ -76,7 +115,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
func TestNewFolderIDScopeResolver(t *testing.T) { func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "folders:id:", prefix) require.Equal(t, "folders:id:", prefix)
}) })
@ -84,7 +123,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t) folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
orgId := rand.Int63() orgId := rand.Int63()
uid := util.GenerateShortUID() uid := util.GenerateShortUID()
@ -101,9 +140,46 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
folderStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID) folderStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID)
}) })
t.Run("resolver should should include inherited scopes if any", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
{
UID: "parent",
},
{
UID: "grandparent",
},
}
_, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore, folderSvc)
orgId := rand.Int63()
uid := util.GenerateShortUID()
db := &folder.Folder{ID: rand.Int63(), UID: uid}
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, 3)
if diff := cmp.Diff([]string{
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, "GetFolderByID", mock.Anything, orgId, db.ID)
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t)) _, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -114,7 +190,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
dashboardStore = &FakeDashboardStore{} dashboardStore = &FakeDashboardStore{}
orgId = rand.Int63() orgId = rand.Int63()
scope = "folders:id:0" scope = "folders:id:0"
_, resolver = NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t)) _, resolver = NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
) )
resolved, err := resolver.Resolve(context.Background(), orgId, scope) resolved, err := resolver.Resolve(context.Background(), orgId, scope)
@ -126,7 +202,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t)) _, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -135,7 +211,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t) folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
orgId := rand.Int63() orgId := rand.Int63()
folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
@ -149,7 +225,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
func TestNewDashboardIDScopeResolver(t *testing.T) { func TestNewDashboardIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "dashboards:id:", prefix) require.Equal(t, "dashboards:id:", prefix)
}) })
@ -157,7 +233,7 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t) folderStore := NewFakeFolderStore(t)
_, resolver := NewDashboardIDScopeResolver(store, folderStore) _, resolver := NewDashboardIDScopeResolver(store, folderStore, foldertest.NewFakeService())
orgID := rand.Int63() orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"} folder := &folder.Folder{ID: 2, UID: "2"}
@ -174,15 +250,52 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1]) require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1])
}) })
t.Run("resolver should inlude inherited scopes if any", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
{
UID: "parent",
},
{
UID: "grandparent",
},
}
_, resolver := NewDashboardIDScopeResolver(store, folderStore, folderSvc)
orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"}
dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"}
store.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)
require.NoError(t, err)
require.Len(t, resolvedScopes, 4)
if diff := cmp.Diff([]string{
fmt.Sprintf("dashboards:uid:%s", dashboard.UID),
fmt.Sprintf("folders:uid:%s", folder.UID),
"folders:uid:parent",
"folders:uid:grandparent",
}, resolvedScopes); diff != "" {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) _, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) 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) { t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
_, resolver := NewDashboardIDScopeResolver(store, NewFakeFolderStore(t)) _, resolver := NewDashboardIDScopeResolver(store, NewFakeFolderStore(t), foldertest.NewFakeService())
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)
@ -197,14 +310,14 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
func TestNewDashboardUIDScopeResolver(t *testing.T) { func TestNewDashboardUIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "dashboards:uid:", prefix) require.Equal(t, "dashboards:uid:", prefix)
}) })
t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) { t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t) folderStore := NewFakeFolderStore(t)
_, resolver := NewDashboardUIDScopeResolver(store, folderStore) _, resolver := NewDashboardUIDScopeResolver(store, folderStore, foldertest.NewFakeService())
orgID := rand.Int63() orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"} folder := &folder.Folder{ID: 2, UID: "2"}
@ -221,15 +334,53 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) {
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1]) require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1])
}) })
t.Run("resolver should include inherited scopes if any", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
{
UID: "parent",
},
{
UID: "grandparent",
},
}
_, resolver := NewDashboardUIDScopeResolver(store, folderStore, folderSvc)
orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"}
dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"}
store.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)
require.Len(t, resolvedScopes, 4)
if diff := cmp.Diff([]string{
fmt.Sprintf("dashboards:uid:%s", dashboard.UID),
fmt.Sprintf("folders:uid:%s", folder.UID),
"folders:uid:parent",
"folders:uid:grandparent",
}, resolvedScopes); diff != "" {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t)) _, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) 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) { t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
_, resolver := NewDashboardUIDScopeResolver(store, NewFakeFolderStore(t)) _, resolver := NewDashboardUIDScopeResolver(store, NewFakeFolderStore(t), foldertest.NewFakeService())
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)

View File

@ -53,9 +53,10 @@ func ProvideDashboardService(
cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore dashboards.FolderStore, dashAlertExtractor alerting.DashAlertExtractor, cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore dashboards.FolderStore, dashAlertExtractor alerting.DashAlertExtractor,
features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService, features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl, dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl,
folderSvc folder.Service,
) *DashboardServiceImpl { ) *DashboardServiceImpl {
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashboardStore, folderStore)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashboardStore, folderStore, folderSvc))
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashboardStore, folderStore)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashboardStore, folderStore, folderSvc))
return &DashboardServiceImpl{ return &DashboardServiceImpl{
cfg: cfg, cfg: cfg,

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotatest"
@ -831,6 +832,7 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
foldertest.NewFakeService(),
) )
guardian.InitLegacyGuardian(sqlStore, service, &teamtest.FakeService{}) guardian.InitLegacyGuardian(sqlStore, service, &teamtest.FakeService{})
@ -890,6 +892,7 @@ func callSaveWithResult(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSt
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
foldertest.NewFakeService(),
) )
res, err := service.SaveDashboard(context.Background(), &dto, false) res, err := service.SaveDashboard(context.Background(), &dto, false)
require.NoError(t, err) require.NoError(t, err)
@ -911,6 +914,7 @@ func callSaveWithError(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSto
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
foldertest.NewFakeService(),
) )
_, err = service.SaveDashboard(context.Background(), &dto, false) _, err = service.SaveDashboard(context.Background(), &dto, false)
return err return err
@ -950,6 +954,7 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
foldertest.NewFakeService(),
) )
res, err := service.SaveDashboard(context.Background(), &dto, false) res, err := service.SaveDashboard(context.Background(), &dto, false)
require.NoError(t, err) require.NoError(t, err)
@ -990,6 +995,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
foldertest.NewFakeService(),
) )
res, err := service.SaveDashboard(context.Background(), &dto, false) res, err := service.SaveDashboard(context.Background(), &dto, false)
require.NoError(t, err) require.NoError(t, err)

View File

@ -51,8 +51,6 @@ func ProvideService(
features featuremgmt.FeatureToggles, features featuremgmt.FeatureToggles,
folderPermissionsService accesscontrol.FolderPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
) folder.Service { ) folder.Service {
ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore, folderStore))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore, folderStore))
store := ProvideStore(db, cfg, features) store := ProvideStore(db, cfg, features)
svr := &Service{ svr := &Service{
cfg: cfg, cfg: cfg,
@ -68,6 +66,9 @@ func ProvideService(
if features.IsEnabled(featuremgmt.FlagNestedFolders) { if features.IsEnabled(featuremgmt.FlagNestedFolders) {
svr.DBMigration(db) svr.DBMigration(db)
} }
ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore, folderStore, svr))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore, folderStore, svr))
return svr return svr
} }
@ -185,6 +186,13 @@ func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery)
return filtered, nil return filtered, nil
} }
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if !s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
return nil, nil
}
return s.store.GetParents(ctx, q)
}
func (s *Service) getFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*folder.Folder, error) { func (s *Service) getFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*folder.Folder, error) {
if id == 0 { if id == 0 {
return &folder.Folder{ID: id, Title: "General"}, nil return &folder.Folder{ID: id, Title: "General"}, nil

View File

@ -21,6 +21,11 @@ var _ folder.Service = (*FakeService)(nil)
func (s *FakeService) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery) ([]*folder.Folder, error) { func (s *FakeService) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery) ([]*folder.Folder, error) {
return s.ExpectedFolders, s.ExpectedError return s.ExpectedFolders, s.ExpectedError
} }
func (s *FakeService) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
return s.ExpectedFolders, s.ExpectedError
}
func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) { func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
return s.ExpectedFolder, s.ExpectedError return s.ExpectedFolder, s.ExpectedError
} }

View File

@ -7,6 +7,9 @@ import (
type Service interface { type Service interface {
// GetChildren returns an array containing all child folders. // GetChildren returns an array containing all child folders.
GetChildren(ctx context.Context, cmd *GetChildrenQuery) ([]*Folder, error) GetChildren(ctx context.Context, cmd *GetChildrenQuery) ([]*Folder, error)
// GetParents returns an array containing add parent folders if nested folders are enabled
// otherwise it returns an empty array
GetParents(ctx context.Context, q GetParentsQuery) ([]*Folder, error)
Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error) Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error)
// GetFolder takes a GetFolderCommand and returns a folder matching the // GetFolder takes a GetFolderCommand and returns a folder matching the

View File

@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
dashdb "github.com/grafana/grafana/pkg/services/dashboards/database" dashdb "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest" "github.com/grafana/grafana/pkg/services/licensing/licensingtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
@ -601,7 +602,7 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
}) })
require.NoError(t, err) require.NoError(t, err)
ac := accesscontrolmock.New().WithPermissions(permissions) ac := accesscontrolmock.New().WithPermissions(permissions)
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore, dashStore)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore, dashStore, foldertest.NewFakeService()))
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
teamSvc := teamimpl.ProvideService(store, store.Cfg) teamSvc := teamimpl.ProvideService(store, store.Cfg)

View File

@ -28,6 +28,7 @@ import (
"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/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl" "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/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
@ -293,6 +294,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
service := dashboardservice.ProvideDashboardService( service := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashboardStore, dashAlertExtractor, cfg, dashboardStore, dashboardStore, dashAlertExtractor,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
) )
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true) dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
require.NoError(t, err) require.NoError(t, err)
@ -437,6 +439,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
dashboardService := dashboardservice.ProvideDashboardService( dashboardService := dashboardservice.ProvideDashboardService(
sqlStore.Cfg, dashboardStore, dashboardStore, nil, sqlStore.Cfg, dashboardStore, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
) )
guardian.InitLegacyGuardian(sqlStore, dashboardService, &teamtest.FakeService{}) guardian.InitLegacyGuardian(sqlStore, dashboardService, &teamtest.FakeService{})
service := LibraryElementService{ service := LibraryElementService{

View File

@ -27,6 +27,7 @@ import (
"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/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl" "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/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -706,6 +707,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash
service := dashboardservice.ProvideDashboardService( service := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashboardStore, dashAlertService, cfg, dashboardStore, dashboardStore, dashAlertService,
featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), acmock.NewMockedPermissionsService(), ac, featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), acmock.NewMockedPermissionsService(), ac,
foldertest.NewFakeService(),
) )
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true) dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
require.NoError(t, err) require.NoError(t, err)

View File

@ -28,6 +28,7 @@ import (
"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/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl" "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/guardian"
"github.com/grafana/grafana/pkg/services/ngalert" "github.com/grafana/grafana/pkg/services/ngalert"
"github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/metrics"
@ -81,6 +82,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
dashboardService := dashboardservice.ProvideDashboardService( dashboardService := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashboardStore, nil, cfg, dashboardStore, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
) )
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()