Folders: Set folder creation permission as part of legacy create (#94040)

* Add folder store to dashboard permissions
* Include folder store in annotation scope resolver
* Add folder store when initialising library elements
* Include folder store in search v2 service initialisation
* Include folder store in GetInheritedScopes
* Add folder store to folder permissions provider
* Include cfg, folder permissions in folder service
* Move setting of folder permissions for folder service create method
This commit is contained in:
Arati R.
2024-10-01 14:03:02 +02:00
committed by GitHub
parent 2a73b89374
commit e399fe6d09
31 changed files with 269 additions and 137 deletions

View File

@ -587,7 +587,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *contextmodel.ReqContext) response.Res
// where <type> is the type of annotation with id <id>. // where <type> is the type of annotation with id <id>.
// If annotationPermissionUpdate feature toggle is enabled, dashboard annotation scope will be resolved to the corresponding // If annotationPermissionUpdate feature toggle is enabled, dashboard annotation scope will be resolved to the corresponding
// dashboard and folder scopes (eg, "dashboards:uid:<annotation_dashboard_uid>", "folders:uid:<parent_folder_uid>" etc). // dashboard and folder scopes (eg, "dashboards:uid:<annotation_dashboard_uid>", "folders:uid:<parent_folder_uid>" etc).
func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, features featuremgmt.FeatureToggles, dashSvc dashboards.DashboardService, folderSvc folder.Service) (string, accesscontrol.ScopeAttributeResolver) { func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, features featuremgmt.FeatureToggles, dashSvc dashboards.DashboardService, folderStore folder.Store) (string, accesscontrol.ScopeAttributeResolver) {
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("") prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) { return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
scopeParts := strings.Split(initialScope, ":") scopeParts := strings.Split(initialScope, ":")
@ -649,7 +649,7 @@ func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, feature
// Append dashboard parent scopes if dashboard is in a folder or the general scope if dashboard is not in a folder // Append dashboard parent scopes if dashboard is in a folder or the general scope if dashboard is not in a folder
if dashboard.FolderUID != "" { if dashboard.FolderUID != "" {
scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(dashboard.FolderUID)) scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(dashboard.FolderUID))
inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, dashboard.FolderUID, folderSvc) inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, dashboard.FolderUID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -397,14 +397,15 @@ func TestAPI_Annotations(t *testing.T) {
dashService := &dashboards.FakeDashboardService{} dashService := &dashboards.FakeDashboardService{}
dashService.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{UID: dashUID, FolderUID: folderUID, FolderID: 1}, nil) dashService.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{UID: dashUID, FolderUID: folderUID, FolderID: 1}, nil)
folderService := &foldertest.FakeService{} folderService := &foldertest.FakeService{}
fStore := folder.NewFakeStore()
folderService.ExpectedFolder = &folder.Folder{UID: folderUID, ID: 1} folderService.ExpectedFolder = &folder.Folder{UID: folderUID, ID: 1}
folderDB := &foldertest.FakeFolderStore{} folderDB := &foldertest.FakeFolderStore{}
folderDB.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(&folder.Folder{UID: folderUID, ID: 1}, nil) folderDB.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(&folder.Folder{UID: folderUID, ID: 1}, nil)
hs.DashboardService = dashService hs.DashboardService = dashService
hs.folderService = folderService hs.folderService = folderService
hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()) hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo, hs.Features, dashService, folderService)) hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo, hs.Features, dashService, fStore))
hs.AccessControl.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderDB, dashService, folderService)) hs.AccessControl.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderDB, dashService, fStore))
}) })
var body io.Reader var body io.Reader
if tt.body != "" { if tt.body != "" {
@ -504,7 +505,7 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
features := featuremgmt.WithFeatures(tc.featureToggles...) features := featuremgmt.WithFeatures(tc.featureToggles...)
prefix, resolver := AnnotationTypeScopeResolver(fakeAnnoRepo, features, dashSvc, &foldertest.FakeService{}) prefix, resolver := AnnotationTypeScopeResolver(fakeAnnoRepo, features, dashSvc, folder.NewFakeStore())
require.Equal(t, "annotations:id:", prefix) require.Equal(t, "annotations:id:", prefix)
resolved, err := resolver.Resolve(context.Background(), 1, tc.given) resolved, err := resolver.Resolve(context.Background(), 1, tc.given)

View File

@ -836,7 +836,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
db := db.InitTestDB(t) db := db.InitTestDB(t)
fStore := folderimpl.ProvideStore(db) fStore := folderimpl.ProvideStore(db)
folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()),
dashboardStore, folderStore, db, features, dashboardStore, folderStore, db, features, cfg, folderPermissions,
supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
if dashboardService == nil { if dashboardService == nil {
dashboardService, err = service.ProvideDashboardServiceImpl( dashboardService, err = service.ProvideDashboardServiceImpl(

View File

@ -1,7 +1,6 @@
package api package api
import ( import (
"context"
"errors" "errors"
"net/http" "net/http"
"strconv" "strconv"
@ -12,12 +11,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"github.com/grafana/authlib/claims"
"github.com/grafana/grafana/pkg/api/apierrors" "github.com/grafana/grafana/pkg/api/apierrors"
"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/apimachinery/identity"
folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
"github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders" internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders"
@ -31,7 +28,6 @@ import (
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements/model" "github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errhttp" "github.com/grafana/grafana/pkg/util/errhttp"
@ -219,10 +215,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
return apierrors.ToFolderErrorResponse(err) return apierrors.ToFolderErrorResponse(err)
} }
if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil {
hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err)
}
// Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call // Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object // Required for cases when caller wants to immediately interact with the newly created object
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser) hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
@ -236,36 +228,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
return response.JSON(http.StatusOK, folderDTO) return response.JSON(http.StatusOK, folderDTO)
} }
func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
if !hs.Cfg.RBAC.PermissionsOnCreation("folder") {
return nil
}
var permissions []accesscontrol.SetResourcePermissionCommand
if user.IsIdentityType(claims.TypeUser) {
userID, err := user.GetInternalID()
if err != nil {
return err
}
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(),
})
}
isNested := folder.ParentUID != ""
if !isNested || !hs.Features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
}...)
}
_, err := hs.folderPermissionsService.SetPermissions(ctx, orgID, folder.UID, permissions...)
return err
}
// swagger:route POST /folders/{folder_uid}/move folders moveFolder // swagger:route POST /folders/{folder_uid}/move folders moveFolder
// //
// Move folder. // Move folder.

View File

@ -464,14 +464,15 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog
features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db, permreg.ProvidePermissionRegistry(), features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db, permreg.ProvidePermissionRegistry(),
) )
fStore := folderimpl.ProvideStore(sc.db) fStore := folderimpl.ProvideStore(sc.db)
folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore,
folderStore, sc.db, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions(
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets) cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, fStore, acSvc, sc.teamSvc, sc.userSvc, actionSets)
require.NoError(b, err) require.NoError(b, err)
folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore,
folderStore, sc.db, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions( dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions(
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets) cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, fStore, acSvc, sc.teamSvc, sc.userSvc, actionSets)
require.NoError(b, err) require.NoError(b, err)
dashboardSvc, err := dashboardservice.ProvideDashboardServiceImpl( dashboardSvc, err := dashboardservice.ProvideDashboardServiceImpl(

View File

@ -253,6 +253,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
authInfoService login.AuthInfoService, storageService store.StorageService, authInfoService login.AuthInfoService, storageService store.StorageService,
notificationService notifications.Service, dashboardService dashboards.DashboardService, notificationService notifications.Service, dashboardService dashboards.DashboardService,
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
folderStore folder.Store,
dsGuardian guardian.DatasourceGuardianProvider, dsGuardian guardian.DatasourceGuardianProvider,
dashboardsnapshotsService dashboardsnapshots.Service, pluginSettings pluginSettings.Service, dashboardsnapshotsService dashboardsnapshots.Service, pluginSettings pluginSettings.Service,
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service,
@ -379,7 +380,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
hs.registerRoutes() hs.registerRoutes()
// Register access control scope resolver for annotations // Register access control scope resolver for annotations
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo, features, dashboardService, folderService)) hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo, features, dashboardService, folderStore))
if err := hs.declareFixedRoles(); err != nil { if err := hs.declareFixedRoles(); err != nil {
return nil, err return nil, err

View File

@ -93,7 +93,7 @@ func registerDashboardRoles(cfg *setting.Cfg, features featuremgmt.FeatureToggle
func ProvideDashboardPermissions( func ProvideDashboardPermissions(
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl, cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service, license licensing.Licensing, dashboardStore dashboards.Store, folderStore folder.Store, service accesscontrol.Service,
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService, teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
) (*DashboardPermissionsService, error) { ) (*DashboardPermissionsService, error) {
getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) { getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) {
@ -145,7 +145,7 @@ func ProvideDashboardPermissions(
} }
parentScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(queryResult.UID) parentScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(queryResult.UID)
nestedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, queryResult.UID, folderService) nestedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, queryResult.UID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -84,7 +84,7 @@ func registerFolderRoles(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
func ProvideFolderPermissions( func ProvideFolderPermissions(
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl, cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl,
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service, license licensing.Licensing, dashboardStore dashboards.Store, folderStore folder.Store, service accesscontrol.Service,
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService, teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
) (*FolderPermissionsService, error) { ) (*FolderPermissionsService, error) {
if err := registerFolderRoles(cfg, features, service); err != nil { if err := registerFolderRoles(cfg, features, service); err != nil {
@ -111,7 +111,7 @@ func ProvideFolderPermissions(
return nil return nil
}, },
InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) { InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) {
return dashboards.GetInheritedScopes(ctx, orgID, resourceID, folderService) return dashboards.GetInheritedScopes(ctx, orgID, resourceID, folderStore)
}, },
Assignments: resourcepermissions.Assignments{ Assignments: resourcepermissions.Assignments{
Users: true, Users: true,

View File

@ -0,0 +1,83 @@
package testutil
import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/permreg"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
"github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/supportbundles/bundleregistry"
"github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideFolderPermissions(
features featuremgmt.FeatureToggles,
cfg *setting.Cfg,
sqlStore *sqlstore.SQLStore,
) (*ossaccesscontrol.FolderPermissionsService, error) {
actionSets := resourcepermissions.NewActionSetService(features)
acSvc := acimpl.ProvideOSSService(
cfg, acdb.ProvideService(sqlStore), actionSets, localcache.ProvideService(),
features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sqlStore, permreg.ProvidePermissionRegistry(),
)
license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())
fStore := folderimpl.ProvideStore(sqlStore)
quotaService := quotatest.New(false, nil)
orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService)
if err != nil {
return nil, err
}
teamSvc, err := teamimpl.ProvideService(sqlStore, cfg, tracing.InitializeTracerForTest())
if err != nil {
return nil, err
}
cache := localcache.ProvideService()
userSvc, err := userimpl.ProvideService(
sqlStore,
orgService,
cfg,
teamSvc,
cache,
tracing.InitializeTracerForTest(),
quotaService,
bundleregistry.ProvideService(),
)
if err != nil {
return nil, err
}
return ossaccesscontrol.ProvideFolderPermissions(
cfg,
features,
routing.NewRouteRegister(),
sqlStore,
ac,
license,
&dashboards.FakeDashboardStore{},
fStore,
acSvc,
teamSvc,
userSvc,
actionSets,
)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
ftestutil "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol/testutil"
"github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/annotations/testutil" "github.com/grafana/grafana/pkg/services/annotations/testutil"
"github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/authz/zanzana"
@ -227,10 +228,11 @@ func TestIntegrationAnnotationListingWithInheritedRBAC(t *testing.T) {
}) })
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()) ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
folderPermissions, err := ftestutil.ProvideFolderPermissions(features, cfg, sql)
require.NoError(t, err)
fStore := folderimpl.ProvideStore(sql) fStore := folderimpl.ProvideStore(sql)
folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore,
folderimpl.ProvideDashboardFolderStore(sql), sql, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) folderimpl.ProvideDashboardFolderStore(sql), sql, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
cfg.AnnotationMaximumTagsLength = 60 cfg.AnnotationMaximumTagsLength = 60
store := NewXormStore(cfg, log.New("annotation.test"), sql, tagService) store := NewXormStore(cfg, log.New("annotation.test"), sql, tagService)

View File

@ -43,7 +43,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(folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func NewFolderNameScopeResolver(folderDB folder.FolderStore, folderStore folder.Store) (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) {
ctx, span := tracer.Start(ctx, "dashboards.NewFolderNameScopeResolver") ctx, span := tracer.Start(ctx, "dashboards.NewFolderNameScopeResolver")
@ -63,7 +63,7 @@ func NewFolderNameScopeResolver(folderDB folder.FolderStore, folderSvc folder.Se
return nil, err return nil, err
} }
result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderSvc) result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,7 +74,7 @@ func NewFolderNameScopeResolver(folderDB folder.FolderStore, folderSvc folder.Se
} }
// 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(folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func NewFolderIDScopeResolver(folderDB folder.FolderStore, folderStore folder.Store) (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) {
ctx, span := tracer.Start(ctx, "dashboards.NewFolderIDScopeResolver") ctx, span := tracer.Start(ctx, "dashboards.NewFolderIDScopeResolver")
@ -98,7 +98,7 @@ func NewFolderIDScopeResolver(folderDB folder.FolderStore, folderSvc folder.Serv
return nil, err return nil, err
} }
result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderSvc) result, err := GetInheritedScopes(ctx, folder.OrgID, folder.UID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -110,7 +110,7 @@ func NewFolderIDScopeResolver(folderDB folder.FolderStore, folderSvc folder.Serv
// NewFolderUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:uid:" // NewFolderUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:uid:"
// into uid based scopes for folder and its parents // into uid based scopes for folder and its parents
func NewFolderUIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func NewFolderUIDScopeResolver(folderStore folder.Store) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScopeUID("") prefix := ScopeFoldersProvider.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) {
ctx, span := tracer.Start(ctx, "dashboards.NewFolderUIDScopeResolver") ctx, span := tracer.Start(ctx, "dashboards.NewFolderUIDScopeResolver")
@ -125,7 +125,7 @@ func NewFolderUIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttrib
return nil, err return nil, err
} }
inheritedScopes, err := GetInheritedScopes(ctx, orgID, uid, folderSvc) inheritedScopes, err := GetInheritedScopes(ctx, orgID, uid, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -135,7 +135,7 @@ func NewFolderUIDScopeResolver(folderSvc folder.Service) (string, ac.ScopeAttrib
// 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(folderDB folder.FolderStore, ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func NewDashboardIDScopeResolver(folderDB folder.FolderStore, ds DashboardService, folderStore folder.Store) (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) {
ctx, span := tracer.Start(ctx, "dashboards.NewDashboardIDScopeResolver") ctx, span := tracer.Start(ctx, "dashboards.NewDashboardIDScopeResolver")
@ -155,13 +155,13 @@ func NewDashboardIDScopeResolver(folderDB folder.FolderStore, ds DashboardServic
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderSvc) return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderStore)
}) })
} }
// 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(folderDB folder.FolderStore, ds DashboardService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func NewDashboardUIDScopeResolver(folderDB folder.FolderStore, ds DashboardService, folderStore folder.Store) (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) {
ctx, span := tracer.Start(ctx, "dashboards.NewDashboardUIDScopeResolver") ctx, span := tracer.Start(ctx, "dashboards.NewDashboardUIDScopeResolver")
@ -181,11 +181,11 @@ func NewDashboardUIDScopeResolver(folderDB folder.FolderStore, ds DashboardServi
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderSvc) return resolveDashboardScope(ctx, folderDB, orgID, dashboard, folderStore)
}) })
} }
func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) { func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, orgID int64, dashboard *Dashboard, folderStore folder.Store) ([]string, error) {
ctx, span := tracer.Start(ctx, "dashboards.resolveDashboardScope") ctx, span := tracer.Start(ctx, "dashboards.resolveDashboardScope")
span.End() span.End()
@ -208,7 +208,7 @@ func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, org
folderUID = folder.UID folderUID = folder.UID
} }
result, err := GetInheritedScopes(ctx, orgID, folderUID, folderSvc) result, err := GetInheritedScopes(ctx, orgID, folderUID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -223,17 +223,24 @@ func resolveDashboardScope(ctx context.Context, folderDB folder.FolderStore, org
return result, nil return result, nil
} }
func GetInheritedScopes(ctx context.Context, orgID int64, folderUID string, folderSvc folder.Service) ([]string, error) { func GetInheritedScopes(ctx context.Context, orgID int64, folderUID string, folderStore folder.Store) ([]string, error) {
ctx, span := tracer.Start(ctx, "dashboards.GetInheritedScopes") ctx, span := tracer.Start(ctx, "dashboards.GetInheritedScopes")
span.End() span.End()
if folderUID == ac.GeneralFolderUID { if folderUID == ac.GeneralFolderUID {
return nil, nil return nil, nil
} }
ancestors, err := folderSvc.GetParents(ctx, folder.GetParentsQuery{
var ancestors []*folder.Folder
var err error
if folderUID == folder.SharedWithMeFolderUID {
ancestors = []*folder.Folder{&folder.SharedWithMeFolder}
} else {
ancestors, err = folderStore.GetParents(ctx, folder.GetParentsQuery{
UID: folderUID, UID: folderUID,
OrgID: orgID, OrgID: orgID,
}) })
}
if err != nil { if err != nil {
if errors.Is(err, folder.ErrFolderNotFound) { if errors.Is(err, folder.ErrFolderNotFound) {

View File

@ -18,7 +18,7 @@ import (
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(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) prefix, _ := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
require.Equal(t, "folders:name:", prefix) require.Equal(t, "folders:name:", prefix)
}) })
@ -31,7 +31,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
scope := "folders:name:" + title scope := "folders:name:" + title
_, resolver := NewFolderNameScopeResolver(folderStore, foldertest.NewFakeService()) _, resolver := NewFolderNameScopeResolver(folderStore, folder.NewFakeStore())
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, resolvedScopes, 1) require.Len(t, resolvedScopes, 1)
@ -49,8 +49,8 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
scope := "folders:name:" + title scope := "folders:name:" + title
folderSvc := foldertest.NewFakeService() fStore := folder.NewFakeStore()
folderSvc.ExpectedFolders = []*folder.Folder{ fStore.ExpectedParentFolders = []*folder.Folder{
{ {
UID: "parent", UID: "parent",
}, },
@ -58,7 +58,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
UID: "grandparent", UID: "grandparent",
}, },
} }
_, resolver := NewFolderNameScopeResolver(folderStore, folderSvc) _, resolver := NewFolderNameScopeResolver(folderStore, fStore)
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
require.NoError(t, err) require.NoError(t, err)
@ -75,20 +75,20 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title, mock.Anything) folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title, mock.Anything)
}) })
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 := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
_, 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) {
_, resolver := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, resolver := NewFolderNameScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
_, 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)
}) })
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
folderStore := foldertest.NewFakeFolderStore(t) folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewFolderNameScopeResolver(folderStore, foldertest.NewFakeService()) _, resolver := NewFolderNameScopeResolver(folderStore, folder.NewFakeStore())
orgId := rand.Int63() orgId := rand.Int63()
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
@ -103,12 +103,12 @@ 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(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) prefix, _ := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
require.Equal(t, "folders:id:", prefix) require.Equal(t, "folders:id:", prefix)
}) })
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 := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
_, 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)
@ -118,7 +118,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
var ( var (
orgId = rand.Int63() orgId = rand.Int63()
scope = "folders:id:0" scope = "folders:id:0"
_, resolver = NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, resolver = NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
) )
resolved, err := resolver.Resolve(context.Background(), orgId, scope) resolved, err := resolver.Resolve(context.Background(), orgId, scope)
@ -129,7 +129,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) {
_, resolver := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()) _, resolver := NewFolderIDScopeResolver(foldertest.NewFakeFolderStore(t), folder.NewFakeStore())
_, 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)
@ -137,7 +137,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
folderStore := foldertest.NewFakeFolderStore(t) folderStore := foldertest.NewFakeFolderStore(t)
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()
_, resolver := NewFolderIDScopeResolver(folderStore, foldertest.NewFakeService()) _, resolver := NewFolderIDScopeResolver(folderStore, folder.NewFakeStore())
orgId := rand.Int63() orgId := rand.Int63()
scope := "folders:id:10" scope := "folders:id:10"
@ -149,12 +149,12 @@ 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(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) prefix, _ := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, folder.NewFakeStore())
require.Equal(t, "dashboards:id:", prefix) require.Equal(t, "dashboards:id:", prefix)
}) })
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(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) _, resolver := NewDashboardIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, folder.NewFakeStore())
_, 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)
}) })
@ -162,12 +162,12 @@ 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(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) prefix, _ := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, folder.NewFakeStore())
require.Equal(t, "dashboards:uid:", prefix) require.Equal(t, "dashboards:uid:", prefix)
}) })
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(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, foldertest.NewFakeService()) _, resolver := NewDashboardUIDScopeResolver(foldertest.NewFakeFolderStore(t), &FakeDashboardService{}, folder.NewFakeStore())
_, 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)
}) })

View File

@ -303,8 +303,10 @@ func TestIntegrationDashboardInheritedFolderRBAC(t *testing.T) {
guardian.New = origNewGuardian guardian.New = origNewGuardian
}) })
folderPermissions := mock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideStore(sqlStore) folderStore := folderimpl.ProvideStore(sqlStore)
folderSvc := folderimpl.ProvideService(folderStore, mock.New(), bus.ProvideBus(tracer), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(sqlStore), sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) folderSvc := folderimpl.ProvideService(folderStore, mock.New(), bus.ProvideBus(tracer), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(sqlStore),
sqlStore, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
parentUID := "" parentUID := ""
for i := 0; ; i++ { for i := 0; ; i++ {

View File

@ -16,6 +16,8 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol/testutil"
"github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
@ -829,10 +831,11 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false) insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false)
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()) ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
folderPermissions := mock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore,
folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) folderStore, sqlStore, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
user := &user.SignedInUser{ user := &user.SignedInUser{
OrgID: 1, OrgID: 1,
@ -951,8 +954,11 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderPermissions, err := testutil.ProvideFolderPermissions(features, cfg, sqlStore)
require.NoError(t, err)
folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore,
folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) folderStore, sqlStore, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
user := &user.SignedInUser{ user := &user.SignedInUser{
OrgID: 1, OrgID: 1,

View File

@ -81,8 +81,8 @@ func ProvideDashboardServiceImpl(
metrics: newDashboardsMetrics(r), metrics: newDashboardsMetrics(r),
} }
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderStore, dashSvc, folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(folderStore, dashSvc, fStore))
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, dashSvc, folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, dashSvc, fStore))
if err := folderSvc.RegisterService(dashSvc); err != nil { if err := folderSvc.RegisterService(dashSvc); err != nil {
return nil, err return nil, err

View File

@ -11,6 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/grafana/authlib/claims"
"github.com/grafana/dskit/concurrency" "github.com/grafana/dskit/concurrency"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
@ -29,11 +30,13 @@ 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/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/store/entity" "github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/services/supportbundles" "github.com/grafana/grafana/pkg/services/supportbundles"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -46,6 +49,8 @@ type Service struct {
dashboardStore dashboards.Store dashboardStore dashboards.Store
dashboardFolderStore folder.FolderStore dashboardFolderStore folder.FolderStore
features featuremgmt.FeatureToggles features featuremgmt.FeatureToggles
cfg *setting.Cfg
folderPermissions accesscontrol.FolderPermissionsService
accessControl accesscontrol.AccessControl accessControl accesscontrol.AccessControl
// bus is currently used to publish event in case of folder full path change. // bus is currently used to publish event in case of folder full path change.
// For example when a folder is moved to another folder or when a folder is renamed. // For example when a folder is moved to another folder or when a folder is renamed.
@ -65,6 +70,8 @@ func ProvideService(
folderStore folder.FolderStore, folderStore folder.FolderStore,
db db.DB, // DB for the (new) nested folder store db db.DB, // DB for the (new) nested folder store
features featuremgmt.FeatureToggles, features featuremgmt.FeatureToggles,
cfg *setting.Cfg,
folderPermissions accesscontrol.FolderPermissionsService,
supportBundles supportbundles.Service, supportBundles supportbundles.Service,
r prometheus.Registerer, r prometheus.Registerer,
tracer tracing.Tracer, tracer tracing.Tracer,
@ -75,6 +82,8 @@ func ProvideService(
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: store, store: store,
features: features, features: features,
cfg: cfg,
folderPermissions: folderPermissions,
accessControl: ac, accessControl: ac,
bus: bus, bus: bus,
db: db, db: db,
@ -86,9 +95,9 @@ func ProvideService(
supportBundles.RegisterSupportItemCollector(srv.supportBundleCollector()) supportBundles.RegisterSupportItemCollector(srv.supportBundleCollector())
ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(folderStore, srv)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(folderStore, store))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, srv)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, store))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(srv)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(store))
return srv return srv
} }
@ -658,9 +667,43 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
if nestedFolder != nil && nestedFolder.ParentUID != "" { if nestedFolder != nil && nestedFolder.ParentUID != "" {
f.ParentUID = nestedFolder.ParentUID f.ParentUID = nestedFolder.ParentUID
} }
if err = s.setDefaultFolderPermissions(ctx, cmd.OrgID, user, f); err != nil {
return nil, err
}
return f, nil return f, nil
} }
func (s *Service) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
if !s.cfg.RBAC.PermissionsOnCreation("folder") {
return nil
}
var permissions []accesscontrol.SetResourcePermissionCommand
if user.IsIdentityType(claims.TypeUser) {
userID, err := user.GetInternalID()
if err != nil {
return err
}
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(),
})
}
isNested := folder.ParentUID != ""
if !isNested || !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
}...)
}
_, err := s.folderPermissions.SetPermissions(ctx, orgID, folder.UID, permissions...)
return err
}
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) { func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
ctx, span := s.tracer.Start(ctx, "folder.Update") ctx, span := s.tracer.Start(ctx, "folder.Update")
defer span.End() defer span.End()

View File

@ -62,10 +62,11 @@ func TestIntegrationProvideFolderService(t *testing.T) {
} }
t.Run("should register scope resolvers", func(t *testing.T) { t.Run("should register scope resolvers", func(t *testing.T) {
ac := acmock.New() ac := acmock.New()
db, _ := db.InitTestDBWithCfg(t) db, cfg := db.InitTestDBWithCfg(t)
folderPermissions := acmock.NewMockedPermissionsService()
store := ProvideStore(db) store := ProvideStore(db)
ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db, ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db,
featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) featuremgmt.WithFeatures(), cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 3) require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 3)
}) })
@ -97,6 +98,7 @@ func TestIntegrationFolderService(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: features, features: features,
cfg: cfg,
bus: bus.ProvideBus(tracing.InitializeTracerForTest()), bus: bus.ProvideBus(tracing.InitializeTracerForTest()),
db: db, db: db,
accessControl: acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()), accessControl: acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()),
@ -439,6 +441,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: featuresFlagOn, features: featuresFlagOn,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
accessControl: ac, accessControl: ac,
@ -494,7 +497,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOn, db, serviceWithFlagOn, dashSrv, ac) alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOn, db, serviceWithFlagOn, dashSrv, ac)
require.NoError(t, err) require.NoError(t, err)
elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOn, featuresFlagOn, ac) elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOn, serviceWithFlagOn.store, featuresFlagOn, ac)
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOn) lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOn)
require.NoError(t, err) require.NoError(t, err)
@ -554,6 +557,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: featuresFlagOff, features: featuresFlagOff,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
registry: make(map[string]folder.RegistryService), registry: make(map[string]folder.RegistryService),
@ -576,7 +580,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOff, dashSrv, ac) alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOff, dashSrv, ac)
require.NoError(t, err) require.NoError(t, err)
elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOff, featuresFlagOff, ac) elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOff, serviceWithFlagOff.store, featuresFlagOff, ac)
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOff) lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOff)
require.NoError(t, err) require.NoError(t, err)
@ -632,6 +636,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
log: slog.New(logtest.NewTestHandler(t)).With("logger", "test-folder-service"), log: slog.New(logtest.NewTestHandler(t)).With("logger", "test-folder-service"),
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
features: featuresFlagOff, features: featuresFlagOff,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
registry: make(map[string]folder.RegistryService), registry: make(map[string]folder.RegistryService),
@ -705,7 +710,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
CanEditValue: true, CanEditValue: true,
}) })
elementService := libraryelements.ProvideService(cfg, db, routeRegister, tc.service, tc.featuresFlag, ac) elementService := libraryelements.ProvideService(cfg, db, routeRegister, tc.service, tc.service.store, tc.featuresFlag, ac)
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, tc.service) lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, tc.service)
require.NoError(t, err) require.NoError(t, err)
@ -810,6 +815,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
dashboardStore: &dashStore, dashboardStore: &dashStore,
dashboardFolderStore: dashboardFolderStore, dashboardFolderStore: dashboardFolderStore,
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
cfg: setting.NewCfg(),
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()), accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
metrics: newFoldersMetrics(nil), metrics: newFoldersMetrics(nil),
tracer: tracing.InitializeTracerForTest(), tracer: tracing.InitializeTracerForTest(),
@ -847,6 +853,7 @@ func TestFolderServiceDualWrite(t *testing.T) {
dashboardStore: dashStore, dashboardStore: dashStore,
dashboardFolderStore: dashboardFolderStore, dashboardFolderStore: dashboardFolderStore,
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
cfg: cfg,
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()), accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
metrics: newFoldersMetrics(nil), metrics: newFoldersMetrics(nil),
tracer: tracing.InitializeTracerForTest(), tracer: tracing.InitializeTracerForTest(),
@ -1479,6 +1486,7 @@ func TestIntegrationNestedFolderSharedWithMe(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: featuresFlagOn, features: featuresFlagOn,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
accessControl: ac, accessControl: ac,
@ -1901,6 +1909,7 @@ func TestFolderServiceGetFolder(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: features, features: features,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
accessControl: ac, accessControl: ac,
@ -1983,6 +1992,7 @@ func TestFolderServiceGetFolders(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: featuresFlagOff, features: featuresFlagOff,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
accessControl: ac, accessControl: ac,
@ -2070,6 +2080,7 @@ func TestGetChildrenFilterByPermission(t *testing.T) {
dashboardFolderStore: folderStore, dashboardFolderStore: folderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: features, features: features,
cfg: cfg,
bus: b, bus: b,
db: db, db: db,
accessControl: ac, accessControl: ac,
@ -2533,6 +2544,7 @@ func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore folder
dashboardFolderStore: dashboardFolderStore, dashboardFolderStore: dashboardFolderStore,
store: nestedFolderStore, store: nestedFolderStore,
features: features, features: features,
cfg: setting.NewCfg(),
accessControl: ac, accessControl: ac,
db: db, db: db,
metrics: newFoldersMetrics(nil), metrics: newFoldersMetrics(nil),

View File

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"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/foldertest" "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/user" "github.com/grafana/grafana/pkg/services/user"
@ -958,13 +959,13 @@ func setupAccessControlGuardianTest(
fakeDashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Maybe().Return(d, nil) fakeDashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Maybe().Return(d, nil)
ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()) ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())
folderSvc := foldertest.NewFakeService()
fStore := folder.NewFakeStore()
folderStore := foldertest.NewFakeFolderStore(t) folderStore := foldertest.NewFakeFolderStore(t)
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, fakeDashboardService, folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(folderStore, fakeDashboardService, fStore))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(fStore))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, folderSvc)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, fStore))
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()

View File

@ -35,7 +35,7 @@ var (
// LibraryPanelUIDScopeResolver provides a ScopeAttributeResolver that is able to convert a scope prefixed with "library.panels:uid:" // LibraryPanelUIDScopeResolver provides a ScopeAttributeResolver that is able to convert a scope prefixed with "library.panels:uid:"
// into uid based scopes for a library panel and its associated folder hierarchy // into uid based scopes for a library panel and its associated folder hierarchy
func LibraryPanelUIDScopeResolver(l *LibraryElementService, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) { func LibraryPanelUIDScopeResolver(l *LibraryElementService, folderStore folder.Store) (string, ac.ScopeAttributeResolver) {
prefix := ScopeLibraryPanelsProvider.GetResourceScopeUID("") prefix := ScopeLibraryPanelsProvider.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) {
@ -60,7 +60,7 @@ func LibraryPanelUIDScopeResolver(l *LibraryElementService, folderSvc folder.Ser
return nil, err return nil, err
} }
inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, libElDTO.FolderUID, folderSvc) inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, libElDTO.FolderUID, folderStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,7 +15,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.RouteRegister, folderService folder.Service, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) *LibraryElementService { func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.RouteRegister, folderService folder.Service, folderStore folder.Store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) *LibraryElementService {
l := &LibraryElementService{ l := &LibraryElementService{
Cfg: cfg, Cfg: cfg,
SQLStore: sqlStore, SQLStore: sqlStore,
@ -27,7 +27,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.Rout
} }
l.registerAPIEndpoints() l.registerAPIEndpoints()
ac.RegisterScopeAttributeResolver(LibraryPanelUIDScopeResolver(l, l.folderService)) ac.RegisterScopeAttributeResolver(LibraryPanelUIDScopeResolver(l, folderStore))
return l return l
} }

View File

@ -324,6 +324,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
cfg := setting.NewCfg() cfg := setting.NewCfg()
ac := actest.FakeAccessControl{ExpectedEvaluate: true} ac := actest.FakeAccessControl{ExpectedEvaluate: true}
folderPermissions := acmock.NewMockedPermissionsService()
quotaService := quotatest.New(false, nil) quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService) dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService)
require.NoError(t, err) require.NoError(t, err)
@ -331,7 +332,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
store := folderimpl.ProvideStore(sc.sqlStore) store := folderimpl.ProvideStore(sc.sqlStore)
s := folderimpl.ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, s := folderimpl.ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
ctx := identity.WithRequester(context.Background(), &sc.user) ctx := identity.WithRequester(context.Background(), &sc.user)
folder, err := s.Create(ctx, &folder.CreateFolderCommand{ folder, err := s.Create(ctx, &folder.CreateFolderCommand{
@ -463,7 +464,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
guardian.InitAccessControlGuardian(cfg, ac, dashService) guardian.InitAccessControlGuardian(cfg, ac, dashService)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderSrv := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracer), dashboardStore, folderStore, sqlStore, folderSrv := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracer), dashboardStore, folderStore, sqlStore,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
service := LibraryElementService{ service := LibraryElementService{
Cfg: cfg, Cfg: cfg,
features: featuremgmt.WithFeatures(), features: featuremgmt.WithFeatures(),

View File

@ -18,6 +18,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol/testutil"
"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"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
@ -747,6 +748,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
ac := actest.FakeAccessControl{ExpectedEvaluate: true} ac := actest.FakeAccessControl{ExpectedEvaluate: true}
folderPermissions := acmock.NewMockedPermissionsService()
cfg := setting.NewCfg() cfg := setting.NewCfg()
quotaService := quotatest.New(false, nil) quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService) dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService)
@ -754,7 +756,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
fStore := folderimpl.ProvideStore(sc.sqlStore) fStore := folderimpl.ProvideStore(sc.sqlStore)
s := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, s := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
ctx := identity.WithRequester(context.Background(), sc.user) ctx := identity.WithRequester(context.Background(), sc.user)
@ -838,10 +840,12 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
require.NoError(t, err) require.NoError(t, err)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderPermissions, err := testutil.ProvideFolderPermissions(features, cfg, sqlStore)
require.NoError(t, err)
folderService := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, folderService := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService, features, ac) elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService, fStore, features, ac)
service := LibraryPanelService{ service := LibraryPanelService{
Cfg: cfg, Cfg: cfg,
SQLStore: sqlStore, SQLStore: sqlStore,

View File

@ -27,6 +27,7 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"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"
@ -1817,13 +1818,14 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
}}, nil).Maybe() }}, nil).Maybe()
ac := &recordingAccessControlFake{} ac := &recordingAccessControlFake{}
folderPermissions := acmock.NewMockedPermissionsService()
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotatest.New(false, nil)) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotatest.New(false, nil))
require.NoError(t, err) require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderService := folderimpl.ProvideService(fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, folderService := folderimpl.ProvideService(fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore,
featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) featuremgmt.WithFeatures(), cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
store := store.DBstore{ store := store.DBstore{
Logger: log, Logger: log,
SQLStore: sqlStore, SQLStore: sqlStore,

View File

@ -1546,11 +1546,11 @@ func TestProvisiongWithFullpath(t *testing.T) {
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
_, dashboardStore := testutil.SetupDashboardService(t, sqlStore, folderStore, cfg) _, dashboardStore := testutil.SetupDashboardService(t, sqlStore, folderStore, cfg)
ac := acmock.New() ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService()
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders) features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
fStore := folderimpl.ProvideStore(sqlStore) fStore := folderimpl.ProvideStore(sqlStore)
folderService := folderimpl.ProvideService(fStore, ac, inProcBus, dashboardStore, folderStore, sqlStore, folderService := folderimpl.ProvideService(fStore, ac, inProcBus, dashboardStore, folderStore, sqlStore,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
ruleService := createAlertRuleService(t, folderService) ruleService := createAlertRuleService(t, folderService)
var orgID int64 = 1 var orgID int64 = 1

View File

@ -27,9 +27,10 @@ import (
func SetupFolderService(tb testing.TB, cfg *setting.Cfg, db db.DB, dashboardStore dashboards.Store, folderStore *folderimpl.DashboardFolderStoreImpl, bus *bus.InProcBus, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service { func SetupFolderService(tb testing.TB, cfg *setting.Cfg, db db.DB, dashboardStore dashboards.Store, folderStore *folderimpl.DashboardFolderStoreImpl, bus *bus.InProcBus, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
tb.Helper() tb.Helper()
folderPermissions := acmock.NewMockedPermissionsService()
fStore := folderimpl.ProvideStore(db) fStore := folderimpl.ProvideStore(db)
return folderimpl.ProvideService(fStore, ac, bus, dashboardStore, folderStore, db, return folderimpl.ProvideService(fStore, ac, bus, dashboardStore, folderStore, db,
features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
} }
func SetupDashboardService(tb testing.TB, sqlStore db.DB, fs *folderimpl.DashboardFolderStoreImpl, cfg *setting.Cfg) (*dashboardservice.DashboardServiceImpl, dashboards.Store) { func SetupDashboardService(tb testing.TB, sqlStore db.DB, fs *folderimpl.DashboardFolderStoreImpl, cfg *setting.Cfg) (*dashboardservice.DashboardServiceImpl, dashboards.Store) {

View File

@ -24,7 +24,7 @@ var _ FutureAuthService = (*simpleAuthService)(nil)
type simpleAuthService struct { type simpleAuthService struct {
sql db.DB sql db.DB
ac accesscontrol.Service ac accesscontrol.Service
folderService folder.Service folderStore folder.Store
logger log.Logger logger log.Logger
} }
@ -32,14 +32,14 @@ func (a *simpleAuthService) GetDashboardReadFilter(ctx context.Context, orgID in
canReadDashboard, canReadFolder := accesscontrol.Checker(user, dashboards.ActionDashboardsRead), accesscontrol.Checker(user, dashboards.ActionFoldersRead) canReadDashboard, canReadFolder := accesscontrol.Checker(user, dashboards.ActionDashboardsRead), accesscontrol.Checker(user, dashboards.ActionFoldersRead)
return func(kind entityKind, uid, parent string) bool { return func(kind entityKind, uid, parent string) bool {
if kind == entityKindFolder { if kind == entityKindFolder {
scopes, err := dashboards.GetInheritedScopes(ctx, orgID, uid, a.folderService) scopes, err := dashboards.GetInheritedScopes(ctx, orgID, uid, a.folderStore)
if err != nil { if err != nil {
a.logger.Debug("Could not retrieve inherited folder scopes:", "err", err) a.logger.Debug("Could not retrieve inherited folder scopes:", "err", err)
} }
scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(uid)) scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(uid))
return canReadFolder(scopes...) return canReadFolder(scopes...)
} else if kind == entityKindDashboard { } else if kind == entityKindDashboard {
scopes, err := dashboards.GetInheritedScopes(ctx, orgID, parent, a.folderService) scopes, err := dashboards.GetInheritedScopes(ctx, orgID, parent, a.folderStore)
if err != nil { if err != nil {
a.logger.Debug("Could not retrieve inherited folder scopes:", "err", err) a.logger.Debug("Could not retrieve inherited folder scopes:", "err", err)
} }

View File

@ -18,7 +18,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"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/folder"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotatest"
@ -754,7 +754,7 @@ func setupIntegrationEnv(t *testing.T, folderCount, dashboardsPerFolder int, sql
ExpectedOrgs: []*org.OrgDTO{{ID: 1}}, ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
} }
searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{}, searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{},
tracing.InitializeTracerForTest(), features, orgSvc, nil, foldertest.NewFakeService()).(*StandardSearchService) tracing.InitializeTracerForTest(), features, orgSvc, nil, folder.NewFakeStore()).(*StandardSearchService)
require.True(t, ok) require.True(t, ok)
err = runSearchService(searchService) err = runSearchService(searchService)

View File

@ -85,7 +85,7 @@ func (s *StandardSearchService) IsReady(ctx context.Context, orgId int64) IsSear
func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEventsService, func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEventsService,
ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service, ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service,
userService user.Service, folderService folder.Service) SearchService { userService user.Service, folderStore folder.Store) SearchService {
extender := &NoopExtender{} extender := &NoopExtender{}
logger := log.New("searchV2") logger := log.New("searchV2")
s := &StandardSearchService{ s := &StandardSearchService{
@ -95,7 +95,7 @@ func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEv
auth: &simpleAuthService{ auth: &simpleAuthService{
sql: sql, sql: sql,
ac: ac, ac: ac,
folderService: folderService, folderStore: folderStore,
logger: logger, logger: logger,
}, },
dashboardIndex: newSearchIndex( dashboardIndex: newSearchIndex(

View File

@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"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/folder"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/store"
@ -42,7 +42,7 @@ func setupBenchEnv(b *testing.B, folderCount, dashboardsPerFolder int) (*Standar
ExpectedOrgs: []*org.OrgDTO{{ID: 1}}, ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
} }
searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{}, searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{},
tracing.InitializeTracerForTest(), features, orgSvc, nil, foldertest.NewFakeService()).(*StandardSearchService) tracing.InitializeTracerForTest(), features, orgSvc, nil, folder.NewFakeStore()).(*StandardSearchService)
require.True(b, ok) require.True(b, ok)
err = runSearchService(searchService) err = runSearchService(searchService)

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol/testutil"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/dashboards/database"
@ -822,10 +823,11 @@ func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db), quotatest.New(false, nil)) dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db), quotatest.New(false, nil))
require.NoError(t, err) require.NoError(t, err)
folderPermissions, err := testutil.ProvideFolderPermissions(features, cfg, db)
require.NoError(t, err)
fStore := folderimpl.ProvideStore(db) fStore := folderimpl.ProvideStore(db)
folderSvc := folderimpl.ProvideService(fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderSvc := folderimpl.ProvideService(fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore,
folderimpl.ProvideDashboardFolderStore(db), db, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) folderimpl.ProvideDashboardFolderStore(db), db, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
// create parent folder // create parent folder
parent, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ parent, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
UID: "parent", UID: "parent",

View File

@ -78,12 +78,13 @@ func setupBenchMark(b *testing.B, usr user.SignedInUser, features featuremgmt.Fe
quotaService := quotatest.New(false, nil) quotaService := quotatest.New(false, nil)
folderPermissions := mock.NewMockedPermissionsService()
dashboardWriteStore, err := database.ProvideDashboardStore(store, cfg, features, tagimpl.ProvideService(store), quotaService) dashboardWriteStore, err := database.ProvideDashboardStore(store, cfg, features, tagimpl.ProvideService(store), quotaService)
require.NoError(b, err) require.NoError(b, err)
fStore := folderimpl.ProvideStore(store) fStore := folderimpl.ProvideStore(store)
folderSvc := folderimpl.ProvideService(fStore, mock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(store), folderSvc := folderimpl.ProvideService(fStore, mock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(store),
store, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) store, features, cfg, folderPermissions, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
origNewGuardian := guardian.New origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true, CanSaveValue: true}) guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true, CanSaveValue: true})