Search v1: Add support for inherited folder permissions if nested folders are enabled (#63275)

* Add features dependency to SQLBuilder

* Add features dependency to AccessControlDashboardPermissionFilter

* Add test for folder inheritance

* Dashboard permissions: Return recursive query

* Recursive query for inherited folders

* Modify search builder

* Adjust db.SQLBuilder

* Pass flag to SQLbuilder if CTEs are supported

* Add support for mysql < 8.0

* Add benchmarking for search with nested folders

* Set features to AlertStore

* Update pkg/infra/db/sqlbuilder.go

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>

* Set features to LibraryElementService

* SQLBuilder tests with nested folder flag set

* Apply suggestion from code review

Co-authored-by: IevaVasiljeva <ieva.vasiljeva@grafana.com>
Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
This commit is contained in:
Sofia Papagiannaki
2023-04-06 11:16:15 +03:00
committed by GitHub
co-authored by Ieva Emil Tullstedt
parent 2648fcb833
commit 988a120d6d
24 changed files with 1487 additions and 105 deletions
+162 -5
View File
@@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/dashboards"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user"
@@ -24,6 +25,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
t.Run("WriteDashboardPermissionFilter", func(t *testing.T) {
t.Run("user ACL", func(t *testing.T) {
test(t,
@@ -31,6 +33,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -38,6 +41,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -45,6 +49,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -52,6 +57,41 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(),
)
})
t.Run("user ACL with nested folders", func(t *testing.T) {
test(t,
DashboardProps{},
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{User: true, Permission: dashboards.PERMISSION_VIEW},
Search{RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
})
@@ -61,6 +101,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Role: org.RoleViewer, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -68,6 +109,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Role: org.RoleViewer, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -75,6 +117,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Role: org.RoleEditor, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -82,6 +125,41 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Role: org.RoleEditor, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(),
)
})
t.Run("role ACL with nested folders", func(t *testing.T) {
test(t,
DashboardProps{},
&DashboardPermission{Role: org.RoleViewer, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Role: org.RoleViewer, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Role: org.RoleEditor, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Role: org.RoleEditor, Permission: dashboards.PERMISSION_VIEW},
Search{UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
})
@@ -91,6 +169,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -98,6 +177,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -105,6 +185,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -112,6 +193,41 @@ func TestIntegrationSQLBuilder(t *testing.T) {
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: false, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(),
)
})
t.Run("team ACL with nested folders", func(t *testing.T) {
test(t,
DashboardProps{},
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_VIEW},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: true, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{},
&DashboardPermission{Team: true, Permission: dashboards.PERMISSION_EDIT},
Search{UserFromACL: false, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
})
@@ -121,6 +237,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -128,6 +245,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -135,6 +253,7 @@ func TestIntegrationSQLBuilder(t *testing.T) {
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleEditor, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(),
)
test(t,
@@ -142,6 +261,41 @@ func TestIntegrationSQLBuilder(t *testing.T) {
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(),
)
})
t.Run("defaults for user ACL with nested folders", func(t *testing.T) {
test(t,
DashboardProps{},
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{OrgId: -1},
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_VIEW},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{OrgId: -1},
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleEditor, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
test(t,
DashboardProps{OrgId: -1},
nil,
Search{OrgId: -1, UsersOrgRole: org.RoleViewer, RequiredPermission: dashboards.PERMISSION_EDIT},
shouldNotFind,
featuremgmt.WithFeatures(featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)),
)
})
})
@@ -172,7 +326,7 @@ type dashboardResponse struct {
Id int64
}
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool, features featuremgmt.FeatureToggles) {
t.Helper()
t.Run("", func(t *testing.T) {
@@ -186,7 +340,7 @@ func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *Dash
aclUserID = createDummyACL(t, sqlStore, dashboardPermission, search, dashboard.ID)
t.Logf("Created ACL with user ID %d\n", aclUserID)
}
dashboards := getDashboards(t, sqlStore, search, aclUserID)
dashboards := getDashboards(t, sqlStore, search, aclUserID, features)
if shouldFind {
require.Len(t, dashboards, 1, "Should return one dashboard")
@@ -292,7 +446,7 @@ func createDummyACL(t *testing.T, sqlStore *sqlstore.SQLStore, dashboardPermissi
return 0
}
func getDashboards(t *testing.T, sqlStore *sqlstore.SQLStore, search Search, aclUserID int64) []*dashboardResponse {
func getDashboards(t *testing.T, sqlStore *sqlstore.SQLStore, search Search, aclUserID int64, features featuremgmt.FeatureToggles) []*dashboardResponse {
t.Helper()
old := sqlStore.Cfg.RBACEnabled
@@ -301,7 +455,10 @@ func getDashboards(t *testing.T, sqlStore *sqlstore.SQLStore, search Search, acl
sqlStore.Cfg.RBACEnabled = old
}()
builder := NewSqlBuilder(sqlStore.Cfg, sqlStore.GetDialect())
recursiveQueriesAreSupported, err := sqlStore.RecursiveQueriesAreSupported()
require.NoError(t, err)
builder := NewSqlBuilder(sqlStore.Cfg, features, sqlStore.GetDialect(), recursiveQueriesAreSupported)
signedInUser := &user.SignedInUser{
UserID: 9999999999,
}
@@ -325,7 +482,7 @@ func getDashboards(t *testing.T, sqlStore *sqlstore.SQLStore, search Search, acl
builder.Write("SELECT * FROM dashboard WHERE true")
builder.WriteDashboardPermissionFilter(signedInUser, search.RequiredPermission)
t.Logf("Searching for dashboards, SQL: %q\n", builder.GetSQLString())
err := sqlStore.GetEngine().SQL(builder.GetSQLString(), builder.params...).Find(&res)
err = sqlStore.GetEngine().SQL(builder.GetSQLString(), builder.params...).Find(&res)
require.NoError(t, err)
return res
}