mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 17:02:20 +08:00

* replace xorm.io/xorm imports * replace xorm from other go.mod files * clean up workspace * nolint does not make sense anymore as it is not a module * try if nolint directive helps * use nolint:all for xorm * add more nolints * try to skip xorm in linter config * exclude xorm differently * retrigger ci
206 lines
6.8 KiB
Go
206 lines
6.8 KiB
Go
package accesscontrol
|
|
|
|
import (
|
|
"github.com/grafana/grafana/pkg/util/xorm"
|
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
)
|
|
|
|
const PreventSeedingOnCallAccessID = "prevent seeding OnCall access"
|
|
|
|
const migSQLITERoleNameNullable = `ALTER TABLE seed_assignment ADD COLUMN tmp_role_name VARCHAR(190) DEFAULT NULL;
|
|
UPDATE seed_assignment SET tmp_role_name = role_name;
|
|
ALTER TABLE seed_assignment DROP COLUMN role_name;
|
|
ALTER TABLE seed_assignment RENAME COLUMN tmp_role_name TO role_name;`
|
|
|
|
func AddSeedAssignmentMigrations(mg *migrator.Migrator) {
|
|
seedAssignmentTable := migrator.Table{Name: "seed_assignment"}
|
|
|
|
mg.AddMigration("add action column to seed_assignment",
|
|
migrator.NewAddColumnMigration(seedAssignmentTable,
|
|
&migrator.Column{Name: "action", Type: migrator.DB_Varchar, Length: 190, Nullable: true}))
|
|
|
|
mg.AddMigration("add scope column to seed_assignment",
|
|
migrator.NewAddColumnMigration(seedAssignmentTable,
|
|
&migrator.Column{Name: "scope", Type: migrator.DB_Varchar, Length: 190, Nullable: true}))
|
|
|
|
mg.AddMigration("remove unique index builtin_role_role_name before nullable update",
|
|
migrator.NewDropIndexMigration(seedAssignmentTable,
|
|
&migrator.Index{Cols: []string{"builtin_role", "role_name"}, Type: migrator.UniqueIndex}))
|
|
|
|
mg.AddMigration("update seed_assignment role_name column to nullable",
|
|
migrator.NewRawSQLMigration("").
|
|
SQLite(migSQLITERoleNameNullable).
|
|
Postgres("ALTER TABLE `seed_assignment` ALTER COLUMN role_name DROP NOT NULL;").
|
|
Mysql("ALTER TABLE seed_assignment MODIFY role_name VARCHAR(190) DEFAULT NULL;"))
|
|
|
|
mg.AddMigration("add unique index builtin_role_name back",
|
|
migrator.NewAddIndexMigration(seedAssignmentTable,
|
|
&migrator.Index{Cols: []string{"builtin_role", "role_name"}, Type: migrator.UniqueIndex}))
|
|
|
|
mg.AddMigration("add unique index builtin_role_action_scope",
|
|
migrator.NewAddIndexMigration(seedAssignmentTable,
|
|
&migrator.Index{Cols: []string{"builtin_role", "action", "scope"}, Type: migrator.UniqueIndex}))
|
|
|
|
mg.AddMigration("add primary key to seed_assigment", &seedAssignmentPrimaryKeyMigrator{})
|
|
|
|
mg.AddMigration("add origin column to seed_assignment",
|
|
migrator.NewAddColumnMigration(seedAssignmentTable,
|
|
&migrator.Column{Name: "origin", Type: migrator.DB_Varchar, Length: 190, Nullable: true}))
|
|
|
|
mg.AddMigration("add origin to plugin seed_assignment", &seedAssignmentOnCallMigrator{})
|
|
mg.AddMigration(PreventSeedingOnCallAccessID, &SeedAssignmentOnCallAccessMigrator{})
|
|
}
|
|
|
|
type seedAssignmentPrimaryKeyMigrator struct {
|
|
migrator.MigrationBase
|
|
}
|
|
|
|
func (m *seedAssignmentPrimaryKeyMigrator) SQL(dialect migrator.Dialect) string {
|
|
return CodeMigrationSQL
|
|
}
|
|
|
|
func (m *seedAssignmentPrimaryKeyMigrator) Exec(sess *xorm.Session, mig *migrator.Migrator) error {
|
|
driver := mig.Dialect.DriverName()
|
|
|
|
switch driver {
|
|
case migrator.MySQL:
|
|
_, err := sess.Exec("ALTER TABLE seed_assignment ADD id INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (id)")
|
|
return err
|
|
case migrator.Postgres:
|
|
_, err := sess.Exec("ALTER TABLE seed_assignment ADD COLUMN id SERIAL PRIMARY KEY")
|
|
return err
|
|
}
|
|
|
|
// sqlite does not allow to add constraint after a table is created
|
|
// We need to create a new table with desired columns, move data to new table, delete old table and rename new table to old
|
|
|
|
// create temp table
|
|
_, err := sess.Exec(`
|
|
CREATE TABLE seed_assignment_temp (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
builtin_role TEXT,
|
|
action TEXT,
|
|
scope TEXT,
|
|
role_name TEXT
|
|
);
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// copy data to temp table
|
|
_, err = sess.Exec("INSERT INTO seed_assignment_temp (builtin_role, action, scope, role_name) SELECT * FROM seed_assignment;")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// drop indices on old table
|
|
_, err = sess.Exec("DROP INDEX UQE_seed_assignment_builtin_role_action_scope;")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = sess.Exec("DROP INDEX UQE_seed_assignment_builtin_role_role_name;")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// drop old table
|
|
_, err = sess.Exec("DROP TABLE seed_assignment;")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// rename temp table to old name
|
|
_, err = sess.Exec("ALTER TABLE seed_assignment_temp RENAME TO seed_assignment;")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// recreate indexes on new table
|
|
_, err = sess.Exec("CREATE UNIQUE INDEX UQE_seed_assignment_builtin_role_action_scope ON seed_assignment (builtin_role, action, scope);")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = sess.Exec("CREATE UNIQUE INDEX UQE_seed_assignment_builtin_role_role_name ON seed_assignment (builtin_role, role_name);")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type seedAssignmentOnCallMigrator struct {
|
|
migrator.MigrationBase
|
|
}
|
|
|
|
func (m *seedAssignmentOnCallMigrator) SQL(dialect migrator.Dialect) string {
|
|
return CodeMigrationSQL
|
|
}
|
|
|
|
func (m *seedAssignmentOnCallMigrator) Exec(sess *xorm.Session, mig *migrator.Migrator) error {
|
|
_, err := sess.Exec(
|
|
`UPDATE seed_assignment SET origin = ? WHERE action LIKE ? OR scope = ?`,
|
|
"grafana-oncall-app",
|
|
"grafana-oncall-app%",
|
|
"plugins:id:grafana-oncall-app",
|
|
)
|
|
return err
|
|
}
|
|
|
|
type SeedAssignmentOnCallAccessMigrator struct {
|
|
migrator.MigrationBase
|
|
}
|
|
|
|
func (m *SeedAssignmentOnCallAccessMigrator) SQL(dialect migrator.Dialect) string {
|
|
return CodeMigrationSQL
|
|
}
|
|
|
|
func (m *SeedAssignmentOnCallAccessMigrator) Exec(sess *xorm.Session, mig *migrator.Migrator) error {
|
|
// Check if the migration is necessary
|
|
hasEntry := 0
|
|
if _, err := sess.SQL(`SELECT 1 FROM seed_assignment LIMIT 1`).Get(&hasEntry); err != nil {
|
|
return err
|
|
}
|
|
if hasEntry == 0 {
|
|
// Skip migration the seed assignment table has not been populated
|
|
// Hence the oncall access permission can be granted without any risk
|
|
return nil
|
|
}
|
|
|
|
// Check if the permission has not already been seeded
|
|
// This is the case for instances that activated the accessControlOnCall feature already.
|
|
type SeedAssignment struct {
|
|
BuiltinRole, Action, Scope, Origin string
|
|
}
|
|
assigns := []SeedAssignment{}
|
|
err := sess.SQL(`SELECT builtin_role, action, scope, origin FROM seed_assignment WHERE action = ? AND scope = ?`,
|
|
"plugins.app:access", "plugins:id:grafana-oncall-app").
|
|
Find(&assigns)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
basicRoles := map[string]bool{"Viewer": true, "Editor": true, "Admin": true, "Grafana Admin": true}
|
|
for i := range assigns {
|
|
delete(basicRoles, assigns[i].BuiltinRole)
|
|
}
|
|
if len(basicRoles) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// By default, basic roles have access to all app plugins; no need for extra permission.
|
|
// Mark OnCall Access permission as already seeded to prevent it from being added to basic roles.
|
|
toSeed := []SeedAssignment{}
|
|
for br := range basicRoles {
|
|
toSeed = append(toSeed, SeedAssignment{
|
|
BuiltinRole: br,
|
|
Action: "plugins.app:access",
|
|
Scope: "plugins:id:grafana-oncall-app",
|
|
Origin: "grafana-oncall-app",
|
|
})
|
|
}
|
|
_, err = sess.Table("seed_assignment").InsertMulti(&toSeed)
|
|
return err
|
|
}
|