From a356dc75f297b651f7b1b0c541122253ba9b3a9f Mon Sep 17 00:00:00 2001 From: lance6716 Date: Wed, 30 Nov 2022 20:18:01 +0800 Subject: [PATCH] This is an automated cherry-pick of #7739 Signed-off-by: ti-chi-bot --- dm/pkg/checker/privilege.go | 34 +++++++- dm/pkg/checker/privilege_test.go | 141 +++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/dm/pkg/checker/privilege.go b/dm/pkg/checker/privilege.go index ecfa20333ca..19e1e50ebf3 100644 --- a/dm/pkg/checker/privilege.go +++ b/dm/pkg/checker/privilege.go @@ -25,6 +25,12 @@ import ( _ "github.com/pingcap/tidb/types/parser_driver" // for parser driver "github.com/pingcap/tidb/util/dbutil" "github.com/pingcap/tidb/util/filter" +<<<<<<< HEAD +======= + "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tiflow/dm/pkg/log" + "github.com/pingcap/tiflow/pkg/container/sortmap" +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) "go.uber.org/zap" "github.com/pingcap/tiflow/dm/pkg/log" @@ -172,7 +178,7 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil return NewError("grant has no user %s", grant) } - dbName := grantStmt.Level.DBName + dbPatChar, dbPatType := stringutil.CompilePattern(grantStmt.Level.DBName, '\\') tableName := grantStmt.Level.TableName switch grantStmt.Level.Level { case ast.GrantLevelGlobal: @@ -203,6 +209,7 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil if priv == mysql.GrantPriv { continue } +<<<<<<< HEAD if _, ok := lackPriv[priv][dbName]; !ok { continue } @@ -210,26 +217,44 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil if len(lackPriv[priv]) == 0 { delete(lackPriv, priv) } +======= + for dbName := range privs.dbs { + if stringutil.DoMatch(dbName, dbPatChar, dbPatType) { + delete(privs.dbs, dbName) + } + } +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) } continue } if _, ok := lackPriv[privElem.Priv]; !ok { continue } +<<<<<<< HEAD if _, ok := lackPriv[privElem.Priv][dbName]; !ok { continue } +======= +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) // dumpling could report error if an allow-list table is lack of privilege. // we only check that SELECT is granted on all columns, otherwise we can't SHOW CREATE TABLE if privElem.Priv == mysql.SelectPriv && len(privElem.Cols) != 0 { continue } +<<<<<<< HEAD delete(lackPriv[privElem.Priv], dbName) if len(lackPriv[privElem.Priv]) == 0 { delete(lackPriv, privElem.Priv) +======= + for dbName := range privs.dbs { + if stringutil.DoMatch(dbName, dbPatChar, dbPatType) { + delete(privs.dbs, dbName) + } +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) } } case ast.GrantLevelTable: + dbName := grantStmt.Level.DBName for _, privElem := range grantStmt.Privs { // all privileges available at a given privilege level (except GRANT OPTION) // from https://dev.mysql.com/doc/refman/5.7/en/privileges-provided.html#priv_all @@ -241,6 +266,7 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil if _, ok := lackPriv[priv][dbName]; !ok { continue } +<<<<<<< HEAD if _, ok := lackPriv[priv][dbName][tableName]; !ok { continue } @@ -251,6 +277,9 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil if len(lackPriv[priv]) == 0 { delete(lackPriv, priv) } +======= + delete(dbPrivs.tables, tableName) +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) } continue } @@ -260,9 +289,12 @@ func verifyPrivileges(result *Result, grants []string, lackPriv map[mysql.Privil if _, ok := lackPriv[privElem.Priv][dbName]; !ok { continue } +<<<<<<< HEAD if _, ok := lackPriv[privElem.Priv][dbName][tableName]; !ok { continue } +======= +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) // dumpling could report error if an allow-list table is lack of privilege. // we only check that SELECT is granted on all columns, otherwise we can't SHOW CREATE TABLE if privElem.Priv == mysql.SelectPriv && len(privElem.Cols) != 0 { diff --git a/dm/pkg/checker/privilege_test.go b/dm/pkg/checker/privilege_test.go index 901b920c23a..a90c23f3cd0 100644 --- a/dm/pkg/checker/privilege_test.go +++ b/dm/pkg/checker/privilege_test.go @@ -283,9 +283,150 @@ func (t *testCheckSuite) TestVerifyReplicationPrivileges(c *tc.C) { }, } +<<<<<<< HEAD replicationPrivileges := map[mysql.PrivilegeType]struct{}{ mysql.ReplicationClientPriv: {}, mysql.ReplicationSlavePriv: {}, +======= + for _, cs := range cases { + result := &Result{ + State: StateFailure, + } + replRequiredPrivs := map[mysql.PrivilegeType]priv{ + mysql.ReplicationSlavePriv: {needGlobal: true}, + mysql.ReplicationClientPriv: {needGlobal: true}, + } + err := verifyPrivilegesWithResult(result, cs.grants, replRequiredPrivs) + if cs.replicationState == StateSuccess { + require.Nil(t, err, "grants: %v", cs.grants) + } else { + require.NotNil(t, err, "grants: %v", cs.grants) + require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) + } + } +} + +func TestVerifyPrivilegesWildcard(t *testing.T) { + cases := []struct { + grants []string + checkTables []filter.Table + replicationState State + errStr string + }{ + { + grants: []string{ + "GRANT SELECT ON `demo\\_foobar`.* TO `dmuser`@`%`", + }, + checkTables: []filter.Table{ + {Schema: "demo_foobar", Name: "t1"}, + }, + replicationState: StateSuccess, + }, + { + grants: []string{ + "GRANT SELECT ON `demo\\_foobar`.* TO `dmuser`@`%`", + }, + checkTables: []filter.Table{ + {Schema: "demo2foobar", Name: "t1"}, + }, + replicationState: StateFailure, + errStr: "lack of Select privilege: {`demo2foobar`.`t1`}; ", + }, + { + grants: []string{ + "GRANT SELECT ON `demo_`.* TO `dmuser`@`%`", + }, + checkTables: []filter.Table{ + {Schema: "demo1", Name: "t1"}, + {Schema: "demo2", Name: "t1"}, + }, + replicationState: StateSuccess, + }, + { + grants: []string{ + "GRANT SELECT ON `demo%`.* TO `dmuser`@`%`", + }, + checkTables: []filter.Table{ + {Schema: "demo_some", Name: "t1"}, + {Schema: "block_db", Name: "t1"}, + }, + replicationState: StateFailure, + errStr: "lack of Select privilege: {`block_db`.`t1`}; ", + }, + { + grants: []string{ + "GRANT SELECT ON `demo_db`.`t1` TO `dmuser`@`%`", + }, + checkTables: []filter.Table{ + {Schema: "demo_db", Name: "t1"}, + {Schema: "demo2db", Name: "t1"}, + }, + replicationState: StateFailure, + errStr: "lack of Select privilege: {`demo2db`.`t1`}; ", + }, + } + + for i, cs := range cases { + t.Logf("case %d", i) + result := &Result{ + State: StateFailure, + } + requiredPrivs := map[mysql.PrivilegeType]priv{ + mysql.SelectPriv: { + dbs: genTableLevelPrivs(cs.checkTables), + }, + } + err := verifyPrivilegesWithResult(result, cs.grants, requiredPrivs) + if cs.replicationState == StateSuccess { + require.Nil(t, err, "grants: %v", cs.grants) + } else { + require.NotNil(t, err, "grants: %v", cs.grants) + require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) + } + } +} + +func TestVerifyTargetPrivilege(t *testing.T) { + cases := []struct { + grants []string + checkState State + errStr string + }{ + { + grants: nil, // non grants + checkState: StateWarning, + errStr: "there is no such grant defined for current user on host '%'", + }, + { + grants: []string{"invalid SQL statement"}, + checkState: StateWarning, + errStr: "line 1 column 7 near \"invalid SQL statement\" ", + }, + { + grants: []string{"CREATE DATABASE db1"}, // non GRANT statement + checkState: StateWarning, + errStr: "CREATE DATABASE db1 is not grant statement", + }, + { + grants: []string{ + "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%'", + }, + checkState: StateSuccess, + }, + { + grants: []string{ + "GRANT SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP ON *.* TO 'root'@'%'", + }, + checkState: StateSuccess, + }, + { + grants: []string{ + "GRANT SELECT, INSERT, DELETE, ALTER, DROP ON *.* TO 'root'@'%'", + }, + checkState: StateWarning, + errStr: "lack of Create global (*.*) privilege; lack of Update global (*.*) privilege; ", + }, +>>>>>>> a2d2a5213c (checker(dm): support wildcard in privilege checking (#7739)) } for _, cs := range cases { result := &Result{