mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 15:42:13 +08:00
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:
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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.
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
)
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -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++ {
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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),
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
@ -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",
|
||||||
|
@ -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})
|
||||||
|
Reference in New Issue
Block a user