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

This commit is contained in:
Stephanie Hingtgen 2025-01-23 09:30:14 -07:00 committed by GitHub
parent 59b246dbea
commit 192a81d07f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 207 additions and 147 deletions

View File

@ -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())
}) })
} }

View File

@ -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())
}) })
} }

View File

@ -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)

View File

@ -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 { if err != nil {
return nil, err return nil, err
} }
return guard, nil 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 {
return nil, err
}
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

View File

@ -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)

View File

@ -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
} }

View File

@ -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,68 +60,22 @@ func NewAccessControlDashboardGuardian(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
}, folderService: foldersService,
dashboard: dashboard,
}, nil
}
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardUID.
func NewAccessControlDashboardGuardianByUID(
ctx context.Context, cfg *setting.Cfg, dashboardUID string, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService,
) (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 {
return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
ac: ac,
dashboardService: dashboardService,
},
folder: dashboards.FromDashboard(dashboard),
}, nil
}
return &accessControlDashboardGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
user: user,
ac: ac,
dashboardService: dashboardService,
}, },
dashboard: dashboard, dashboard: dashboard,
}, nil }, nil
} }
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard. // NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard.
// This constructor should be preferred over the other two if the dashboard in available // This constructor should be preferred over the other two if the dashboard is available
// since it avoids querying the database for fetching the dashboard. // since it avoids querying the database for fetching the dashboard.
func NewAccessControlDashboardGuardianByDashboard( func NewAccessControlDashboardGuardianByDashboard(
ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user identity.Requester, ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, folderService folder.Service,
logger log.Logger,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
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,
@ -128,6 +84,7 @@ func NewAccessControlDashboardGuardianByDashboard(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: folderService,
}, },
folder: dashboards.FromDashboard(dashboard), folder: dashboards.FromDashboard(dashboard),
}, nil }, nil
@ -141,16 +98,35 @@ func NewAccessControlDashboardGuardianByDashboard(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: folderService,
}, },
dashboard: dashboard, dashboard: dashboard,
}, nil }, nil
} }
// NewAccessControlFolderGuardian creates a folder guardian by the provided folder. // NewAccessControlFolderGuardianByUID creates a folder guardian by the provided folderUID.
func NewAccessControlFolderGuardian( func NewAccessControlFolderGuardianByUID(
ctx context.Context, cfg *setting.Cfg, f *folder.Folder, user identity.Requester, ctx context.Context, cfg *setting.Cfg, folderUID string, user identity.Requester,
ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, foldersService folder.Service,
) (DashboardGuardian, error) { ) (DashboardGuardian, error) {
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,
@ -159,6 +135,44 @@ func NewAccessControlFolderGuardian(
user: user, user: user,
ac: ac, ac: ac,
dashboardService: dashboardService, dashboardService: dashboardService,
folderService: foldersService,
},
folder: f,
}, nil
}
// NewAccessControlFolderGuardian creates a folder guardian by the provided folder.
func NewAccessControlFolderGuardian(
ctx context.Context, cfg *setting.Cfg, f *folder.Folder, user identity.Requester,
ac accesscontrol.AccessControl, orgID int64, dashboardService dashboards.DashboardService,
folderService folder.Service,
) (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{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
ac: ac,
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
} }

View File

@ -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
} }

View File

@ -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,18 +34,18 @@ 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
// 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) {
panic("no guardian factory implementation provided")
}
// NewByDashboard 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 NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, 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")
} }
// NewByFolderUID factory for creating a new folder guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByFolderUID = func(ctx context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
panic("no guardian factory implementation provided")
}
// NewByFolder factory for creating a new folder guardian instance // NewByFolder 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 NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) { var NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) {
@ -107,14 +108,6 @@ func MockDashboardGuardian(mock *FakeDashboardGuardian) {
mock.User = user mock.User = user
return mock, nil return mock, nil
} }
NewByUID = func(_ context.Context, dashUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId
mock.DashUID = dashUID
mock.User = user
return mock, nil
}
NewByDashboard = func(_ context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByDashboard = func(_ context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId mock.OrgID = orgId
mock.DashUID = dash.UID mock.DashUID = dash.UID
@ -123,6 +116,13 @@ func MockDashboardGuardian(mock *FakeDashboardGuardian) {
return mock, nil return mock, nil
} }
NewByFolderUID = func(_ context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId
mock.DashUID = folderUID
mock.User = user
return mock, nil
}
NewByFolder = func(_ context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByFolder = func(_ context.Context, f *folder.Folder, orgId int64, user identity.Requester) (DashboardGuardian, error) {
mock.OrgID = orgId mock.OrgID = orgId
mock.DashUID = f.UID mock.DashUID = f.UID

View File

@ -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) {
return NewAccessControlDashboardGuardianByUID(ctx, cfg, dashUID, user, ac, dashboardService)
} }
NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) { NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user identity.Requester) (DashboardGuardian, error) {
return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, ac, dashboardService) return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, ac, dashboardService, folderService, logger)
}
NewByFolderUID = func(ctx context.Context, folderUID string, orgId int64, user identity.Requester) (DashboardGuardian, error) {
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)
} }
} }

View File

@ -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
} }

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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,22 +328,24 @@ 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()
features := featuremgmt.WithFeatures() if folderSvc == nil {
cfg := setting.NewCfg() features := featuremgmt.WithFeatures()
ac := actest.FakeAccessControl{ExpectedEvaluate: true} cfg := setting.NewCfg()
dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore)) ac := actest.FakeAccessControl{ExpectedEvaluate: true}
require.NoError(t, err) dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore))
require.NoError(t, err)
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)
}) })

View File

@ -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)