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/orphaned.go

102 lines
2.7 KiB

package accesscontrol
import (
"fmt"
"strconv"
"strings"
"xorm.io/xorm"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
const (
orphanedServiceAccountsPermissions = "delete orphaned service account permissions"
)
func AddOrphanedMigrations(mg *migrator.Migrator) {
mg.AddMigration(orphanedServiceAccountsPermissions, &orphanedServiceAccountPermissions{})
}
var _ migrator.CodeMigration = new(alertingScopeRemovalMigrator)
type orphanedServiceAccountPermissions struct {
migrator.MigrationBase
}
func (m *orphanedServiceAccountPermissions) SQL(dialect migrator.Dialect) string {
return CodeMigrationSQL
}
func (m *orphanedServiceAccountPermissions) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
var idents []string
// find all permissions that are scopes directly to a service account
err := sess.SQL("SELECT DISTINCT p.identifier FROM permission AS p WHERE p.kind = 'serviceaccounts' AND NOT p.identifier = '*'").Find(&idents)
if err != nil {
return fmt.Errorf("failed to fetch permissinos scoped to service accounts: %w", err)
}
ids := make([]int64, 0, len(idents))
for _, id := range idents {
id, err := strconv.ParseInt(id, 10, 64)
if err == nil {
ids = append(ids, id)
}
}
if len(ids) == 0 {
return nil
}
return batch(len(ids), batchSize, func(start, end int) error {
return m.exec(sess, mg, ids[start:end])
})
}
func (m *orphanedServiceAccountPermissions) exec(sess *xorm.Session, mg *migrator.Migrator, ids []int64) error {
// get all service accounts from batch
raw := "SELECT u.id FROM " + mg.Dialect.Quote("user") + " AS u WHERE u.is_service_account AND u.id IN(?" + strings.Repeat(",?", len(ids)-1) + ")"
args := make([]any, 0, len(ids))
for _, id := range ids {
args = append(args, id)
}
var existingIDs []int64
err := sess.SQL(raw, args...).Find(&existingIDs)
if err != nil {
return fmt.Errorf("failed to fetch existing service accounts: %w", err)
}
existing := make(map[int64]struct{}, len(existingIDs))
for _, id := range existingIDs {
existing[id] = struct{}{}
}
// filter out orphaned permissions
var orphaned []string
for _, id := range ids {
if _, ok := existing[id]; !ok {
orphaned = append(orphaned, strconv.FormatInt(id, 10))
}
}
if len(orphaned) == 0 {
return nil
}
// delete all orphaned permissions
rawDelete := "DELETE FROM permission WHERE kind = 'serviceaccounts' AND identifier IN(?" + strings.Repeat(",?", len(orphaned)-1) + ")"
deleteArgs := make([]any, 0, len(orphaned)+1)
deleteArgs = append(deleteArgs, rawDelete)
for _, id := range orphaned {
deleteArgs = append(deleteArgs, id)
}
_, err = sess.Exec(deleteArgs...)
if err != nil {
return fmt.Errorf("failed to delete orphaned service accounts: %w", err)
}
return nil
}