Folders: Fix guardian to use folder service (#99339)

pull/99385/head
Stephanie Hingtgen 11 months ago committed by GitHub
parent 59b246dbea
commit 192a81d07f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      pkg/api/dashboard_snapshot_test.go
  2. 6
      pkg/api/dashboard_test.go
  3. 2
      pkg/api/folder_bench_test.go
  4. 20
      pkg/services/dashboards/service/dashboard_service.go
  5. 3
      pkg/services/dashboards/service/dashboard_service_integration_test.go
  6. 14
      pkg/services/folder/folderimpl/folder.go
  7. 117
      pkg/services/guardian/accesscontrol_guardian.go
  8. 3
      pkg/services/guardian/accesscontrol_guardian_test.go
  9. 20
      pkg/services/guardian/guardian.go
  10. 18
      pkg/services/guardian/provider.go
  11. 13
      pkg/services/libraryelements/guard.go
  12. 4
      pkg/services/libraryelements/libraryelements_get_all_test.go
  13. 6
      pkg/services/libraryelements/libraryelements_patch_test.go
  14. 21
      pkg/services/libraryelements/libraryelements_permissions_test.go
  15. 44
      pkg/services/libraryelements/libraryelements_test.go
  16. 8
      pkg/services/librarypanels/librarypanels_test.go

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db/dbtest" "github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/log"
"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"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
@ -40,7 +41,7 @@ func TestHTTPServer_DeleteDashboardSnapshot(t *testing.T) {
hs.DashboardService = svc hs.DashboardService = svc
hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures()) hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures())
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService, hs.folderService, log.NewNopLogger())
}) })
} }

@ -151,7 +151,7 @@ func TestHTTPServer_GetDashboard_AccessControl(t *testing.T) {
hs.starService = startest.NewStarServiceFake() hs.starService = startest.NewStarServiceFake()
hs.dashboardProvisioningService = mockDashboardProvisioningService{} hs.dashboardProvisioningService = mockDashboardProvisioningService{}
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService, hs.folderService, log.NewNopLogger())
}) })
} }
@ -279,7 +279,7 @@ func TestHTTPServer_DeleteDashboardByUID_AccessControl(t *testing.T) {
license.On("FeatureEnabled", publicdashboardModels.FeaturePublicDashboardsEmailSharing).Return(false) license.On("FeatureEnabled", publicdashboardModels.FeaturePublicDashboardsEmailSharing).Return(false)
hs.PublicDashboardsApi = api.ProvideApi(pubDashService, nil, hs.AccessControl, featuremgmt.WithFeatures(), middleware, hs.Cfg, license) hs.PublicDashboardsApi = api.ProvideApi(pubDashService, nil, hs.AccessControl, featuremgmt.WithFeatures(), middleware, hs.Cfg, license)
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService, hs.folderService, log.NewNopLogger())
}) })
} }
deleteDashboard := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) { deleteDashboard := func(server *webtest.Server, permissions []accesscontrol.Permission) (*http.Response, error) {
@ -330,7 +330,7 @@ func TestHTTPServer_GetDashboardVersions_AccessControl(t *testing.T) {
ExpectedDashboardVersion: &dashver.DashboardVersionDTO{}, ExpectedDashboardVersion: &dashver.DashboardVersionDTO{},
} }
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService, hs.folderService, log.NewNopLogger())
}) })
} }

@ -495,7 +495,7 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog
} }
hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures()) hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures())
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService, hs.folderService, log.NewNopLogger())
m.Get("/api/folders", hs.GetFolders) m.Get("/api/folders", hs.GetFolders)
m.Get("/api/search", hs.Search) m.Get("/api/search", hs.Search)

@ -56,6 +56,7 @@ var (
provisionerPermissions = []accesscontrol.Permission{ provisionerPermissions = []accesscontrol.Permission{
{Action: dashboards.ActionFoldersCreate, Scope: dashboards.ScopeFoldersAll}, {Action: dashboards.ActionFoldersCreate, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll}, {Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll}, {Action: dashboards.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionDashboardsWrite, Scope: dashboards.ScopeFoldersAll}, {Action: dashboards.ActionDashboardsWrite, Scope: dashboards.ScopeFoldersAll},
{Action: datasources.ActionRead, Scope: datasources.ScopeAll}, {Action: datasources.ActionRead, Scope: datasources.ScopeAll},
@ -611,13 +612,28 @@ func getGuardianForSavePermissionCheck(ctx context.Context, d *dashboards.Dashbo
if newDashboard { if newDashboard {
// if it's a new dashboard/folder check the parent folder permissions // if it's a new dashboard/folder check the parent folder permissions
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc() metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
// nolint:staticcheck guard, err := guardian.NewByFolder(ctx, &folder.Folder{
guard, err := guardian.New(ctx, d.FolderID, d.OrgID, user) ID: d.FolderID, // nolint:staticcheck
OrgID: d.OrgID,
}, d.OrgID, user)
if err != nil {
return nil, err
}
return guard, nil
}
if d.IsFolder {
guard, err := guardian.NewByFolder(ctx, &folder.Folder{
ID: d.ID, // nolint:staticcheck
UID: d.UID,
OrgID: d.OrgID,
}, d.OrgID, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return guard, nil return guard, nil
} }
guard, err := guardian.NewByDashboard(ctx, d, d.OrgID, user) guard, err := guardian.NewByDashboard(ctx, d, d.OrgID, user)
if err != nil { if err != nil {
return nil, err return nil, err

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"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"
@ -896,7 +897,7 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
) )
dashboardService.RegisterDashboardPermissions(dashboardPermissions) dashboardService.RegisterDashboardPermissions(dashboardPermissions)
require.NoError(t, err) require.NoError(t, err)
guardian.InitAccessControlGuardian(cfg, ac, dashboardService) guardian.InitAccessControlGuardian(cfg, ac, dashboardService, folderService, log.NewNopLogger())
savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore) savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore)
savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.UID, sqlStore) savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.UID, sqlStore)

@ -392,7 +392,7 @@ func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQu
// we only need to check access to the folder // we only need to check access to the folder
// if the parent is accessible then the subfolders are accessible as well (due to inheritance) // if the parent is accessible then the subfolders are accessible as well (due to inheritance)
g, err := guardian.NewByUID(ctx, q.UID, q.OrgID, q.SignedInUser) g, err := guardian.NewByFolderUID(ctx, q.UID, q.OrgID, q.SignedInUser)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -941,7 +941,7 @@ func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderComm
return folder.ErrBadRequest.Errorf("invalid orgID") return folder.ErrBadRequest.Errorf("invalid orgID")
} }
guard, err := guardian.NewByUID(ctx, cmd.UID, cmd.OrgID, cmd.SignedInUser) guard, err := guardian.NewByFolderUID(ctx, cmd.UID, cmd.OrgID, cmd.SignedInUser)
if err != nil { if err != nil {
return err return err
} }
@ -1414,13 +1414,19 @@ func getGuardianForSavePermissionCheck(ctx context.Context, d *dashboards.Dashbo
// if it's a new dashboard/folder check the parent folder permissions // if it's a new dashboard/folder check the parent folder permissions
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
// nolint:staticcheck // nolint:staticcheck
guard, err := guardian.New(ctx, d.FolderID, d.OrgID, user) guard, err := guardian.NewByFolder(ctx, &folder.Folder{
ID: d.FolderID, // nolint:staticcheck
OrgID: d.OrgID,
}, d.OrgID, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return guard, nil return guard, nil
} }
guard, err := guardian.NewByDashboard(ctx, d, d.OrgID, user) guard, err := guardian.NewByFolder(ctx, &folder.Folder{
UID: d.UID,
OrgID: d.OrgID,
}, d.OrgID, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -18,6 +18,7 @@ var _ DashboardGuardian = new(accessControlDashboardGuardian)
func NewAccessControlDashboardGuardian( func NewAccessControlDashboardGuardian(
ctx context.Context, cfg *setting.Cfg, dashboardId int64, user identity.Requester, ctx context.Context, cfg *setting.Cfg, dashboardId int64, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService,
foldersService folder.Service, logger log.Logger,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
var dashboard *dashboards.Dashboard var dashboard *dashboards.Dashboard
if dashboardId != 0 { if dashboardId != 0 {
@ -37,6 +38,7 @@ func NewAccessControlDashboardGuardian(
} }
if dashboard != nil && dashboard.IsFolder { if dashboard != nil && dashboard.IsFolder {
logger.Info("using dashboard guardian for folder", "folder", dashboard.UID)
return &accessControlFolderGuardian{ return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{ accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx, ctx: ctx,
@ -58,34 +60,22 @@ func NewAccessControlDashboardGuardian(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: foldersService,
}, },
dashboard: dashboard, dashboard: dashboard,
}, nil }, nil
} }
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardUID. // NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard.
func NewAccessControlDashboardGuardianByUID( // This constructor should be preferred over the other two if the dashboard is available
ctx context.Context, cfg *setting.Cfg, dashboardUID string, user identity.Requester, // since it avoids querying the database for fetching the dashboard.
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, func NewAccessControlDashboardGuardianByDashboard(
ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, folderService folder.Service,
logger log.Logger,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
var dashboard *dashboards.Dashboard
if dashboardUID != "" {
q := &dashboards.GetDashboardQuery{
UID: dashboardUID,
OrgID: user.GetOrgID(),
}
qResult, err := dashboardService.GetDashboard(ctx, q)
if err != nil {
if errors.Is(err, dashboards.ErrDashboardNotFound) {
return nil, ErrGuardianDashboardNotFound.Errorf("failed to get dashboard by UID: %w", err)
}
return nil, ErrGuardianGetDashboardFailure.Errorf("failed to get dashboard by UID: %w", err)
}
dashboard = qResult
}
if dashboard != nil && dashboard.IsFolder { if dashboard != nil && dashboard.IsFolder {
logger.Info("using by dashboard guardian for folder", "folder", dashboard.UID)
return &accessControlFolderGuardian{ return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{ accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx, ctx: ctx,
@ -94,6 +84,7 @@ func NewAccessControlDashboardGuardianByUID(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: folderService,
}, },
folder: dashboards.FromDashboard(dashboard), folder: dashboards.FromDashboard(dashboard),
}, nil }, nil
@ -107,19 +98,35 @@ func NewAccessControlDashboardGuardianByUID(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: folderService,
}, },
dashboard: dashboard, dashboard: dashboard,
}, nil }, nil
} }
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard. // NewAccessControlFolderGuardianByUID creates a folder guardian by the provided folderUID.
// This constructor should be preferred over the other two if the dashboard in available func NewAccessControlFolderGuardianByUID(
// since it avoids querying the database for fetching the dashboard. ctx context.Context, cfg *setting.Cfg, folderUID string, user identity.Requester,
func NewAccessControlDashboardGuardianByDashboard( ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, foldersService folder.Service,
ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
if dashboard != nil && dashboard.IsFolder { var f *folder.Folder
if folderUID != "" {
q := &folder.GetFolderQuery{
UID: &folderUID,
OrgID: user.GetOrgID(),
SignedInUser: user,
}
qResult, err := foldersService.Get(ctx, q)
if err != nil {
if errors.Is(err, dashboards.ErrFolderNotFound) {
return nil, ErrGuardianFolderNotFound.Errorf("failed to get folder by UID: %w", err)
}
return nil, ErrGuardianGetFolderFailure.Errorf("failed to get folder by UID: %w", err)
}
f = qResult
}
return &accessControlFolderGuardian{ return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{ accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx, ctx: ctx,
@ -128,29 +135,35 @@ func NewAccessControlDashboardGuardianByDashboard(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: foldersService,
}, },
folder: dashboards.FromDashboard(dashboard), folder: f,
}, nil
}
return &accessControlDashboardGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
user: user,
ac: ac,
dashboardService: dashboardService,
},
dashboard: dashboard,
}, nil }, nil
} }
// NewAccessControlFolderGuardian creates a folder guardian by the provided folder. // NewAccessControlFolderGuardian creates a folder guardian by the provided folder.
func NewAccessControlFolderGuardian( func NewAccessControlFolderGuardian(
ctx context.Context, cfg *setting.Cfg, f *folder.Folder, user identity.Requester, ctx context.Context, cfg *setting.Cfg, f *folder.Folder, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, ac accesscontrol.AccessControl, orgID int64, dashboardService dashboards.DashboardService,
folderService folder.Service,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
if f.UID == "" { // nolint:staticcheck
query := &folder.GetFolderQuery{
ID: &f.ID, // nolint:staticcheck
OrgID: orgID,
SignedInUser: user,
}
folder, err := folderService.Get(ctx, query)
if err != nil {
if errors.Is(err, dashboards.ErrFolderNotFound) {
return nil, ErrGuardianFolderNotFound.Errorf("failed to get folder: %w", err)
}
return nil, ErrGuardianGetFolderFailure.Errorf("failed to get folder: %w", err)
}
f = folder
}
return &accessControlFolderGuardian{ return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{ accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx, ctx: ctx,
@ -159,6 +172,7 @@ func NewAccessControlFolderGuardian(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: folderService,
}, },
folder: f, folder: f,
}, nil }, nil
@ -171,6 +185,7 @@ type accessControlBaseGuardian struct {
user identity.Requester user identity.Requester
ac accesscontrol.AccessControl ac accesscontrol.AccessControl
dashboardService dashboards.DashboardService dashboardService dashboards.DashboardService
folderService folder.Service
} }
type accessControlDashboardGuardian struct { type accessControlDashboardGuardian struct {
@ -353,24 +368,24 @@ func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator
return ok, err return ok, err
} }
func (a *accessControlDashboardGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) { func (a *accessControlDashboardGuardian) loadParentFolder(folderID int64) (*folder.Folder, error) {
if folderID == 0 { if folderID == 0 {
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil return &folder.Folder{UID: accesscontrol.GeneralFolderUID, OrgID: a.user.GetOrgID()}, nil
} }
folderQuery := &dashboards.GetDashboardQuery{ID: folderID, OrgID: a.user.GetOrgID()} folderQuery := &folder.GetFolderQuery{ID: &folderID, OrgID: a.user.GetOrgID(), SignedInUser: a.user}
folderQueryResult, err := a.dashboardService.GetDashboard(a.ctx, folderQuery) folderQueryResult, err := a.folderService.Get(a.ctx, folderQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return folderQueryResult, nil return folderQueryResult, nil
} }
func (a *accessControlFolderGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) { func (a *accessControlFolderGuardian) loadParentFolder(folderID int64) (*folder.Folder, error) {
if folderID == 0 { if folderID == 0 {
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil return &folder.Folder{UID: accesscontrol.GeneralFolderUID, OrgID: a.user.GetOrgID()}, nil
} }
folderQuery := &dashboards.GetDashboardQuery{ID: folderID, OrgID: a.user.GetOrgID()} folderQuery := &folder.GetFolderQuery{ID: &folderID, OrgID: a.user.GetOrgID(), SignedInUser: a.user}
folderQueryResult, err := a.dashboardService.GetDashboard(a.ctx, folderQuery) folderQueryResult, err := a.folderService.Get(a.ctx, folderQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log"
"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"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
@ -976,7 +977,7 @@ func setupAccessControlGuardianTest(
userPermissions[orgID][p.Action] = append(userPermissions[orgID][p.Action], p.Scope) userPermissions[orgID][p.Action] = append(userPermissions[orgID][p.Action], p.Scope)
} }
g, err := NewAccessControlDashboardGuardianByDashboard(context.Background(), cfg, d, &user.SignedInUser{OrgID: orgID, Permissions: userPermissions}, ac, fakeDashboardService) g, err := NewAccessControlDashboardGuardianByDashboard(context.Background(), cfg, d, &user.SignedInUser{OrgID: orgID, Permissions: userPermissions}, ac, fakeDashboardService, folderSvc, log.NewNopLogger())
require.NoError(t, err) require.NoError(t, err)
return g return g
} }

@ -15,6 +15,7 @@ var (
ErrGuardianGetDashboardFailure = errutil.Internal("guardian.getDashboardFailure", errutil.WithPublicMessage("Failed to get dashboard")) ErrGuardianGetDashboardFailure = errutil.Internal("guardian.getDashboardFailure", errutil.WithPublicMessage("Failed to get dashboard"))
ErrGuardianDashboardNotFound = errutil.NotFound("guardian.dashboardNotFound") ErrGuardianDashboardNotFound = errutil.NotFound("guardian.dashboardNotFound")
ErrGuardianFolderNotFound = errutil.NotFound("guardian.folderNotFound") ErrGuardianFolderNotFound = errutil.NotFound("guardian.folderNotFound")
ErrGuardianGetFolderFailure = errutil.Internal("guardian.getFolderFailure", errutil.WithPublicMessage("Failed to get folder"))
) )
// DashboardGuardian to be used for guard against operations without access on dashboard and acl // DashboardGuardian to be used for guard against operations without access on dashboard and acl
@ -33,15 +34,15 @@ var New = func(ctx context.Context, dashId int64, orgId int64, user identity.Req
panic("no guardian factory implementation provided") panic("no guardian factory implementation provided")
} }
// NewByUID factory for creating a new dashboard guardian instance // NewByDashboard factory for creating a new dashboard guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned // When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByUID = func(ctx context.Context, dashUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) { var NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) {
panic("no guardian factory implementation provided") panic("no guardian factory implementation provided")
} }
// NewByDashboard factory for creating a new dashboard guardian instance // NewByFolderUID factory for creating a new folder guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned // When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) { var NewByFolderUID = func(ctx context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
panic("no guardian factory implementation provided") panic("no guardian factory implementation provided")
} }
@ -107,18 +108,17 @@ func MockDashboardGuardian(mock *FakeDashboardGuardian) {
mock.User = user mock.User = user
return mock, nil return mock, nil
} }
NewByDashboard = func(_ context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) {
NewByUID = func(_ context.Context, dashUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId mock.OrgID = orgId
mock.DashUID = dashUID mock.DashUID = dash.UID
mock.DashID = dash.ID
mock.User = user mock.User = user
return mock, nil return mock, nil
} }
NewByDashboard = func(_ context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByFolderUID = func(_ context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId mock.OrgID = orgId
mock.DashUID = dash.UID mock.DashUID = folderUID
mock.DashID = dash.ID
mock.User = user mock.User = user
return mock, nil return mock, nil
} }

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
@ -16,28 +17,29 @@ type Provider struct{}
func ProvideService( func ProvideService(
cfg *setting.Cfg, ac accesscontrol.AccessControl, cfg *setting.Cfg, ac accesscontrol.AccessControl,
dashboardService dashboards.DashboardService, teamService team.Service, dashboardService dashboards.DashboardService, teamService team.Service,
folderService folder.Service,
) *Provider { ) *Provider {
// TODO: Fix this hack, see https://github.com/grafana/grafana-enterprise/issues/2935 // TODO: Fix this hack, see https://github.com/grafana/grafana-enterprise/issues/2935
InitAccessControlGuardian(cfg, ac, dashboardService) InitAccessControlGuardian(cfg, ac, dashboardService, folderService, log.New("guardian"))
return &Provider{} return &Provider{}
} }
func InitAccessControlGuardian( func InitAccessControlGuardian(
cfg *setting.Cfg, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, cfg *setting.Cfg, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, folderService folder.Service, logger log.Logger,
) { ) {
New = func(ctx context.Context, dashId int64, orgId int64, user identity.Requester) (DashboardGuardian, error) { New = func(ctx context.Context, dashId int64, orgId int64, user identity.Requester) (DashboardGuardian, error) {
return NewAccessControlDashboardGuardian(ctx, cfg, dashId, user, ac, dashboardService) return NewAccessControlDashboardGuardian(ctx, cfg, dashId, user, ac, dashboardService, folderService, logger)
} }
NewByUID = func(ctx context.Context, dashUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) {
return NewAccessControlDashboardGuardianByUID(ctx, cfg, dashUID, user, ac, dashboardService) return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, ac, dashboardService, folderService, logger)
} }
NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByFolderUID = func(ctx context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, ac, dashboardService) return NewAccessControlFolderGuardianByUID(ctx, cfg, folderUID, user, ac, dashboardService, folderService)
} }
NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) {
return NewAccessControlFolderGuardian(ctx, cfg, f, user, ac, dashboardService) return NewAccessControlFolderGuardian(ctx, cfg, f, user, ac, orgId, dashboardService, folderService)
} }
} }

@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"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/org"
@ -41,7 +42,7 @@ func (l *LibraryElementService) requireEditPermissionsOnFolderUID(ctx context.Co
return dashboards.ErrFolderAccessDenied return dashboards.ErrFolderAccessDenied
} }
g, err := guardian.NewByUID(ctx, folderUID, user.GetOrgID(), user) g, err := guardian.NewByFolderUID(ctx, folderUID, user.GetOrgID(), user)
if err != nil { if err != nil {
return err return err
} }
@ -67,7 +68,10 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
return dashboards.ErrFolderAccessDenied return dashboards.ErrFolderAccessDenied
} }
g, err := guardian.New(ctx, folderID, user.GetOrgID(), user) g, err := guardian.NewByFolder(ctx, &folder.Folder{
ID: folderID,
OrgID: user.GetOrgID(),
}, user.GetOrgID(), user)
if err != nil { if err != nil {
return err return err
} }
@ -88,7 +92,10 @@ func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Conte
return nil return nil
} }
g, err := guardian.New(ctx, folderID, user.GetOrgID(), user) g, err := guardian.NewByFolder(ctx, &folder.Folder{
ID: folderID,
OrgID: user.GetOrgID(),
}, user.GetOrgID(), user)
if err != nil { if err != nil {
return err return err
} }

@ -539,7 +539,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilterUIDs is set to existing folders, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilterUIDs is set to existing folders, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
newFolder := createFolder(t, sc, "NewFolder") newFolder := createFolder(t, sc, "NewFolder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2") command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
@ -608,7 +608,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to a nonexistent folders, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to a nonexistent folders, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
newFolder := createFolder(t, sc, "NewFolder") newFolder := createFolder(t, sc, "NewFolder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(newFolder.ID, sc.folder.UID, "Text - Library Panel2") command := getCreatePanelCommand(newFolder.ID, sc.folder.UID, "Text - Library Panel2")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)

@ -24,7 +24,7 @@ func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to patch a library panel that exists, it should succeed", scenarioWithPanel(t, "When an admin tries to patch a library panel that exists, it should succeed",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
newFolder := createFolder(t, sc, "NewFolder") newFolder := createFolder(t, sc, "NewFolder", nil)
cmd := model.PatchLibraryElementCommand{ cmd := model.PatchLibraryElementCommand{
FolderID: newFolder.ID, // nolint:staticcheck FolderID: newFolder.ID, // nolint:staticcheck
FolderUID: &newFolder.UID, FolderUID: &newFolder.UID,
@ -91,7 +91,7 @@ func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result", scenarioWithPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
newFolder := createFolder(t, sc, "NewFolder") newFolder := createFolder(t, sc, "NewFolder", nil)
cmd := model.PatchLibraryElementCommand{ cmd := model.PatchLibraryElementCommand{
FolderID: newFolder.ID, // nolint:staticcheck FolderID: newFolder.ID, // nolint:staticcheck
FolderUID: &newFolder.UID, FolderUID: &newFolder.UID,
@ -335,7 +335,7 @@ func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail", scenarioWithPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
newFolder := createFolder(t, sc, "NewFolder") newFolder := createFolder(t, sc, "NewFolder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel") command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel")
sc.ctx.Req.Body = mockRequestBody(command) sc.ctx.Req.Body = mockRequestBody(command)

@ -38,7 +38,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role), testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role),
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name") command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
@ -56,7 +56,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role), testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role),
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", nil)
command := getCreatePanelCommand(0, "", "Library Panel Name") command := getCreatePanelCommand(0, "", "Library Panel Name")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext) resp := sc.service.createHandler(sc.reqContext)
@ -178,7 +178,7 @@ func TestLibraryElementCreatePermissions(t *testing.T) {
for _, testCase := range accessCases { for _, testCase := range accessCases {
testScenario(t, testCase.desc, testScenario(t, testCase.desc,
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", nil)
sc.reqContext.SignedInUser.Permissions = map[int64]map[string][]string{ sc.reqContext.SignedInUser.Permissions = map[int64]map[string][]string{
1: testCase.permissions, 1: testCase.permissions,
} }
@ -235,14 +235,14 @@ func TestLibraryElementPatchPermissions(t *testing.T) {
for _, testCase := range accessCases { for _, testCase := range accessCases {
testScenario(t, testCase.desc, testScenario(t, testCase.desc,
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
fromFolder := createFolder(t, sc, "FromFolder") fromFolder := createFolder(t, sc, "FromFolder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(fromFolder.ID, fromFolder.UID, "Library Panel Name") command := getCreatePanelCommand(fromFolder.ID, fromFolder.UID, "Library Panel Name")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext) resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp) result := validateAndUnMarshalResponse(t, resp)
toFolder := createFolder(t, sc, "ToFolder") toFolder := createFolder(t, sc, "ToFolder", nil)
sc.reqContext.SignedInUser.Permissions = map[int64]map[string][]string{ sc.reqContext.SignedInUser.Permissions = map[int64]map[string][]string{
1: testCase.permissions, 1: testCase.permissions,
@ -268,6 +268,7 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
desc: "can delete library elements when granted write access to the correct folder", desc: "can delete library elements when granted write access to the correct folder",
permissions: map[string][]string{ permissions: map[string][]string{
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Folder")}, dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Folder")},
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Folder")},
}, },
status: http.StatusOK, status: http.StatusOK,
}, },
@ -275,6 +276,7 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
desc: "can delete library elements when granted write access to all folders", desc: "can delete library elements when granted write access to all folders",
permissions: map[string][]string{ permissions: map[string][]string{
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceAllScope()}, dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceAllScope()},
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceAllScope()},
}, },
status: http.StatusOK, status: http.StatusOK,
}, },
@ -282,6 +284,7 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
desc: "can't delete library elements when granted write access to the wrong folder", desc: "can't delete library elements when granted write access to the wrong folder",
permissions: map[string][]string{ permissions: map[string][]string{
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Other_folder")}, dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Other_folder")},
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("Other_folder")},
}, },
status: http.StatusForbidden, status: http.StatusForbidden,
}, },
@ -297,7 +300,7 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
for _, testCase := range accessCases { for _, testCase := range accessCases {
testScenario(t, testCase.desc, testScenario(t, testCase.desc,
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", sc.service.folderService)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name") command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
@ -327,7 +330,7 @@ func TestLibraryElementsWithMissingFolders(t *testing.T) {
testScenario(t, "When a user tries to patch a library panel by moving it to a folder that doesn't exist, it should fail", testScenario(t, "When a user tries to patch a library panel by moving it to a folder that doesn't exist, it should fail",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", nil)
// nolint:staticcheck // nolint:staticcheck
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name") command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
sc.reqContext.Req.Body = mockRequestBody(command) sc.reqContext.Req.Body = mockRequestBody(command)
@ -368,7 +371,7 @@ func TestLibraryElementsGetPermissions(t *testing.T) {
for _, testCase := range getCases { for _, testCase := range getCases {
testScenario(t, testCase.desc, testScenario(t, testCase.desc,
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
folder := createFolder(t, sc, "Folder") folder := createFolder(t, sc, "Folder", nil)
// nolint:staticcheck // nolint:staticcheck
cmd := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel") cmd := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel")
sc.reqContext.Req.Body = mockRequestBody(cmd) sc.reqContext.Req.Body = mockRequestBody(cmd)
@ -419,7 +422,7 @@ func TestLibraryElementsGetAllPermissions(t *testing.T) {
testScenario(t, testCase.desc, testScenario(t, testCase.desc,
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
for i := 1; i <= 2; i++ { for i := 1; i <= 2; i++ {
folder := createFolder(t, sc, fmt.Sprintf("Folder%d", i)) folder := createFolder(t, sc, fmt.Sprintf("Folder%d", i), nil)
// nolint:staticcheck // nolint:staticcheck
cmd := getCreatePanelCommand(folder.ID, folder.UID, fmt.Sprintf("Library Panel %d", i)) cmd := getCreatePanelCommand(folder.ID, folder.UID, fmt.Sprintf("Library Panel %d", i))
sc.reqContext.Req.Body = mockRequestBody(cmd) sc.reqContext.Req.Body = mockRequestBody(cmd)

@ -32,7 +32,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl" "github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements/model" "github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -99,7 +98,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail", scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID+"xxxx") err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID+"xxxx")
require.EqualError(t, err, dashboards.ErrFolderNotFound.Error()) require.EqualError(t, err, guardian.ErrGuardianFolderNotFound.Errorf("failed to get folder by UID: %w", dashboards.ErrFolderNotFound).Error())
}) })
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too", scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
@ -300,17 +299,19 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
require.NoError(t, err) require.NoError(t, err)
ac := actest.FakeAccessControl{ExpectedEvaluate: true} ac := actest.FakeAccessControl{ExpectedEvaluate: true}
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) dashboardPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
var expectedFolder *folder.Folder fStore := folderimpl.ProvideStore(sqlStore)
if dash.FolderUID != "" || dash.FolderID != 0 { // nolint:staticcheck folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore,
expectedFolder = &folder.Folder{ID: folderID, UID: folderUID} folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), cfg, nil, tracing.InitializeTracerForTest())
} _, err = folderSvc.Create(context.Background(), &folder.CreateFolderCommand{UID: folderUID, SignedInUser: &user, Title: folderUID + "-title"})
require.NoError(t, err)
service, err := dashboardservice.ProvideDashboardServiceImpl( service, err := dashboardservice.ProvideDashboardServiceImpl(
cfg, dashboardStore, folderStore, cfg, dashboardStore, folderStore,
features, folderPermissions, ac, features, folderPermissions, ac,
&foldertest.FakeService{ExpectedFolder: expectedFolder}, folderSvc,
folder.NewFakeStore(), folder.NewFakeStore(),
nil, nil,
nil, nil,
@ -327,9 +328,10 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
return dashboard return dashboard
} }
func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder { func createFolder(t *testing.T, sc scenarioContext, title string, folderSvc folder.Service) *folder.Folder {
t.Helper() t.Helper()
if folderSvc == nil {
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
cfg := setting.NewCfg() cfg := setting.NewCfg()
ac := actest.FakeAccessControl{ExpectedEvaluate: true} ac := actest.FakeAccessControl{ExpectedEvaluate: true}
@ -338,11 +340,12 @@ 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, folderSvc = folderimpl.ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore,
features, supportbundlestest.NewFakeBundleService(), cfg, nil, tracing.InitializeTracerForTest()) features, supportbundlestest.NewFakeBundleService(), cfg, 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 := folderSvc.Create(ctx, &folder.CreateFolderCommand{
OrgID: sc.user.OrgID, Title: title, UID: title, SignedInUser: &sc.user, OrgID: sc.user.OrgID, Title: title, UID: title, SignedInUser: &sc.user,
}) })
require.NoError(t, err) require.NoError(t, err)
@ -399,15 +402,18 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
fStore := folderimpl.ProvideStore(sqlStore)
folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore,
folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), cfg, nil, tracing.InitializeTracerForTest())
dashboardService, svcErr := dashboardservice.ProvideDashboardServiceImpl( dashboardService, svcErr := dashboardservice.ProvideDashboardServiceImpl(
cfg, dashboardStore, folderStore, cfg, dashboardStore, folderStore,
features, folderPermissions, ac, features, folderPermissions, ac,
foldertest.NewFakeService(), folder.NewFakeStore(), folderSvc, fStore,
nil, nil, nil, nil, quotaService, nil, nil, nil, nil, nil, quotaService, nil,
) )
require.NoError(t, svcErr) require.NoError(t, svcErr)
dashboardService.RegisterDashboardPermissions(dashboardPermissions) dashboardService.RegisterDashboardPermissions(dashboardPermissions)
guardian.InitAccessControlGuardian(cfg, ac, dashboardService) guardian.InitAccessControlGuardian(cfg, ac, dashboardService, folderSvc, log.NewNopLogger())
testScenario(t, desc, func(t *testing.T, sc scenarioContext) { testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
// nolint:staticcheck // nolint:staticcheck
@ -462,23 +468,23 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
fStore := folderimpl.ProvideStore(sqlStore)
folderSvc := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore,
folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), cfg, nil, tracing.InitializeTracerForTest())
dashService, dashSvcErr := dashboardservice.ProvideDashboardServiceImpl( dashService, dashSvcErr := dashboardservice.ProvideDashboardServiceImpl(
cfg, dashboardStore, folderStore, cfg, dashboardStore, folderStore,
features, folderPermissions, ac, features, folderPermissions, ac,
foldertest.NewFakeService(), folder.NewFakeStore(), folderSvc, fStore,
nil, nil, nil, nil, quotaService, nil, nil, nil, nil, nil, quotaService, nil,
) )
require.NoError(t, dashSvcErr) require.NoError(t, dashSvcErr)
dashService.RegisterDashboardPermissions(dashboardPermissions) dashService.RegisterDashboardPermissions(dashboardPermissions)
guardian.InitAccessControlGuardian(cfg, ac, dashService) guardian.InitAccessControlGuardian(cfg, ac, dashService, folderSvc, log.NewNopLogger())
fStore := folderimpl.ProvideStore(sqlStore)
folderSrv := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracer), dashboardStore, folderStore, sqlStore,
features, supportbundlestest.NewFakeBundleService(), cfg, nil, tracing.InitializeTracerForTest())
service := LibraryElementService{ service := LibraryElementService{
Cfg: cfg, Cfg: cfg,
features: featuremgmt.WithFeatures(), features: featuremgmt.WithFeatures(),
SQLStore: sqlStore, SQLStore: sqlStore,
folderService: folderSrv, folderService: folderSvc,
} }
// deliberate difference between signed in user and user in db to make it crystal clear // deliberate difference between signed in user and user in db to make it crystal clear
@ -510,7 +516,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
}, },
} }
sc.folder = createFolder(t, sc, "ScenarioFolder") sc.folder = createFolder(t, sc, "ScenarioFolder", folderSvc)
fn(t, sc) fn(t, sc)
}) })

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/kinds/librarypanel" "github.com/grafana/grafana/pkg/kinds/librarypanel"
@ -823,18 +824,19 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
ac := actest.FakeAccessControl{ExpectedEvaluate: true} ac := actest.FakeAccessControl{ExpectedEvaluate: true}
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 1}, nil)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
dashPermissionService := acmock.NewMockedPermissionsService() dashPermissionService := acmock.NewMockedPermissionsService()
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolder = &folder.Folder{ID: 1}
dashService, err := dashboardservice.ProvideDashboardServiceImpl( dashService, err := dashboardservice.ProvideDashboardServiceImpl(
cfg, dashStore, folderStore, cfg, dashStore, folderStore,
features, acmock.NewMockedPermissionsService(), ac, features, acmock.NewMockedPermissionsService(), ac,
foldertest.NewFakeService(), folder.NewFakeStore(), folderSvc, folder.NewFakeStore(),
nil, nil, nil, nil, quotaService, nil, nil, nil, nil, nil, quotaService, nil,
) )
require.NoError(t, err) require.NoError(t, err)
dashService.RegisterDashboardPermissions(dashPermissionService) dashService.RegisterDashboardPermissions(dashPermissionService)
guardian.InitAccessControlGuardian(cfg, ac, dashService) guardian.InitAccessControlGuardian(cfg, ac, dashService, folderSvc, log.NewNopLogger())
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore)) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
require.NoError(t, err) require.NoError(t, err)

Loading…
Cancel
Save