mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 03:32:20 +08:00
RBAC: Fix dashboard filter in SQLBuilder (#53379)
* Reuse DasbhoardPermissionFilter * Use rbac dashboard filter if enabled
This commit is contained in:
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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(),
|
||||
),
|
||||
}
|
||||
|
@ -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...)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
Reference in New Issue
Block a user