From 44b8e232d1deeacaeb30d326021f65e32c475bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20Fern=C3=A1ndez=20Garc=C3=ADa-Minguill?= =?UTF-8?q?=C3=A1n?= Date: Tue, 17 May 2022 15:09:34 +0200 Subject: [PATCH] Allow wildcards in grants --- go/logic/inspect.go | 48 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/go/logic/inspect.go b/go/logic/inspect.go index 9f82d74..da21feb 100644 --- a/go/logic/inspect.go +++ b/go/logic/inspect.go @@ -9,6 +9,7 @@ import ( gosql "database/sql" "fmt" "reflect" + "regexp" "strings" "sync/atomic" "time" @@ -30,6 +31,7 @@ type Inspector struct { informationSchemaDb *gosql.DB migrationContext *base.MigrationContext name string + grantOnRe *regexp.Regexp } func NewInspector(migrationContext *base.MigrationContext) *Inspector { @@ -37,6 +39,7 @@ func NewInspector(migrationContext *base.MigrationContext) *Inspector { connectionConfig: migrationContext.InspectorConnectionConfig, migrationContext: migrationContext, name: "inspector", + grantOnRe: regexp.MustCompile(" ON `(.*?)`\\.\\*"), } } @@ -242,16 +245,10 @@ func (this *Inspector) validateGrants() error { if strings.Contains(grant, `REPLICATION SLAVE`) && strings.Contains(grant, ` ON *.*`) { foundReplicationSlave = true } - if strings.Contains(grant, fmt.Sprintf("GRANT ALL PRIVILEGES ON `%s`.*", this.migrationContext.DatabaseName)) { + if this.grantContainsAll(grant, "GRANT ALL PRIVILEGES ") { foundDBAll = true } - if strings.Contains(grant, fmt.Sprintf("GRANT ALL PRIVILEGES ON `%s`.*", strings.Replace(this.migrationContext.DatabaseName, "_", "\\_", -1))) { - foundDBAll = true - } - if base.StringContainsAll(grant, `ALTER`, `CREATE`, `DELETE`, `DROP`, `INDEX`, `INSERT`, `LOCK TABLES`, `SELECT`, `TRIGGER`, `UPDATE`, ` ON *.*`) { - foundDBAll = true - } - if base.StringContainsAll(grant, `ALTER`, `CREATE`, `DELETE`, `DROP`, `INDEX`, `INSERT`, `LOCK TABLES`, `SELECT`, `TRIGGER`, `UPDATE`, fmt.Sprintf(" ON `%s`.*", this.migrationContext.DatabaseName)) { + if this.grantContainsAll(grant, `ALTER`, `CREATE`, `DELETE`, `DROP`, `INDEX`, `INSERT`, `LOCK TABLES`, `SELECT`, `TRIGGER`, `UPDATE`) { foundDBAll = true } } @@ -278,6 +275,41 @@ func (this *Inspector) validateGrants() error { return this.migrationContext.Log.Errorf("User has insufficient privileges for migration. Needed: SUPER|REPLICATION CLIENT, REPLICATION SLAVE and ALL on %s.*", sql.EscapeName(this.migrationContext.DatabaseName)) } +// grantContainsAll verifies the passed grant contain all the passed strings and +// that the grant match the DB +func (this *Inspector) grantContainsAll(grant string, substrings ...string) bool { + if !base.StringContainsAll(grant, substrings...) { + return false + } + if strings.Contains(grant, ` ON *.*`) { + return true + } + matches := this.grantOnRe.FindStringSubmatch(grant) + if matches == nil { + return false + } + mysqlPattern := matches[1] + regexPattern := "^" + escape := false + for _, c := range mysqlPattern { + if escape { + regexPattern += regexp.QuoteMeta(string(c)) + escape = false + } else if c == '%' { + regexPattern += ".*" + } else if c == '_' { + regexPattern += "." + } else if c == '\\' { + escape = true + } else { + regexPattern += regexp.QuoteMeta(string(c)) + } + } + regexPattern += "$" + match, _ := regexp.MatchString(regexPattern, this.migrationContext.DatabaseName) + return match +} + // restartReplication is required so that we are _certain_ the binlog format and // row image settings have actually been applied to the replication thread. // It is entirely possible, for example, that the replication is using 'STATEMENT'