Nested folders: Remove feature flag (#109212)
This commit is contained in:
@@ -147,10 +147,7 @@ func (s *Service) getUserPermissions(ctx context.Context, user identity.Requeste
|
||||
permissions = append(permissions, basicRole.Permissions...)
|
||||
}
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
permissions = append(permissions, SharedWithMeFolderPermission)
|
||||
}
|
||||
permissions = append(permissions, SharedWithMeFolderPermission)
|
||||
|
||||
// we don't care about the error here, if this fails we get 0 and no
|
||||
// permission assigned to user will be returned, only for org role.
|
||||
@@ -232,9 +229,7 @@ func (s *Service) getUserDirectPermissions(ctx context.Context, user identity.Re
|
||||
}
|
||||
|
||||
permissions = s.actionResolver.ExpandActionSets(permissions)
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
permissions = append(permissions, SharedWithMeFolderPermission)
|
||||
}
|
||||
permissions = append(permissions, SharedWithMeFolderPermission)
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
@@ -933,7 +933,8 @@ func TestIntegrationService_SaveExternalServiceRole(t *testing.T) {
|
||||
// Check that the permissions and assignment are stored correctly
|
||||
perms, errGetPerms := ac.getUserPermissions(ctx, &user.SignedInUser{OrgID: r.cmd.AssignmentOrgID, UserID: 2}, accesscontrol.Options{})
|
||||
require.NoError(t, errGetPerms)
|
||||
assert.ElementsMatch(t, r.cmd.Permissions, perms)
|
||||
// shared with me is added by default for all users in pkg/services/accesscontrol/acimpl/service.go
|
||||
assert.Equal(t, append([]accesscontrol.Permission{{Action: "folders:read", Scope: "folders:uid:sharedwithme"}}, r.cmd.Permissions...), perms)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -989,7 +990,8 @@ func TestIntegrationService_DeleteExternalServiceRole(t *testing.T) {
|
||||
// Check that the permissions and assignment are removed correctly
|
||||
perms, errGetPerms := ac.getUserPermissions(ctx, &user.SignedInUser{OrgID: tt.initCmd.AssignmentOrgID, UserID: 2}, accesscontrol.Options{})
|
||||
require.NoError(t, errGetPerms)
|
||||
assert.Empty(t, perms)
|
||||
// shared with me is added by default for all users in pkg/services/accesscontrol/acimpl/service.go
|
||||
assert.Equal(t, []accesscontrol.Permission{{Action: "folders:read", Scope: "folders:uid:sharedwithme"}}, perms)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
@@ -831,10 +832,9 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
||||
|
||||
if len(query.FolderUIDs) > 0 {
|
||||
filters = append(filters, searchstore.FolderUIDFilter{
|
||||
Dialect: d.store.GetDialect(),
|
||||
OrgID: orgID,
|
||||
UIDs: query.FolderUIDs,
|
||||
NestedFoldersEnabled: d.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders),
|
||||
Dialect: d.store.GetDialect(),
|
||||
OrgID: orgID,
|
||||
UIDs: query.FolderUIDs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -900,6 +900,9 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
||||
if item.Term != "" {
|
||||
item.Tags = append(item.Tags, item.Term)
|
||||
}
|
||||
if item.FolderTitle != "" {
|
||||
item.FolderSlug = slugify.Slugify(item.FolderTitle)
|
||||
}
|
||||
seen[item.ID] = len(uniqueRes)
|
||||
uniqueRes = append(uniqueRes, item)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -30,13 +31,16 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
||||
var flder, dashInRoot, childDash *dashboards.Dashboard
|
||||
var currentUser *user.SignedInUser
|
||||
var dashboardStore dashboards.Store
|
||||
var folderStore *folderimpl.FolderStoreImpl
|
||||
|
||||
setup := func() {
|
||||
sqlStore, cfg = db.InitTestDBWithCfg(t)
|
||||
var err error
|
||||
dashboardStore, err = ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
flder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||
folderStore = folderimpl.ProvideStore(sqlStore)
|
||||
require.NoError(t, err)
|
||||
flder = insertTestDashFolder(t, dashboardStore, folderStore, "1 test dash folder", 1, 0, "", "prod", "webapp")
|
||||
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod", "webapp")
|
||||
childDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, flder.ID, flder.UID, false, "prod", "webapp")
|
||||
insertTestDashboard(t, dashboardStore, "test dash 45", 1, flder.ID, flder.UID, false, "prod")
|
||||
@@ -132,8 +136,8 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
||||
sqlStore, cfg = db.InitTestDBWithCfg(t)
|
||||
var err error
|
||||
require.NoError(t, err)
|
||||
folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod")
|
||||
folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, "", true, "prod")
|
||||
folder1 = insertTestDashFolder(t, dashboardStore, folderStore, "1 test dash folder", 1, 0, "", "prod")
|
||||
folder2 = insertTestDashFolder(t, dashboardStore, folderStore, "2 test dash folder", 1, 0, "", "prod")
|
||||
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod")
|
||||
childDash1 = insertTestDashboard(t, dashboardStore, "child dash 1", 1, folder1.ID, folder1.UID, false, "prod")
|
||||
childDash2 = insertTestDashboard(t, dashboardStore, "child dash 2", 1, folder2.ID, folder2.UID, false, "prod")
|
||||
@@ -190,8 +194,8 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
||||
})
|
||||
t.Run("and a dashboard is moved from folder with acl to the folder without an acl", func(t *testing.T) {
|
||||
setup2()
|
||||
moveDashboard(t, dashboardStore, 1, childDash1.Data, folder2.ID, childDash2.FolderUID)
|
||||
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID), dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}, dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}}}
|
||||
moveDashboard(t, dashboardStore, 1, childDash1.Data, folder2.ID, childDash2.UID)
|
||||
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID), dashboards.ScopeDashboardsProvider.GetResourceScopeUID(folder2.UID), dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}, dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}}}
|
||||
actest.AddUserPermissionToDB(t, sqlStore, currentUser)
|
||||
|
||||
t.Run("should return folder without acl and its children", func(t *testing.T) {
|
||||
@@ -202,7 +206,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
||||
}
|
||||
hits, err := testSearchDashboards(dashboardStore, query)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(hits), 4)
|
||||
assert.Equal(t, 4, len(hits))
|
||||
assert.Equal(t, hits[0].ID, folder2.ID)
|
||||
assert.Equal(t, hits[1].ID, childDash1.ID)
|
||||
assert.Equal(t, hits[2].ID, childDash2.ID)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
libmodel "github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
@@ -51,7 +52,8 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
||||
// test dash 23
|
||||
// test dash 45
|
||||
// test dash 67
|
||||
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||
folderStore := folderimpl.ProvideStore(sqlStore)
|
||||
savedFolder = insertTestDashFolder(t, dashboardStore, folderStore, "1 test dash folder", 1, 0, "", "prod", "webapp")
|
||||
savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.ID, savedFolder.UID, false, "prod")
|
||||
savedDash2 = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod")
|
||||
@@ -814,7 +816,7 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||
|
||||
sqlStore := db.InitTestDB(t)
|
||||
cfg := setting.NewCfg()
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPanelTitleSearch)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPanelTitleSearch)
|
||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -931,7 +933,7 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
||||
}
|
||||
|
||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPanelTitleSearch)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPanelTitleSearch)
|
||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -970,110 +972,89 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
||||
folderIDs []int64
|
||||
folderUIDs []string
|
||||
query string
|
||||
expectedResult map[string][]res
|
||||
expectedResult []res
|
||||
typ string
|
||||
}{
|
||||
{
|
||||
desc: "find dashboard under general using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under general"}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||
},
|
||||
desc: "find dashboard under general using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: []res{{title: "dashboard under general"}},
|
||||
},
|
||||
{
|
||||
desc: "find dashboard under general using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under general"}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||
},
|
||||
desc: "find dashboard under general using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: []res{{title: "dashboard under general"}},
|
||||
},
|
||||
{
|
||||
desc: "find dashboard under f0 using folder UID",
|
||||
folderUIDs: []string{f0.UID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||
},
|
||||
desc: "find dashboard under f0 using folder UID",
|
||||
folderUIDs: []string{f0.UID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: []res{{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||
},
|
||||
{
|
||||
desc: "find dashboard under f0 or f1 using folder UID",
|
||||
folderUIDs: []string{f0.UID, f1.UID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||
expectedResult: []res{
|
||||
{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "find dashboard under general or f0 using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID, f0.UID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under general"}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under general"}},
|
||||
expectedResult: []res{
|
||||
{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under general"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "find dashboard under general or f0 or f1 using folder UID",
|
||||
folderUIDs: []string{folder.GeneralFolderUID, f0.UID, f1.UID},
|
||||
typ: searchstore.TypeDashboard,
|
||||
expectedResult: map[string][]res{
|
||||
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||
{title: "dashboard under general"}},
|
||||
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||
{title: "dashboard under general"}},
|
||||
expectedResult: []res{
|
||||
{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||
{title: "dashboard under general"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "find subfolder",
|
||||
folderUIDs: []string{f0.UID},
|
||||
typ: searchstore.TypeFolder,
|
||||
expectedResult: map[string][]res{
|
||||
"": {},
|
||||
featuremgmt.FlagNestedFolders: {{title: subfolder.Title, folderUID: f0.UID, folderTitle: f0.Title}},
|
||||
},
|
||||
desc: "find subfolder",
|
||||
folderUIDs: []string{f0.UID},
|
||||
typ: searchstore.TypeFolder,
|
||||
expectedResult: []res{{title: subfolder.Title, folderUID: f0.UID, folderTitle: f0.Title}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for featureFlags := range tc.expectedResult {
|
||||
t.Run(fmt.Sprintf("%s with featureFlags: %v", tc.desc, featureFlags), func(t *testing.T) {
|
||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(featureFlags), tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
res, err := dashboardStore.FindDashboards(context.Background(), &dashboards.FindPersistedDashboardsQuery{
|
||||
SignedInUser: user,
|
||||
Type: tc.typ,
|
||||
FolderUIDs: tc.folderUIDs,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(tc.expectedResult[featureFlags]), len(res))
|
||||
|
||||
for i, r := range tc.expectedResult[featureFlags] {
|
||||
assert.Equal(t, r.title, res[i].Title)
|
||||
if r.folderUID != "" {
|
||||
assert.Equal(t, r.folderUID, res[i].FolderUID)
|
||||
} else {
|
||||
assert.Empty(t, res[i].FolderUID)
|
||||
}
|
||||
if r.folderTitle != "" {
|
||||
assert.Equal(t, r.folderTitle, res[i].FolderTitle)
|
||||
} else {
|
||||
assert.Empty(t, res[i].FolderTitle)
|
||||
}
|
||||
}
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
res, err := dashboardStore.FindDashboards(context.Background(), &dashboards.FindPersistedDashboardsQuery{
|
||||
SignedInUser: user,
|
||||
Type: tc.typ,
|
||||
FolderUIDs: tc.folderUIDs,
|
||||
})
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(tc.expectedResult), len(res))
|
||||
|
||||
for i, r := range tc.expectedResult {
|
||||
assert.Equal(t, r.title, res[i].Title)
|
||||
if r.folderUID != "" {
|
||||
assert.Equal(t, r.folderUID, res[i].FolderUID)
|
||||
} else {
|
||||
assert.Empty(t, res[i].FolderUID)
|
||||
}
|
||||
if r.folderTitle != "" {
|
||||
assert.Equal(t, r.folderTitle, res[i].FolderTitle)
|
||||
} else {
|
||||
assert.Empty(t, res[i].FolderTitle)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,6 +1241,34 @@ func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title st
|
||||
return dash
|
||||
}
|
||||
|
||||
func insertTestDashFolder(t *testing.T, dashboardStore dashboards.Store, folderStore folder.Store, title string, orgId int64,
|
||||
folderId int64, folderUID string, tags ...interface{}) *dashboards.Dashboard {
|
||||
t.Helper()
|
||||
cmd := dashboards.SaveDashboardCommand{
|
||||
OrgID: orgId,
|
||||
FolderID: folderId, // nolint:staticcheck
|
||||
FolderUID: folderUID,
|
||||
IsFolder: true,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
}),
|
||||
}
|
||||
dash, err := dashboardStore.SaveDashboard(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
dash.Data.Set("id", dash.ID)
|
||||
dash.Data.Set("uid", dash.UID)
|
||||
_, err = folderStore.Create(context.Background(), folder.CreateFolderCommand{
|
||||
Title: title,
|
||||
UID: dash.UID,
|
||||
OrgID: orgId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return dash
|
||||
}
|
||||
|
||||
func insertTestDashboardForPlugin(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
||||
folderUID string, isFolder bool, pluginId string) *dashboards.Dashboard {
|
||||
t.Helper()
|
||||
|
||||
@@ -1162,7 +1162,7 @@ func (dr *DashboardServiceImpl) SetDefaultPermissionsAfterCreate(ctx context.Con
|
||||
isNested := obj.GetFolder() != ""
|
||||
if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesDashboards) {
|
||||
// legacy behavior
|
||||
if !isNested || !dr.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
if !isNested {
|
||||
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
|
||||
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
|
||||
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
|
||||
@@ -1392,7 +1392,7 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.FindDashboards")
|
||||
defer span.End()
|
||||
|
||||
if dr.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && len(query.FolderUIDs) > 0 && slices.Contains(query.FolderUIDs, folder.SharedWithMeFolderUID) {
|
||||
if len(query.FolderUIDs) > 0 && slices.Contains(query.FolderUIDs, folder.SharedWithMeFolderUID) {
|
||||
start := time.Now()
|
||||
userDashboardUIDs, err := dr.getUserSharedDashboardUIDs(ctx, query.SignedInUser)
|
||||
if err != nil {
|
||||
|
||||
@@ -1402,7 +1402,6 @@ func TestSearchDashboards(t *testing.T) {
|
||||
|
||||
t.Run("Should handle Shared with me folder correctly", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resourcepb.ResourceSearchRequest) bool {
|
||||
if len(req.Options.Fields) == 0 {
|
||||
@@ -2095,9 +2094,9 @@ func TestSetDefaultPermissionsAfterCreate(t *testing.T) {
|
||||
// Setup mocks and service
|
||||
dashboardStore := &dashboards.FakeDashboardStore{}
|
||||
folderStore := foldertest.FakeFolderStore{}
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
features := featuremgmt.WithFeatures()
|
||||
if tc.featureKubernetesDashboards {
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesDashboards, featuremgmt.FlagNestedFolders)
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesDashboards)
|
||||
}
|
||||
|
||||
permService := acmock.NewMockedPermissionsService()
|
||||
|
||||
@@ -118,13 +118,6 @@ var (
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaSearchAndStorageSquad,
|
||||
},
|
||||
{
|
||||
Name: "nestedFolders",
|
||||
Description: "Enable folder nesting",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
Owner: grafanaSearchAndStorageSquad,
|
||||
Expression: "true", // enabled by default
|
||||
},
|
||||
{
|
||||
Name: "alertingBacktesting",
|
||||
Description: "Rule backtesting API for alerting",
|
||||
|
||||
@@ -88,7 +88,6 @@ showDashboardValidationWarnings,2022-10-14T13:51:05Z,,2e16d5499e1cf29e67db5031fe
|
||||
interFont,2022-10-15T14:22:33Z,2022-12-01T11:59:37Z,9f5e691994c9db15f5984b196ab55fd7213b7f72,Torkel Ödegaard
|
||||
accessControlOnCall,2022-10-19T16:10:09Z,2025-02-25T12:44:40Z,717bd4a6c051d1447c9e35385a4ba176dd391c65,Gabriel MABILLE
|
||||
newDBLibrary,2022-10-26T01:20:41Z,2023-11-14T14:51:35Z,a3acfb1a48126119fd4913efbb572de162264e92,Ryan McKinley
|
||||
nestedFolders,2022-10-26T14:15:14Z,,b346ae03105af606521bbefec16722d590378646,Kristin Laemmert
|
||||
datasourceLogger,2022-11-02T13:51:51Z,2023-02-07T11:49:16Z,06705a49e236dc3ab3b35db51b7bad8625e4866f,Carl Bergquist
|
||||
promQueryBuilder,2022-11-03T17:34:01Z,2022-12-19T13:52:06Z,857e545c5ac71722c8b2d3d27621dfb453dbb8ff,Ryan McKinley
|
||||
elasticsearchBackendMigration,2022-11-10T15:35:15Z,2023-04-12T12:20:43Z,261d620f1c46eb43282cc444ffb4633b77af4283,Ivana Huckova
|
||||
|
||||
|
@@ -13,7 +13,6 @@ grpcServer,preview,@grafana/search-and-storage,false,false,false
|
||||
cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,false,false,false
|
||||
showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,false,false,false
|
||||
mysqlAnsiQuotes,experimental,@grafana/search-and-storage,false,false,false
|
||||
nestedFolders,GA,@grafana/search-and-storage,false,false,false
|
||||
alertingBacktesting,experimental,@grafana/alerting-squad,false,false,false
|
||||
editPanelCSVDragAndDrop,experimental,@grafana/dataviz-squad,false,false,true
|
||||
logsContextDatasourceUi,GA,@grafana/observability-logs,false,false,true
|
||||
|
||||
|
@@ -63,10 +63,6 @@ const (
|
||||
// Use double quotes to escape keyword in a MySQL query
|
||||
FlagMysqlAnsiQuotes = "mysqlAnsiQuotes"
|
||||
|
||||
// FlagNestedFolders
|
||||
// Enable folder nesting
|
||||
FlagNestedFolders = "nestedFolders"
|
||||
|
||||
// FlagAlertingBacktesting
|
||||
// Rule backtesting API for alerting
|
||||
FlagAlertingBacktesting = "alertingBacktesting"
|
||||
|
||||
@@ -2096,19 +2096,6 @@
|
||||
"codeowner": "@grafana/search-and-storage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "nestedFolders",
|
||||
"resourceVersion": "1753448760331",
|
||||
"creationTimestamp": "2022-10-26T14:15:14Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enable folder nesting",
|
||||
"stage": "GA",
|
||||
"codeowner": "@grafana/search-and-storage",
|
||||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "newClickhouseConfigPageDesign",
|
||||
|
||||
@@ -243,29 +243,11 @@ func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery
|
||||
}
|
||||
}
|
||||
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
qry.WithFullpath = false // do not request full path if nested folders are disabled
|
||||
qry.WithFullpathUIDs = false
|
||||
}
|
||||
|
||||
dashFolders, err := s.store.GetFolders(ctx, qry)
|
||||
if err != nil {
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders: %w", err)
|
||||
}
|
||||
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
if q.WithFullpathUIDs || q.WithFullpath {
|
||||
for _, f := range dashFolders { // and fix the full path with folder title (unescaped)
|
||||
if q.WithFullpath {
|
||||
f.Fullpath = f.Title
|
||||
}
|
||||
if q.WithFullpathUIDs {
|
||||
f.FullpathUIDs = f.UID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dashFolders, nil
|
||||
}
|
||||
|
||||
@@ -286,7 +268,7 @@ func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*fol
|
||||
return folder.RootFolder, nil
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && q.UID != nil && *q.UID == folder.SharedWithMeFolderUID {
|
||||
if q.UID != nil && *q.UID == folder.SharedWithMeFolderUID {
|
||||
return folder.SharedWithMeFolder.WithURL(), nil
|
||||
}
|
||||
|
||||
@@ -317,11 +299,6 @@ func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*fol
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
|
||||
// nolint:staticcheck
|
||||
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
f.Fullpath = f.Title // set full path to the folder title (unescaped)
|
||||
f.FullpathUIDs = f.UID // set full path to the folder UID
|
||||
}
|
||||
|
||||
return f, err
|
||||
}
|
||||
|
||||
@@ -378,7 +355,7 @@ func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQu
|
||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && q.UID == folder.SharedWithMeFolderUID {
|
||||
if q.UID == folder.SharedWithMeFolderUID {
|
||||
return s.GetSharedWithMe(ctx, q, true)
|
||||
}
|
||||
|
||||
@@ -646,7 +623,7 @@ func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*
|
||||
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetParentsLegacy")
|
||||
defer span.End()
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) || q.UID == accesscontrol.GeneralFolderUID {
|
||||
if q.UID == accesscontrol.GeneralFolderUID {
|
||||
return nil, nil
|
||||
}
|
||||
if q.UID == folder.SharedWithMeFolderUID {
|
||||
@@ -667,7 +644,7 @@ func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderComm
|
||||
dashFolder := dashboards.NewDashboardFolder(cmd.Title)
|
||||
dashFolder.OrgID = cmd.OrgID
|
||||
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && cmd.ParentUID != "" {
|
||||
if cmd.ParentUID != "" {
|
||||
// Check that the user is allowed to create a subfolder in this folder
|
||||
parentUIDScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.ParentUID)
|
||||
legacyEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, parentUIDScope)
|
||||
@@ -694,7 +671,7 @@ func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderComm
|
||||
}
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && cmd.UID == folder.SharedWithMeFolderUID {
|
||||
if cmd.UID == folder.SharedWithMeFolderUID {
|
||||
return nil, folder.ErrBadRequest.Errorf("cannot create folder with UID %s", folder.SharedWithMeFolderUID)
|
||||
}
|
||||
|
||||
@@ -819,10 +796,6 @@ func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderComm
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
return dashFolder, nil
|
||||
}
|
||||
|
||||
// always expose the dashboard store sequential ID
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
|
||||
// nolint:staticcheck
|
||||
@@ -1279,17 +1252,15 @@ func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDe
|
||||
|
||||
folders := []string{*q.UID}
|
||||
countsMap := make(folder.DescendantCounts, len(s.registry)+1)
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
descendantFolders, err := s.store.GetDescendants(ctx, q.OrgID, *q.UID)
|
||||
if err != nil {
|
||||
s.log.ErrorContext(ctx, "failed to get descendant folders", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range descendantFolders {
|
||||
folders = append(folders, f.UID)
|
||||
}
|
||||
countsMap[entity.StandardKindFolder] = int64(len(descendantFolders))
|
||||
descendantFolders, err := s.store.GetDescendants(ctx, q.OrgID, *q.UID)
|
||||
if err != nil {
|
||||
s.log.ErrorContext(ctx, "failed to get descendant folders", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range descendantFolders {
|
||||
folders = append(folders, f.UID)
|
||||
}
|
||||
countsMap[entity.StandardKindFolder] = int64(len(descendantFolders))
|
||||
|
||||
for _, v := range s.registry {
|
||||
c, err := v.CountInFolders(ctx, q.OrgID, folders, q.SignedInUser)
|
||||
|
||||
@@ -129,7 +129,7 @@ func TestIntegrationDashboardFileReader(t *testing.T) {
|
||||
}
|
||||
|
||||
sql, cfgT := db.InitTestDBWithCfg(t)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
features := featuremgmt.WithFeatures()
|
||||
fStore := folderimpl.ProvideStore(sql)
|
||||
tagService := tagimpl.ProvideService(sql)
|
||||
dashStore, err := database.ProvideDashboardStore(sql, cfgT, features, tagService)
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestIntegrationDuplicatesValidator(t *testing.T) {
|
||||
logger := log.New("test.logger")
|
||||
|
||||
sql, cfgT := db.InitTestDBWithCfg(t)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
features := featuremgmt.WithFeatures()
|
||||
fStore := folderimpl.ProvideStore(sql)
|
||||
tagService := tagimpl.ProvideService(sql)
|
||||
dashStore, err := database.ProvideDashboardStore(sql, cfgT, features, tagService)
|
||||
|
||||
@@ -220,36 +220,23 @@ func (f *accessControlDashboardPermissionFilter) buildClauses(dialect migrator.D
|
||||
}
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
builder.WriteString("(dashboard.folder_id IN (SELECT d.id FROM dashboard as d ")
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString(fmt.Sprintf("WHERE d.org_id = ? AND d.uid IN (SELECT uid FROM %s)", recQueryName))
|
||||
args = append(args, orgID)
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "folder_id", "d.id", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
builder.WriteString("(dashboard.folder_id IN (SELECT d.id FROM dashboard as d ")
|
||||
builder.WriteString("WHERE 1 = 0")
|
||||
}
|
||||
default:
|
||||
builder.WriteString("(dashboard.folder_id IN (SELECT d.id FROM dashboard as d ")
|
||||
if len(permSelectorArgs) > 0 {
|
||||
builder.WriteString("WHERE d.org_id = ? AND d.uid IN ")
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString(fmt.Sprintf("WHERE d.org_id = ? AND d.uid IN (SELECT uid FROM %s)", recQueryName))
|
||||
args = append(args, orgID)
|
||||
builder.WriteString(permSelector.String())
|
||||
args = append(args, permSelectorArgs...)
|
||||
} else {
|
||||
builder.WriteString("WHERE 1 = 0")
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "folder_id", "d.id", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
builder.WriteString("(dashboard.folder_id IN (SELECT d.id FROM dashboard as d ")
|
||||
builder.WriteString("WHERE 1 = 0")
|
||||
}
|
||||
builder.WriteString(") AND NOT dashboard.is_folder)")
|
||||
|
||||
@@ -297,33 +284,22 @@ func (f *accessControlDashboardPermissionFilter) buildClauses(dialect migrator.D
|
||||
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(dashboard.uid IN ")
|
||||
builder.WriteString(fmt.Sprintf("(SELECT uid FROM %s)", recQueryName))
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "uid", "d.uid", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
builder.WriteRune(')')
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
}
|
||||
default:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(dashboard.uid IN ")
|
||||
builder.WriteString(permSelector.String())
|
||||
args = append(args, permSelectorArgs...)
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
builder.WriteString(fmt.Sprintf("(SELECT uid FROM %s)", recQueryName))
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "uid", "d.uid", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
builder.WriteRune(')')
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
}
|
||||
builder.WriteString(" AND dashboard.is_folder)")
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
@@ -107,36 +106,22 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses(di
|
||||
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(folder.uid IN (SELECT uid FROM " + recQueryName)
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "folder", "uid", "", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
f.folderIsRequired = true
|
||||
builder.WriteString(") AND NOT dashboard.is_folder)")
|
||||
} else {
|
||||
builder.WriteString("( 1 = 0 AND NOT dashboard.is_folder)")
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(folder.uid IN (SELECT uid FROM " + recQueryName)
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "folder", "uid", "", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
default:
|
||||
builder.WriteString("(")
|
||||
if len(permSelectorArgs) > 0 {
|
||||
builder.WriteString("folder.uid IN ")
|
||||
builder.WriteString(permSelector.String())
|
||||
args = append(args, permSelectorArgs...)
|
||||
f.folderIsRequired = true
|
||||
} else {
|
||||
builder.WriteString("1 = 0 ")
|
||||
}
|
||||
builder.WriteString(" AND NOT dashboard.is_folder)")
|
||||
f.folderIsRequired = true
|
||||
builder.WriteString(") AND NOT dashboard.is_folder)")
|
||||
} else {
|
||||
builder.WriteString("( 1 = 0 AND NOT dashboard.is_folder)")
|
||||
}
|
||||
|
||||
// Include all the dashboards under the root if the user has the required permissions on the root (used to be the General folder)
|
||||
@@ -181,33 +166,22 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses(di
|
||||
}
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(dashboard.uid IN ")
|
||||
builder.WriteString(fmt.Sprintf("(SELECT uid FROM %s)", recQueryName))
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "uid", "", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
builder.WriteRune(')')
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
}
|
||||
default:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
case true:
|
||||
recQueryName := fmt.Sprintf("RecQry%d", len(f.recQueries))
|
||||
f.addRecQry(recQueryName, permSelector.String(), permSelectorArgs, orgID)
|
||||
builder.WriteString("(dashboard.uid IN ")
|
||||
builder.WriteString(permSelector.String())
|
||||
args = append(args, permSelectorArgs...)
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
builder.WriteString(fmt.Sprintf("(SELECT uid FROM %s)", recQueryName))
|
||||
default:
|
||||
nestedFoldersSelectors, nestedFoldersArgs := f.nestedFoldersSelectors(permSelector.String(), permSelectorArgs, "dashboard", "uid", "", orgID)
|
||||
builder.WriteRune('(')
|
||||
builder.WriteString(nestedFoldersSelectors)
|
||||
builder.WriteRune(')')
|
||||
args = append(args, nestedFoldersArgs...)
|
||||
}
|
||||
} else {
|
||||
builder.WriteString("(1 = 0")
|
||||
}
|
||||
builder.WriteString(" AND dashboard.is_folder)")
|
||||
} else {
|
||||
|
||||
@@ -384,14 +384,12 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
|
||||
permission dashboardaccess.PermissionType
|
||||
permissions []accesscontrol.Permission
|
||||
expectedResult []string
|
||||
features []any
|
||||
}{
|
||||
{
|
||||
desc: "Should not be able to view dashboards under inherited folders with no permissions if nested folders are enabled",
|
||||
queryType: searchstore.TypeDashboard,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
permissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
@@ -399,14 +397,12 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
|
||||
queryType: searchstore.TypeFolder,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
permissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
desc: "Should not be able to view inherited dashboards and folders with no permissions if nested folders are enabled",
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
permissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
@@ -416,7 +412,6 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
|
||||
permissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeFoldersAll},
|
||||
},
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: []string{"dashboard under the root", "dashboard under parent folder", "dashboard under subfolder"},
|
||||
},
|
||||
{
|
||||
@@ -426,19 +421,8 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
|
||||
permissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersRead, Scope: "folders:uid:parent", Kind: "folders", Identifier: "parent"},
|
||||
},
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: []string{"parent", "subfolder"},
|
||||
},
|
||||
{
|
||||
desc: "Should not be able to view inherited folders if nested folders are not enabled",
|
||||
queryType: searchstore.TypeFolder,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
permissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersRead, Scope: "folders:uid:parent", Kind: "folders", Identifier: "parent"},
|
||||
},
|
||||
features: []any{},
|
||||
expectedResult: []string{"parent"},
|
||||
},
|
||||
}
|
||||
|
||||
var orgID int64 = 1
|
||||
@@ -452,7 +436,7 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
|
||||
})
|
||||
usr := &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByActionContext(context.Background(), tc.permissions)}}
|
||||
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} {
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
|
||||
m := features.GetEnabled(context.Background())
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
@@ -494,14 +478,12 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
|
||||
permission dashboardaccess.PermissionType
|
||||
signedInUserPermissions []accesscontrol.Permission
|
||||
expectedResult []string
|
||||
features []any
|
||||
}{
|
||||
{
|
||||
desc: "Should not be able to view dashboards under inherited folders with no permissions if nested folders are enabled",
|
||||
queryType: searchstore.TypeDashboard,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
signedInUserPermissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
@@ -509,14 +491,12 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
|
||||
queryType: searchstore.TypeFolder,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
signedInUserPermissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
desc: "Should not be able to view inherited dashboards and folders with no permissions if nested folders are enabled",
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
signedInUserPermissions: nil,
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
@@ -526,7 +506,6 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
|
||||
signedInUserPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeFoldersAll},
|
||||
},
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: []string{"dashboard under the root", "dashboard under parent folder", "dashboard under subfolder"},
|
||||
},
|
||||
{
|
||||
@@ -536,19 +515,8 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
|
||||
signedInUserPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersRead, Scope: "folders:uid:parent"},
|
||||
},
|
||||
features: []any{featuremgmt.FlagNestedFolders},
|
||||
expectedResult: []string{"parent", "subfolder"},
|
||||
},
|
||||
{
|
||||
desc: "Should not be able to view inherited folders if nested folders are not enabled",
|
||||
queryType: searchstore.TypeFolder,
|
||||
permission: dashboardaccess.PERMISSION_VIEW,
|
||||
signedInUserPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersRead, Scope: "folders:uid:parent"},
|
||||
},
|
||||
features: []any{},
|
||||
expectedResult: []string{"parent"},
|
||||
},
|
||||
}
|
||||
|
||||
var orgID int64 = 1
|
||||
@@ -566,7 +534,7 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
|
||||
}),
|
||||
},
|
||||
}
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} {
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
|
||||
m := features.GetEnabled(context.Background())
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
@@ -676,7 +644,7 @@ func TestIntegration_DashboardNestedPermissionFilter_WithActionSets(t *testing.T
|
||||
Scope: "folders:uid:unrelated"})
|
||||
usr := &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByActionContext(context.Background(), tc.signedInUserPermissions)}}
|
||||
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
|
||||
for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
|
||||
m := features.GetEnabled(context.Background())
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
@@ -711,56 +679,65 @@ func TestIntegration_DashboardNestedPermissionFilter_WithActionSets(t *testing.T
|
||||
func setupTest(t *testing.T, numFolders, numDashboards int, permissions []accesscontrol.Permission) db.DB {
|
||||
t.Helper()
|
||||
|
||||
store := db.InitTestDB(t)
|
||||
err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
dashes := make([]dashboards.Dashboard, 0, numFolders+numDashboards)
|
||||
for i := 1; i <= numFolders; i++ {
|
||||
str := strconv.Itoa(i)
|
||||
dashes = append(dashes, dashboards.Dashboard{
|
||||
OrgID: 1,
|
||||
Slug: str,
|
||||
UID: str,
|
||||
Title: str,
|
||||
IsFolder: true,
|
||||
Data: simplejson.New(),
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
}
|
||||
// Seed dashboards
|
||||
for i := numFolders + 1; i <= numFolders+numDashboards; i++ {
|
||||
str := strconv.Itoa(i)
|
||||
folderID := 0
|
||||
if i%(numFolders+1) != 0 {
|
||||
folderID = i % (numFolders + 1)
|
||||
}
|
||||
dashes = append(dashes, dashboards.Dashboard{
|
||||
OrgID: 1,
|
||||
IsFolder: false,
|
||||
FolderUID: strconv.Itoa(folderID),
|
||||
UID: str,
|
||||
Slug: str,
|
||||
Title: str,
|
||||
Data: simplejson.New(),
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
db, cfg := db.InitTestDBWithCfg(t)
|
||||
dashStore, err := database.ProvideDashboardStore(db, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db))
|
||||
require.NoError(t, err)
|
||||
fStore := folderimpl.ProvideStore(db)
|
||||
|
||||
// Create a signed-in user for folder creation
|
||||
usr := &user.SignedInUser{
|
||||
UserID: 1,
|
||||
OrgID: 1,
|
||||
Login: "test",
|
||||
}
|
||||
|
||||
// folders need to be created in both the folder and dashboard table
|
||||
for i := 1; i <= numFolders; i++ {
|
||||
str := strconv.Itoa(i)
|
||||
folder, err := fStore.Create(context.Background(), folder.CreateFolderCommand{
|
||||
Title: str,
|
||||
OrgID: 1,
|
||||
UID: str,
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||
OrgID: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||
"title": str,
|
||||
"uid": folder.UID,
|
||||
}),
|
||||
IsFolder: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// now create dashboards
|
||||
for i := numFolders + 1; i <= numFolders+numDashboards; i++ {
|
||||
str := strconv.Itoa(i)
|
||||
folderID := 0
|
||||
if i%(numFolders+1) != 0 {
|
||||
folderID = i % (numFolders + 1)
|
||||
}
|
||||
|
||||
// Insert dashboards in batches
|
||||
batchSize := 500
|
||||
for i := 0; i < len(dashes); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(dashes) {
|
||||
end = len(dashes)
|
||||
}
|
||||
|
||||
_, err := sess.InsertMulti(dashes[i:end])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := dashboards.SaveDashboardCommand{
|
||||
OrgID: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||
"title": str,
|
||||
"uid": str,
|
||||
}),
|
||||
IsFolder: false,
|
||||
}
|
||||
if folderID != 0 {
|
||||
cmd.FolderUID = strconv.Itoa(folderID)
|
||||
}
|
||||
|
||||
_, err := dashStore.SaveDashboard(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// insert permissions
|
||||
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
role := &accesscontrol.Role{
|
||||
OrgID: 0,
|
||||
UID: "basic_viewer",
|
||||
@@ -792,6 +769,7 @@ func setupTest(t *testing.T, numFolders, numDashboards int, permissions []access
|
||||
}
|
||||
|
||||
if len(permissions) > 0 {
|
||||
batchSize := 500
|
||||
for i := 0; i < len(permissions); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(permissions) {
|
||||
@@ -808,7 +786,7 @@ func setupTest(t *testing.T, numFolders, numDashboards int, permissions []access
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return store
|
||||
return db
|
||||
}
|
||||
|
||||
func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol.Permission, orgID int64, features featuremgmt.FeatureToggles) db.DB {
|
||||
|
||||
@@ -44,11 +44,6 @@ func benchmarkDashboardPermissionFilter(b *testing.B, numUsers, numDashboards, n
|
||||
}}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
// if nestingLevel > 0 enable nested folders
|
||||
if nestingLevel > 0 {
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
}
|
||||
|
||||
store := setupBenchMark(b, usr, features, numUsers, numDashboards, numFolders, nestingLevel)
|
||||
|
||||
recursiveQueriesAreSupported, err := store.RecursiveQueriesAreSupported()
|
||||
|
||||
@@ -37,14 +37,9 @@ func (b *Builder) ToSQL(limit, page int64) (string, []any) {
|
||||
INNER JOIN dashboard ON ids.id = dashboard.id`)
|
||||
b.sql.WriteString("\n")
|
||||
|
||||
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
// covered by UQE_folder_org_id_uid
|
||||
b.sql.WriteString(
|
||||
`LEFT OUTER JOIN folder ON folder.uid = dashboard.folder_uid AND folder.org_id = dashboard.org_id`)
|
||||
} else {
|
||||
b.sql.WriteString(`
|
||||
LEFT OUTER JOIN dashboard AS folder ON folder.id = dashboard.folder_id`)
|
||||
}
|
||||
// covered by UQE_folder_org_id_uid
|
||||
b.sql.WriteString(
|
||||
`LEFT OUTER JOIN folder ON folder.uid = dashboard.folder_uid AND folder.org_id = dashboard.org_id`)
|
||||
b.sql.WriteString(`
|
||||
LEFT OUTER JOIN dashboard_tag ON dashboard.id = dashboard_tag.dashboard_id`)
|
||||
b.sql.WriteString("\n")
|
||||
@@ -69,17 +64,9 @@ func (b *Builder) buildSelect() {
|
||||
dashboard.folder_id,
|
||||
dashboard.deleted,
|
||||
folder.uid AS folder_uid,
|
||||
folder.title AS folder_slug,
|
||||
folder.title AS folder_title
|
||||
`)
|
||||
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
b.sql.WriteString(`
|
||||
folder.title AS folder_slug,`)
|
||||
} else {
|
||||
b.sql.WriteString(`
|
||||
folder.slug AS folder_slug,`)
|
||||
}
|
||||
b.sql.WriteString(`
|
||||
folder.title AS folder_title `)
|
||||
|
||||
for _, f := range b.Filters {
|
||||
if f, ok := f.(model.FilterSelect); ok {
|
||||
b.sql.WriteString(fmt.Sprintf(", %s", f.Select()))
|
||||
|
||||
@@ -66,10 +66,9 @@ func (f FolderFilter) Where() (string, []any) {
|
||||
}
|
||||
|
||||
type FolderUIDFilter struct {
|
||||
Dialect migrator.Dialect
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
NestedFoldersEnabled bool
|
||||
Dialect migrator.Dialect
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
}
|
||||
|
||||
func (f FolderUIDFilter) Where() (string, []any) {
|
||||
@@ -92,35 +91,19 @@ func (f FolderUIDFilter) Where() (string, []any) {
|
||||
case len(params) < 1:
|
||||
// do nothing
|
||||
case len(params) == 1:
|
||||
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)"
|
||||
if f.NestedFoldersEnabled {
|
||||
q = "dashboard.org_id = ? AND dashboard.folder_uid = ?"
|
||||
}
|
||||
q = "dashboard.org_id = ? AND dashboard.folder_uid = ?"
|
||||
params = append([]any{f.OrgID}, params...)
|
||||
default:
|
||||
sqlArray := "(?" + strings.Repeat(",?", len(params)-1) + ")"
|
||||
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN " + sqlArray + ")"
|
||||
if f.NestedFoldersEnabled {
|
||||
q = "dashboard.org_id = ? AND dashboard.folder_uid IN " + sqlArray
|
||||
}
|
||||
q = "dashboard.org_id = ? AND dashboard.folder_uid IN " + sqlArray
|
||||
params = append([]any{f.OrgID}, params...)
|
||||
}
|
||||
|
||||
if includeGeneral {
|
||||
if q == "" {
|
||||
if f.NestedFoldersEnabled {
|
||||
q = "dashboard.folder_uid IS NULL "
|
||||
} else {
|
||||
q = "dashboard.folder_id = ? "
|
||||
params = append(params, 0)
|
||||
}
|
||||
q = "dashboard.folder_uid IS NULL "
|
||||
} else {
|
||||
if f.NestedFoldersEnabled {
|
||||
q = "(" + q + " OR dashboard.folder_uid IS NULL)"
|
||||
} else {
|
||||
q = "(" + q + " OR dashboard.folder_id = ?)"
|
||||
params = append(params, 0)
|
||||
}
|
||||
q = "(" + q + " OR dashboard.folder_uid IS NULL)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,67 +12,34 @@ func TestIntegrationFolderUIDFilter(t *testing.T) {
|
||||
t.Skip("skipping integration test in short mode")
|
||||
}
|
||||
testCases := []struct {
|
||||
description string
|
||||
uids []string
|
||||
expectedSql string
|
||||
expectedParams []any
|
||||
nestedFoldersEnabled bool
|
||||
description string
|
||||
uids []string
|
||||
expectedSql string
|
||||
expectedParams []any
|
||||
}{
|
||||
{
|
||||
description: "searching general folder",
|
||||
uids: []string{"general"},
|
||||
expectedSql: "dashboard.folder_id = ? ",
|
||||
expectedParams: []any{0},
|
||||
nestedFoldersEnabled: false,
|
||||
description: "searching general folder",
|
||||
uids: []string{"general"},
|
||||
expectedSql: "dashboard.folder_uid IS NULL ",
|
||||
expectedParams: []any{},
|
||||
},
|
||||
{
|
||||
description: "searching a specific folder",
|
||||
uids: []string{"abc-123"},
|
||||
expectedSql: "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)",
|
||||
expectedParams: []any{int64(1), "abc-123"},
|
||||
nestedFoldersEnabled: false,
|
||||
description: "searching a specific folder",
|
||||
uids: []string{"abc-123"},
|
||||
expectedSql: "dashboard.org_id = ? AND dashboard.folder_uid = ?",
|
||||
expectedParams: []any{int64(1), "abc-123"},
|
||||
},
|
||||
{
|
||||
description: "searching a specific folders",
|
||||
uids: []string{"abc-123", "def-456"},
|
||||
expectedSql: "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN (?,?))",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456"},
|
||||
nestedFoldersEnabled: false,
|
||||
description: "searching a specific folders",
|
||||
uids: []string{"abc-123", "def-456"},
|
||||
expectedSql: "dashboard.org_id = ? AND dashboard.folder_uid IN (?,?)",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456"},
|
||||
},
|
||||
{
|
||||
description: "searching a specific folders or general",
|
||||
uids: []string{"general", "abc-123", "def-456"},
|
||||
expectedSql: "(dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN (?,?)) OR dashboard.folder_id = ?)",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456", 0},
|
||||
nestedFoldersEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "searching general folder with nestedFoldersEnabled",
|
||||
uids: []string{"general"},
|
||||
expectedSql: "dashboard.folder_uid IS NULL ",
|
||||
expectedParams: []any{},
|
||||
nestedFoldersEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "searching a specific folder with nestedFoldersEnabled",
|
||||
uids: []string{"abc-123"},
|
||||
expectedSql: "dashboard.org_id = ? AND dashboard.folder_uid = ?",
|
||||
expectedParams: []any{int64(1), "abc-123"},
|
||||
nestedFoldersEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "searching a specific folders with nestedFoldersEnabled",
|
||||
uids: []string{"abc-123", "def-456"},
|
||||
expectedSql: "dashboard.org_id = ? AND dashboard.folder_uid IN (?,?)",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456"},
|
||||
nestedFoldersEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "searching a specific folders or general with nestedFoldersEnabled",
|
||||
uids: []string{"general", "abc-123", "def-456"},
|
||||
expectedSql: "(dashboard.org_id = ? AND dashboard.folder_uid IN (?,?) OR dashboard.folder_uid IS NULL)",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456"},
|
||||
nestedFoldersEnabled: true,
|
||||
description: "searching a specific folders or general",
|
||||
uids: []string{"general", "abc-123", "def-456"},
|
||||
expectedSql: "(dashboard.org_id = ? AND dashboard.folder_uid IN (?,?) OR dashboard.folder_uid IS NULL)",
|
||||
expectedParams: []any{int64(1), "abc-123", "def-456"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -81,10 +48,9 @@ func TestIntegrationFolderUIDFilter(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
f := searchstore.FolderUIDFilter{
|
||||
Dialect: store.GetDialect(),
|
||||
OrgID: 1,
|
||||
UIDs: tc.uids,
|
||||
NestedFoldersEnabled: tc.nestedFoldersEnabled,
|
||||
Dialect: store.GetDialect(),
|
||||
OrgID: 1,
|
||||
UIDs: tc.uids,
|
||||
}
|
||||
|
||||
sql, params := f.Where()
|
||||
|
||||
@@ -146,48 +146,6 @@ func TestIntegrationBuilder_RBAC(t *testing.T) {
|
||||
int64(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "user with view permission",
|
||||
userPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"},
|
||||
},
|
||||
level: dashboardaccess.PERMISSION_VIEW,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
expectedParams: []any{
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:read",
|
||||
"dashboards:view",
|
||||
"dashboards:edit",
|
||||
"dashboards:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:read",
|
||||
"folders:view",
|
||||
"folders:edit",
|
||||
"folders:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"folders:read",
|
||||
"folders:view",
|
||||
"folders:edit",
|
||||
"folders:admin",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "user with write permission",
|
||||
userPermissions: []accesscontrol.Permission{
|
||||
@@ -196,16 +154,6 @@ func TestIntegrationBuilder_RBAC(t *testing.T) {
|
||||
level: dashboardaccess.PERMISSION_EDIT,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
expectedParams: []any{
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:write",
|
||||
"dashboards:edit",
|
||||
"dashboards:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
@@ -218,6 +166,7 @@ func TestIntegrationBuilder_RBAC(t *testing.T) {
|
||||
"folders:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
@@ -225,15 +174,26 @@ func TestIntegrationBuilder_RBAC(t *testing.T) {
|
||||
"dashboards:create",
|
||||
"folders:edit",
|
||||
"folders:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:write",
|
||||
"dashboards:edit",
|
||||
"dashboards:admin",
|
||||
int64(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "user with view permission with nesting",
|
||||
desc: "user with view permission",
|
||||
userPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"},
|
||||
},
|
||||
level: dashboardaccess.PERMISSION_VIEW,
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
expectedParams: []any{
|
||||
int64(1),
|
||||
int64(1),
|
||||
@@ -272,53 +232,12 @@ func TestIntegrationBuilder_RBAC(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "user with view permission with remove subquery",
|
||||
userPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:1"},
|
||||
},
|
||||
level: dashboardaccess.PERMISSION_VIEW,
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery),
|
||||
expectedParams: []any{
|
||||
int64(1),
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:read",
|
||||
"dashboards:view",
|
||||
"dashboards:edit",
|
||||
"dashboards:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"dashboards:read",
|
||||
"folders:view",
|
||||
"folders:edit",
|
||||
"folders:admin",
|
||||
int64(1),
|
||||
int64(1),
|
||||
0,
|
||||
"Viewer",
|
||||
int64(1),
|
||||
0,
|
||||
"folders:read",
|
||||
"folders:view",
|
||||
"folders:edit",
|
||||
"folders:admin",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "user with edit permission with nesting and remove subquery",
|
||||
desc: "user with edit permission remove subquery",
|
||||
userPermissions: []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:1"},
|
||||
},
|
||||
level: dashboardaccess.PERMISSION_EDIT,
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPermissionsFilterRemoveSubquery),
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery),
|
||||
expectedParams: []any{
|
||||
int64(1),
|
||||
int64(1),
|
||||
|
||||
Reference in New Issue
Block a user