RBAC: Fix dashboard filter in SQLBuilder (#53379)

* Reuse DasbhoardPermissionFilter

*  Use rbac dashboard filter if enabled
This commit is contained in:
Karl Persson
2022-08-10 10:32:03 +02:00
committed by GitHub
parent 6d495a6a8e
commit aa484a60c9
7 changed files with 48 additions and 69 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/setting"
)
// AlertStore is a subset of SQLStore API to satisfy the needs of the alerting service.
@ -34,15 +35,17 @@ type sqlStore struct {
db db.DB
cache *localcache.CacheService
log *log.ConcreteLogger
cfg *setting.Cfg
}
func ProvideAlertStore(
db db.DB,
cacheService *localcache.CacheService) AlertStore {
cacheService *localcache.CacheService, cfg *setting.Cfg) AlertStore {
return &sqlStore{
db: db,
cache: cacheService,
log: log.New("alerting.store"),
cfg: cfg,
}
}
@ -99,7 +102,7 @@ func deleteAlertByIdInternal(alertId int64, reason string, sess *sqlstore.DBSess
func (ss *sqlStore) HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
builder := sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(ss.cfg)
builder.Write(`SELECT
alert.id,

View File

@ -12,6 +12,7 @@ import (
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/require"
@ -46,6 +47,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
store = &sqlStore{
db: sqlstore.InitTestDB(t),
log: log.New(),
cfg: setting.NewCfg(),
}
testDash = insertTestDashboard(t, store.db, "dashboard with alerts", 1, 0, false, "alert")

View File

@ -102,7 +102,7 @@ func (d *DashboardStore) HasEditPermissionInFolders(ctx context.Context, query *
return nil
}
builder := &sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(d.sqlStore.Cfg)
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?",
query.SignedInUser.OrgId, d.dialect.BooleanStr(true))
builder.WriteDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_EDIT)
@ -130,7 +130,7 @@ func (d *DashboardStore) HasAdminPermissionInDashboardsOrFolders(ctx context.Con
return nil
}
builder := &sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(d.sqlStore.Cfg)
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ?", query.SignedInUser.OrgId)
builder.WriteDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_ADMIN)

View File

@ -223,7 +223,7 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
func getLibraryElements(c context.Context, store *sqlstore.SQLStore, signedInUser *models.SignedInUser, params []Pair) ([]LibraryElementDTO, error) {
libraryElements := make([]LibraryElementWithMeta, 0)
err := store.WithDbSession(c, func(session *sqlstore.DBSession) error {
builder := sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(store.Cfg)
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", 'General' as folder_name ")
builder.Write(", '' as folder_uid ")
@ -327,7 +327,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
return LibraryElementSearchResult{}, folderFilter.parseError
}
err := l.SQLStore.WithDbSession(c, func(session *sqlstore.DBSession) error {
builder := sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(l.Cfg)
if folderFilter.includeGeneralFolder {
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", 'General' as folder_name ")
@ -561,7 +561,7 @@ func (l *LibraryElementService) getConnections(c context.Context, signedInUser *
return err
}
var libraryElementConnections []libraryElementConnectionWithMeta
builder := sqlstore.SQLBuilder{}
builder := sqlstore.NewSqlBuilder(l.Cfg)
builder.Write("SELECT lec.*, u1.login AS created_by_name, u1.email AS created_by_email, dashboard.uid AS connection_uid")
builder.Write(" FROM " + models.LibraryElementConnectionTableName + " AS lec")
builder.Write(" LEFT JOIN " + l.SQLStore.Dialect.Quote("user") + " AS u1 ON lec.created_by = u1.id")

View File

@ -403,21 +403,21 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
sqlStore := sqlstore.InitTestDB(t)
dashboardStore := database.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures())
features := featuremgmt.WithFeatures()
cfg := setting.NewCfg()
cfg.IsFeatureToggleEnabled = features.IsEnabled
ac := acmock.New()
// TODO: Update tests to work with rbac
sqlStore.Cfg.RBACEnabled = false
folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardService := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, nil,
sqlStore.Cfg, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac,
)
guardian.InitLegacyGuardian(sqlStore, dashboardService)
service := LibraryElementService{
Cfg: cfg,
Cfg: sqlStore.Cfg,
SQLStore: sqlStore,
folderService: dashboardservice.ProvideFolderService(
cfg, dashboardService, dashboardStore, nil,
sqlStore.Cfg, dashboardService, dashboardStore, nil,
features, folderPermissions, ac, busmock.New(),
),
}

View File

@ -2,12 +2,19 @@ package sqlstore
import (
"bytes"
"strings"
"github.com/grafana/grafana/pkg/models"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/setting"
)
func NewSqlBuilder(cfg *setting.Cfg) SQLBuilder {
return SQLBuilder{cfg: cfg}
}
type SQLBuilder struct {
cfg *setting.Cfg
sql bytes.Buffer
params []interface{}
}
@ -33,61 +40,22 @@ func (sb *SQLBuilder) AddParams(params ...interface{}) {
}
func (sb *SQLBuilder) WriteDashboardPermissionFilter(user *models.SignedInUser, permission models.PermissionType) {
if user.OrgRole == models.ROLE_ADMIN {
return
var (
sql string
params []interface{}
)
if !ac.IsDisabled(sb.cfg) {
sql, params = permissions.NewAccessControlDashboardPermissionFilter(user, permission, "").Where()
} else {
sql, params = permissions.DashboardPermissionFilter{
OrgRole: user.OrgRole,
Dialect: dialect,
UserId: user.UserId,
OrgId: user.OrgId,
PermissionLevel: permission,
}.Where()
}
okRoles := []interface{}{user.OrgRole}
if user.OrgRole == models.ROLE_EDITOR {
okRoles = append(okRoles, models.ROLE_VIEWER)
}
falseStr := dialect.BooleanStr(false)
sb.sql.WriteString(` AND
(
dashboard.id IN (
SELECT distinct DashboardId from (
SELECT d.id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard_acl AS da ON
da.dashboard_id = d.id OR
da.dashboard_id = d.folder_id
WHERE
d.org_id = ? AND
da.permission >= ? AND
(
da.user_id = ? OR
da.team_id IN (SELECT team_id from team_member AS tm WHERE tm.user_id = ?) OR
da.role IN (?` + strings.Repeat(",?", len(okRoles)-1) + `)
)
UNION
SELECT d.id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard AS folder on folder.id = d.folder_id
LEFT JOIN dashboard_acl AS da ON
(
-- include default permissions -->
da.org_id = -1 AND (
(folder.id IS NOT NULL AND folder.has_acl = ` + falseStr + `) OR
(folder.id IS NULL AND d.has_acl = ` + falseStr + `)
)
)
WHERE
d.org_id = ? AND
da.permission >= ? AND
(
da.user_id = ? OR
da.role IN (?` + strings.Repeat(",?", len(okRoles)-1) + `)
)
) AS a
)
)`)
sb.params = append(sb.params, user.OrgId, permission, user.UserId, user.UserId)
sb.params = append(sb.params, okRoles...)
sb.params = append(sb.params, user.OrgId, permission, user.UserId)
sb.params = append(sb.params, okRoles...)
sb.sql.WriteString(" AND " + sql)
sb.params = append(sb.params, params...)
}

View File

@ -299,7 +299,13 @@ func createDummyACL(t *testing.T, sqlStore *SQLStore, dashboardPermission *Dashb
func getDashboards(t *testing.T, sqlStore *SQLStore, search Search, aclUserID int64) []*dashboardResponse {
t.Helper()
builder := &SQLBuilder{}
old := sqlStore.Cfg.RBACEnabled
sqlStore.Cfg.RBACEnabled = false
defer func() {
sqlStore.Cfg.RBACEnabled = old
}()
builder := NewSqlBuilder(sqlStore.Cfg)
signedInUser := &models.SignedInUser{
UserId: 9999999999,
}