mirror of https://github.com/grafana/grafana
ServiceAccounts: Remove permissions to service account when it is deleted (#93877)
* Service account: clean up permissions related to service accounts when deleted * Add migration for deleting orphaned service account permissions Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>pull/93024/head^2
parent
943525391e
commit
c7ca2bfcf5
@ -0,0 +1,96 @@ |
||||
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 |
||||
} |
||||
|
||||
// Then find all existing service accounts
|
||||
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 AS p WHERE p.kind = 'serviceaccounts' AND p.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 |
||||
} |
Loading…
Reference in new issue