The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/sqlstore/migrations/accesscontrol/seed_assignment.go

204 lines
6.8 KiB

package accesscontrol
import (
"xorm.io/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()
if driver == migrator.MySQL {
_, err := sess.Exec("ALTER TABLE seed_assignment ADD id INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (id)")
return err
} else if driver == 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
}