mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
RBAC: Remove the option to disable RBAC and add automated permission migrations for instances that had RBAC disabled (#66652)
* RBAC: Stop reading enabeld from ini file and always set to true * Migrations: Add a migration for rbac to reset data migrations if rbac was disabled * If rbac was disabled we reset the data and data migrations that rbac has to perform to get it to a correct state * Migrator: Store migration logs on migrator and add function to clear it from the in-memory stored logs * update tests --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
parent
772d00b28f
commit
035bf29146
@ -0,0 +1,87 @@
|
|||||||
|
package accesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
disabledMigrationID = "rbac disabled migrator"
|
||||||
|
teamMigrationID = "teams permissions migration"
|
||||||
|
dashboardMigrationID = "dashboard permissions"
|
||||||
|
dashboardsUIDMigrationID = "dashboard permissions uid scopes"
|
||||||
|
datasourceMigrationID = "data source permissions"
|
||||||
|
datasourceUIDMigrationID = "data source uid permissions"
|
||||||
|
managedPermissionsMigrationID = "managed permissions migration"
|
||||||
|
alertFolderMigrationID = "managed folder permissions alert actions repeated migration"
|
||||||
|
managedPermissionsEnterpriseMigrationID = "managed permissions migration enterprise"
|
||||||
|
)
|
||||||
|
|
||||||
|
var migrations = [...]string{
|
||||||
|
teamMigrationID,
|
||||||
|
dashboardMigrationID,
|
||||||
|
dashboardsUIDMigrationID,
|
||||||
|
datasourceMigrationID,
|
||||||
|
datasourceUIDMigrationID,
|
||||||
|
managedPermissionsMigrationID,
|
||||||
|
alertFolderMigrationID,
|
||||||
|
managedPermissionsEnterpriseMigrationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDisabledMigrator(mg *migrator.Migrator) {
|
||||||
|
mg.AddMigration(disabledMigrationID, &DisabledMigrator{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisabledMigrator struct {
|
||||||
|
migrator.MigrationBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DisabledMigrator) SQL(dialect migrator.Dialect) string {
|
||||||
|
return CodeMigrationSQL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DisabledMigrator) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
|
||||||
|
enabled := mg.Cfg.Raw.Section("rbac").Key("enabled").MustBool(true)
|
||||||
|
if enabled {
|
||||||
|
// if the flag is enabled we skip the reset of data migrations
|
||||||
|
mg.Logger.Debug("skip reset of rbac data migrations")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec("DELETE FROM builtin_role WHERE role_id IN (SELECT id FROM role WHERE name LIKE 'managed:%')"); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove basic role bindings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec("DELETE FROM team_role WHERE role_id IN (SELECT id FROM role WHERE name LIKE 'managed:%')"); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove team role bindings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec("DELETE FROM user_role where role_id IN (SELECT id FROM role WHERE name LIKE 'managed:%')"); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove user role bindings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec("DELETE FROM permission WHERE role_id IN (SELECT id FROM role WHERE name LIKE 'managed:%');"); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove managed rbac permission: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec("DELETE FROM role WHERE name LIKE 'managed:%';"); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove managed rbac roles: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := []interface{}{"DELETE FROM migration_log WHERE migration_id IN (?, ?, ?, ?, ?, ?, ?, ?)"}
|
||||||
|
for _, m := range migrations {
|
||||||
|
params = append(params, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Exec(params...); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove managed permissions migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we also need to clear migration from the in-memory representation of migration log
|
||||||
|
mg.RemoveMigrationLogs(migrations[:]...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
@ -153,6 +154,7 @@ func TestMigrations(t *testing.T) {
|
|||||||
config: &setting.Cfg{
|
config: &setting.Cfg{
|
||||||
EditorsCanAdmin: true,
|
EditorsCanAdmin: true,
|
||||||
IsFeatureToggleEnabled: func(key string) bool { return key == "accesscontrol" },
|
IsFeatureToggleEnabled: func(key string) bool { return key == "accesscontrol" },
|
||||||
|
Raw: ini.Empty(),
|
||||||
},
|
},
|
||||||
expectedRolePerms: map[string][]rawPermission{
|
expectedRolePerms: map[string][]rawPermission{
|
||||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||||
@ -181,6 +183,7 @@ func TestMigrations(t *testing.T) {
|
|||||||
desc: "without editors can admin",
|
desc: "without editors can admin",
|
||||||
config: &setting.Cfg{
|
config: &setting.Cfg{
|
||||||
IsFeatureToggleEnabled: func(key string) bool { return key == "accesscontrol" },
|
IsFeatureToggleEnabled: func(key string) bool { return key == "accesscontrol" },
|
||||||
|
Raw: ini.Empty(),
|
||||||
},
|
},
|
||||||
expectedRolePerms: map[string][]rawPermission{
|
expectedRolePerms: map[string][]rawPermission{
|
||||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||||
@ -256,7 +259,10 @@ func setupTestDB(t *testing.T) *xorm.Engine {
|
|||||||
err = migrator.NewDialect(x).CleanDB()
|
err = migrator.NewDialect(x).CleanDB()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mg := migrator.NewMigrator(x, &setting.Cfg{Logger: log.New("acmigration.test")})
|
mg := migrator.NewMigrator(x, &setting.Cfg{
|
||||||
|
Logger: log.New("acmigration.test"),
|
||||||
|
Raw: ini.Empty(),
|
||||||
|
})
|
||||||
migrations := &migrations.OSSMigrations{}
|
migrations := &migrations.OSSMigrations{}
|
||||||
migrations.AddMigration(mg)
|
migrations.AddMigration(mg)
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ func (*OSSMigrations) AddMigration(mg *Migrator) {
|
|||||||
accesscontrol.AddMigration(mg)
|
accesscontrol.AddMigration(mg)
|
||||||
addQueryHistoryMigrations(mg)
|
addQueryHistoryMigrations(mg)
|
||||||
|
|
||||||
|
accesscontrol.AddDisabledMigrator(mg)
|
||||||
accesscontrol.AddTeamMembershipMigrations(mg)
|
accesscontrol.AddTeamMembershipMigrations(mg)
|
||||||
accesscontrol.AddDashboardPermissionsMigrator(mg)
|
accesscontrol.AddDashboardPermissionsMigrator(mg)
|
||||||
accesscontrol.AddAlertingPermissionsMigrator(mg)
|
accesscontrol.AddAlertingPermissionsMigrator(mg)
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/go-sql-driver/mysql"
|
"github.com/go-sql-driver/mysql"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
@ -33,7 +34,7 @@ func TestMigrations(t *testing.T) {
|
|||||||
_, err = x.SQL(query).Get(&result)
|
_, err = x.SQL(query).Get(&result)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
mg := NewMigrator(x, &setting.Cfg{})
|
mg := NewMigrator(x, &setting.Cfg{Raw: ini.Empty()})
|
||||||
migrations := &OSSMigrations{}
|
migrations := &OSSMigrations{}
|
||||||
migrations.AddMigration(mg)
|
migrations.AddMigration(mg)
|
||||||
expectedMigrations := mg.GetMigrationIDs(true)
|
expectedMigrations := mg.GetMigrationIDs(true)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/prometheus/alertmanager/pkg/labels"
|
"github.com/prometheus/alertmanager/pkg/labels"
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
@ -629,7 +630,7 @@ func setupTestDB(t *testing.T) *xorm.Engine {
|
|||||||
err = migrator.NewDialect(x).CleanDB()
|
err = migrator.NewDialect(x).CleanDB()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mg := migrator.NewMigrator(x, &setting.Cfg{})
|
mg := migrator.NewMigrator(x, &setting.Cfg{Raw: ini.Empty()})
|
||||||
migrations := &migrations.OSSMigrations{}
|
migrations := &migrations.OSSMigrations{}
|
||||||
migrations.AddMigration(mg)
|
migrations.AddMigration(mg)
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ type Migrator struct {
|
|||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
Cfg *setting.Cfg
|
Cfg *setting.Cfg
|
||||||
isLocked atomic.Bool
|
isLocked atomic.Bool
|
||||||
|
logMap map[string]MigrationLog
|
||||||
}
|
}
|
||||||
|
|
||||||
type MigrationLog struct {
|
type MigrationLog struct {
|
||||||
@ -97,9 +98,16 @@ func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
|
|||||||
logMap[logItem.MigrationID] = logItem
|
logMap[logItem.MigrationID] = logItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mg.logMap = logMap
|
||||||
return logMap, nil
|
return logMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mg *Migrator) RemoveMigrationLogs(migrationsIDs ...string) {
|
||||||
|
for _, id := range migrationsIDs {
|
||||||
|
delete(mg.logMap, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (mg *Migrator) Start(isDatabaseLockingEnabled bool, lockAttemptTimeout int) (err error) {
|
func (mg *Migrator) Start(isDatabaseLockingEnabled bool, lockAttemptTimeout int) (err error) {
|
||||||
if !isDatabaseLockingEnabled {
|
if !isDatabaseLockingEnabled {
|
||||||
return mg.run()
|
return mg.run()
|
||||||
@ -128,7 +136,7 @@ func (mg *Migrator) Start(isDatabaseLockingEnabled bool, lockAttemptTimeout int)
|
|||||||
func (mg *Migrator) run() (err error) {
|
func (mg *Migrator) run() (err error) {
|
||||||
mg.Logger.Info("Starting DB migrations")
|
mg.Logger.Info("Starting DB migrations")
|
||||||
|
|
||||||
logMap, err := mg.GetMigrationLog()
|
_, err = mg.GetMigrationLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -138,7 +146,7 @@ func (mg *Migrator) run() (err error) {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
for _, m := range mg.migrations {
|
for _, m := range mg.migrations {
|
||||||
m := m
|
m := m
|
||||||
_, exists := logMap[m.Id()]
|
_, exists := mg.logMap[m.Id()]
|
||||||
if exists {
|
if exists {
|
||||||
mg.Logger.Debug("Skipping migration: Already executed", "id", m.Id())
|
mg.Logger.Debug("Skipping migration: Already executed", "id", m.Id())
|
||||||
migrationsSkipped++
|
migrationsSkipped++
|
||||||
|
@ -1564,7 +1564,7 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
|
|||||||
|
|
||||||
func readAccessControlSettings(iniFile *ini.File, cfg *Cfg) {
|
func readAccessControlSettings(iniFile *ini.File, cfg *Cfg) {
|
||||||
rbac := iniFile.Section("rbac")
|
rbac := iniFile.Section("rbac")
|
||||||
cfg.RBACEnabled = rbac.Key("enabled").MustBool(true)
|
cfg.RBACEnabled = true
|
||||||
cfg.RBACPermissionCache = rbac.Key("permission_cache").MustBool(true)
|
cfg.RBACPermissionCache = rbac.Key("permission_cache").MustBool(true)
|
||||||
cfg.RBACPermissionValidationEnabled = rbac.Key("permission_validation_enabled").MustBool(false)
|
cfg.RBACPermissionValidationEnabled = rbac.Key("permission_validation_enabled").MustBool(false)
|
||||||
cfg.RBACResetBasicRoles = rbac.Key("reset_basic_roles").MustBool(false)
|
cfg.RBACResetBasicRoles = rbac.Key("reset_basic_roles").MustBool(false)
|
||||||
|
Loading…
Reference in New Issue
Block a user