mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 20:54:22 -06:00
1c3ad78672
Fix slow ACL query for dashboards that was used as subquery on multiple places slowing down search and login in instances with many dashboards.
344 lines
8.4 KiB
Go
344 lines
8.4 KiB
Go
package sqlstore
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestSqlBuilder(t *testing.T) {
|
|
t.Run("writeDashboardPermissionFilter", func(t *testing.T) {
|
|
t.Run("user ACL", func(t *testing.T) {
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{User: true, Permission: models.PERMISSION_VIEW},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{User: true, Permission: models.PERMISSION_VIEW},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldNotFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{User: true, Permission: models.PERMISSION_EDIT},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{User: true, Permission: models.PERMISSION_VIEW},
|
|
Search{RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldNotFind,
|
|
)
|
|
})
|
|
|
|
t.Run("role ACL", func(t *testing.T) {
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Role: models.ROLE_VIEWER, Permission: models.PERMISSION_VIEW},
|
|
Search{UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Role: models.ROLE_VIEWER, Permission: models.PERMISSION_VIEW},
|
|
Search{UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldNotFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Role: models.ROLE_EDITOR, Permission: models.PERMISSION_VIEW},
|
|
Search{UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldNotFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Role: models.ROLE_EDITOR, Permission: models.PERMISSION_VIEW},
|
|
Search{UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldNotFind,
|
|
)
|
|
})
|
|
|
|
t.Run("team ACL", func(t *testing.T) {
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Team: true, Permission: models.PERMISSION_VIEW},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Team: true, Permission: models.PERMISSION_VIEW},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldNotFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Team: true, Permission: models.PERMISSION_EDIT},
|
|
Search{UserFromACL: true, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{},
|
|
&DashboardPermission{Team: true, Permission: models.PERMISSION_EDIT},
|
|
Search{UserFromACL: false, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldNotFind,
|
|
)
|
|
})
|
|
|
|
t.Run("defaults for user ACL", func(t *testing.T) {
|
|
test(t,
|
|
DashboardProps{},
|
|
nil,
|
|
Search{OrgId: -1, UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldNotFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{OrgId: -1},
|
|
nil,
|
|
Search{OrgId: -1, UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_VIEW},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{OrgId: -1},
|
|
nil,
|
|
Search{OrgId: -1, UsersOrgRole: models.ROLE_EDITOR, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldFind,
|
|
)
|
|
|
|
test(t,
|
|
DashboardProps{OrgId: -1},
|
|
nil,
|
|
Search{OrgId: -1, UsersOrgRole: models.ROLE_VIEWER, RequiredPermission: models.PERMISSION_EDIT},
|
|
shouldNotFind,
|
|
)
|
|
})
|
|
})
|
|
}
|
|
|
|
var shouldFind = true
|
|
var shouldNotFind = false
|
|
|
|
type DashboardProps struct {
|
|
OrgId int64
|
|
}
|
|
|
|
type DashboardPermission struct {
|
|
User bool
|
|
Team bool
|
|
Role models.RoleType
|
|
Permission models.PermissionType
|
|
}
|
|
|
|
type Search struct {
|
|
UsersOrgRole models.RoleType
|
|
UserFromACL bool
|
|
RequiredPermission models.PermissionType
|
|
OrgId int64
|
|
}
|
|
|
|
type dashboardResponse struct {
|
|
Id int64
|
|
}
|
|
|
|
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
|
|
// Will also cleanup the db
|
|
sqlStore := InitTestDB(t)
|
|
|
|
dashboard, err := createDummyDashboard(dashboardProps)
|
|
if !assert.Equal(t, nil, err) {
|
|
return
|
|
}
|
|
|
|
var aclUserId int64
|
|
if dashboardPermission != nil {
|
|
aclUserId, err = createDummyAcl(dashboardPermission, search, dashboard.Id)
|
|
if !assert.Equal(t, nil, err) {
|
|
return
|
|
}
|
|
}
|
|
dashboards, err := getDashboards(sqlStore, search, aclUserId)
|
|
if !assert.Equal(t, nil, err) {
|
|
return
|
|
}
|
|
|
|
if shouldFind {
|
|
if assert.Equal(t, 1, len(dashboards), "Should return one dashboard") {
|
|
assert.Equal(t, dashboards[0].Id, dashboard.Id, "Should return created dashboard")
|
|
}
|
|
} else {
|
|
assert.Equal(t, 0, len(dashboards), "Should node return any dashboard")
|
|
}
|
|
}
|
|
|
|
func createDummyUser() (*models.User, error) {
|
|
uid := rand.Intn(9999999)
|
|
createUserCmd := &models.CreateUserCommand{
|
|
Email: string(uid) + "@example.com",
|
|
Login: string(uid),
|
|
Name: string(uid),
|
|
Company: "",
|
|
OrgName: "",
|
|
Password: string(uid),
|
|
EmailVerified: true,
|
|
IsAdmin: false,
|
|
SkipOrgSetup: false,
|
|
DefaultOrgRole: string(models.ROLE_VIEWER),
|
|
}
|
|
err := CreateUser(context.Background(), createUserCmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &createUserCmd.Result, nil
|
|
}
|
|
|
|
func createDummyTeam() (*models.Team, error) {
|
|
cmd := &models.CreateTeamCommand{
|
|
// Does not matter in this tests actually
|
|
OrgId: 1,
|
|
Name: "test",
|
|
Email: "test@example.com",
|
|
}
|
|
err := CreateTeam(cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cmd.Result, nil
|
|
}
|
|
|
|
func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, error) {
|
|
json, _ := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
|
|
|
|
saveDashboardCmd := &models.SaveDashboardCommand{
|
|
Dashboard: json,
|
|
UserId: 0,
|
|
Overwrite: false,
|
|
Message: "",
|
|
RestoredFrom: 0,
|
|
PluginId: "",
|
|
FolderId: 0,
|
|
IsFolder: false,
|
|
UpdatedAt: time.Time{},
|
|
}
|
|
if dashboardProps.OrgId != 0 {
|
|
saveDashboardCmd.OrgId = dashboardProps.OrgId
|
|
} else {
|
|
saveDashboardCmd.OrgId = 1
|
|
}
|
|
|
|
err := SaveDashboard(saveDashboardCmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return saveDashboardCmd.Result, nil
|
|
}
|
|
|
|
func createDummyAcl(dashboardPermission *DashboardPermission, search Search, dashboardId int64) (int64, error) {
|
|
acl := &models.DashboardAcl{
|
|
OrgId: 1,
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
Permission: dashboardPermission.Permission,
|
|
DashboardId: dashboardId,
|
|
}
|
|
|
|
var user *models.User
|
|
var err error
|
|
if dashboardPermission.User {
|
|
user, err = createDummyUser()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
acl.UserId = user.Id
|
|
}
|
|
|
|
if dashboardPermission.Team {
|
|
team, err := createDummyTeam()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if search.UserFromACL {
|
|
user, err = createDummyUser()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
addTeamMemberCmd := &models.AddTeamMemberCommand{
|
|
UserId: user.Id,
|
|
OrgId: 1,
|
|
TeamId: team.Id,
|
|
}
|
|
err = AddTeamMember(addTeamMemberCmd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
acl.TeamId = team.Id
|
|
}
|
|
|
|
if len(string(dashboardPermission.Role)) > 0 {
|
|
acl.Role = &dashboardPermission.Role
|
|
}
|
|
|
|
updateAclCmd := &models.UpdateDashboardAclCommand{
|
|
DashboardId: dashboardId,
|
|
Items: []*models.DashboardAcl{acl},
|
|
}
|
|
err = UpdateDashboardAcl(updateAclCmd)
|
|
if user != nil {
|
|
return user.Id, err
|
|
}
|
|
return 0, err
|
|
}
|
|
|
|
func getDashboards(sqlStore *SqlStore, search Search, aclUserId int64) ([]*dashboardResponse, error) {
|
|
builder := &SqlBuilder{}
|
|
signedInUser := &models.SignedInUser{
|
|
UserId: 9999999999,
|
|
}
|
|
|
|
if search.OrgId == 0 {
|
|
signedInUser.OrgId = 1
|
|
} else {
|
|
signedInUser.OrgId = search.OrgId
|
|
}
|
|
|
|
if len(string(search.UsersOrgRole)) > 0 {
|
|
signedInUser.OrgRole = search.UsersOrgRole
|
|
} else {
|
|
signedInUser.OrgRole = models.ROLE_VIEWER
|
|
}
|
|
if search.UserFromACL {
|
|
signedInUser.UserId = aclUserId
|
|
}
|
|
|
|
var res []*dashboardResponse
|
|
builder.Write("SELECT * FROM dashboard WHERE true")
|
|
builder.writeDashboardPermissionFilter(signedInUser, search.RequiredPermission)
|
|
err := sqlStore.engine.SQL(builder.GetSqlString(), builder.params...).Find(&res)
|
|
return res, err
|
|
}
|