Access control: use uid for dashboard and folder scopes (#46807)
* use uid:s for folder and dashboard permissions * evaluate folder and dashboard permissions based on uids * add dashboard.uid to accept list * Check for exact suffix * Check parent folder on create * update test * drop dashboard:create actions with dashboard scope * fix typo * AccessControl: test id 0 scope conversion * AccessControl: store only parent folder UID * AccessControl: extract general as a constant * FolderServices: Prevent creation of a folder uid'd general * FolderServices: Test folder creation prevention * Update pkg/services/guardian/accesscontrol_guardian.go * FolderServices: fix mock call expect * FolderServices: remove uneeded mocks Co-authored-by: jguer <joao.guerreiro@grafana.com>
This commit is contained in:
@@ -21,7 +21,6 @@ var dashboardPermissionTranslation = map[models.PermissionType][]string{
|
||||
models.PERMISSION_EDIT: {
|
||||
ac.ActionDashboardsRead,
|
||||
ac.ActionDashboardsWrite,
|
||||
ac.ActionDashboardsCreate,
|
||||
ac.ActionDashboardsDelete,
|
||||
},
|
||||
models.PERMISSION_ADMIN: {
|
||||
@@ -39,6 +38,7 @@ var folderPermissionTranslation = map[models.PermissionType][]string{
|
||||
dashboards.ActionFoldersRead,
|
||||
}...),
|
||||
models.PERMISSION_EDIT: append(dashboardPermissionTranslation[models.PERMISSION_EDIT], []string{
|
||||
ac.ActionDashboardsCreate,
|
||||
dashboards.ActionFoldersRead,
|
||||
dashboards.ActionFoldersWrite,
|
||||
dashboards.ActionFoldersCreate,
|
||||
@@ -56,6 +56,7 @@ var folderPermissionTranslation = map[models.PermissionType][]string{
|
||||
|
||||
func AddDashboardPermissionsMigrator(mg *migrator.Migrator) {
|
||||
mg.AddMigration("dashboard permissions", &dashboardPermissionsMigrator{})
|
||||
mg.AddMigration("dashboard permissions uid scopes", &dashboardUidPermissionMigrator{})
|
||||
}
|
||||
|
||||
var _ migrator.CodeMigration = new(dashboardPermissionsMigrator)
|
||||
@@ -219,3 +220,63 @@ func getRoleName(p models.DashboardAcl) string {
|
||||
}
|
||||
return fmt.Sprintf("managed:builtins:%s:permissions", strings.ToLower(string(*p.Role)))
|
||||
}
|
||||
|
||||
var _ migrator.CodeMigration = new(dashboardUidPermissionMigrator)
|
||||
|
||||
type dashboardUidPermissionMigrator struct {
|
||||
migrator.MigrationBase
|
||||
}
|
||||
|
||||
func (d *dashboardUidPermissionMigrator) SQL(dialect migrator.Dialect) string {
|
||||
return "code migration"
|
||||
}
|
||||
|
||||
func (d *dashboardUidPermissionMigrator) Exec(sess *xorm.Session, migrator *migrator.Migrator) error {
|
||||
if err := d.migrateWildcards(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.migrateIdScopes(sess)
|
||||
}
|
||||
|
||||
func (d *dashboardUidPermissionMigrator) migrateWildcards(sess *xorm.Session) error {
|
||||
if _, err := sess.Exec("DELETE FROM permission WHERE action = 'dashboards:create' AND scope LIKE 'dashboards%'"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := sess.Exec("UPDATE permission SET scope = 'dashboards:uid:*' WHERE scope = 'dashboards:id:*'"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := sess.Exec("UPDATE permission SET scope = 'folders:uid:*' WHERE scope = 'folders:id:*'"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dashboardUidPermissionMigrator) migrateIdScopes(sess *xorm.Session) error {
|
||||
type dashboard struct {
|
||||
ID int64 `xorm:"id"`
|
||||
UID string `xorm:"uid"`
|
||||
IsFolder bool
|
||||
}
|
||||
var dashboards []dashboard
|
||||
if err := sess.SQL("SELECT id, uid, is_folder FROM dashboard").Find(&dashboards); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, d := range dashboards {
|
||||
var idScope string
|
||||
var uidScope string
|
||||
|
||||
if d.IsFolder {
|
||||
idScope = ac.Scope("folders", "id", strconv.FormatInt(d.ID, 10))
|
||||
uidScope = ac.Scope("folders", "uid", d.UID)
|
||||
} else {
|
||||
idScope = ac.Scope("dashboards", "id", strconv.FormatInt(d.ID, 10))
|
||||
uidScope = ac.Scope("dashboards", "uid", d.UID)
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("UPDATE permission SET scope = ? WHERE scope = ?", uidScope, idScope); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -110,15 +110,16 @@ func (f AccessControlDashboardPermissionFilter) Where() (string, []interface{})
|
||||
|
||||
if len(f.dashboardActions) > 0 {
|
||||
builder.WriteString("((")
|
||||
dashFilter, _ := accesscontrol.Filter(f.User, "dashboard.id", "dashboards:id:", f.dashboardActions...)
|
||||
|
||||
dashFilter, _ := accesscontrol.Filter(f.User, "dashboard.uid", dashboards.ScopeDashboardsPrefix, f.dashboardActions...)
|
||||
builder.WriteString(dashFilter.Where)
|
||||
args = append(args, dashFilter.Args...)
|
||||
|
||||
builder.WriteString(" OR ")
|
||||
builder.WriteString(" OR dashboard.folder_id IN(SELECT id FROM dashboard WHERE ")
|
||||
dashFolderFilter, _ := accesscontrol.Filter(f.User, "dashboard.uid", dashboards.ScopeFoldersPrefix, f.dashboardActions...)
|
||||
|
||||
dashFolderFilter, _ := accesscontrol.Filter(f.User, "dashboard.folder_id", "folders:id:", f.dashboardActions...)
|
||||
builder.WriteString(dashFolderFilter.Where)
|
||||
builder.WriteString(") AND NOT dashboard.is_folder)")
|
||||
builder.WriteString(")) AND NOT dashboard.is_folder)")
|
||||
args = append(args, dashFolderFilter.Args...)
|
||||
}
|
||||
|
||||
@@ -127,12 +128,11 @@ func (f AccessControlDashboardPermissionFilter) Where() (string, []interface{})
|
||||
builder.WriteString(" OR ")
|
||||
}
|
||||
builder.WriteString("(")
|
||||
folderFilter, _ := accesscontrol.Filter(f.User, "dashboard.id", "folders:id:", f.folderActions...)
|
||||
folderFilter, _ := accesscontrol.Filter(f.User, "dashboard.uid", dashboards.ScopeFoldersPrefix, f.folderActions...)
|
||||
builder.WriteString(folderFilter.Where)
|
||||
builder.WriteString(" AND dashboard.is_folder)")
|
||||
args = append(args, folderFilter.Args...)
|
||||
}
|
||||
|
||||
builder.WriteString(")")
|
||||
return builder.String(), args
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestAccessControlDashboardPermissionFilter_Where(t *testing.T) {
|
||||
title: "folder and dashboard actions are defined",
|
||||
dashboardActions: []string{"test"},
|
||||
folderActions: []string{"test"},
|
||||
expectedResult: "((( 1 = 0 OR 1 = 0) AND NOT dashboard.is_folder) OR ( 1 = 0 AND dashboard.is_folder))",
|
||||
expectedResult: "((( 1 = 0 OR dashboard.folder_id IN(SELECT id FROM dashboard WHERE 1 = 0)) AND NOT dashboard.is_folder) OR ( 1 = 0 AND dashboard.is_folder))",
|
||||
},
|
||||
{
|
||||
title: "folder actions are defined but not dashboard actions",
|
||||
@@ -120,7 +120,7 @@ func TestAccessControlDashboardPermissionFilter_Where(t *testing.T) {
|
||||
title: "dashboard actions are defined but not folder actions",
|
||||
dashboardActions: []string{"test"},
|
||||
folderActions: nil,
|
||||
expectedResult: "((( 1 = 0 OR 1 = 0) AND NOT dashboard.is_folder))",
|
||||
expectedResult: "((( 1 = 0 OR dashboard.folder_id IN(SELECT id FROM dashboard WHERE 1 = 0)) AND NOT dashboard.is_folder))",
|
||||
},
|
||||
{
|
||||
title: "dashboard actions are defined but not folder actions",
|
||||
|
||||
Reference in New Issue
Block a user