From d4bad4ebb3a7e1403b184b9669d95033a4f9b6c4 Mon Sep 17 00:00:00 2001 From: Lingyu Song Date: Wed, 13 Mar 2019 15:01:38 +0800 Subject: [PATCH] executor: add `DROP ROLE` support (#9616) --- executor/simple.go | 38 +++++++++++++++++++++++++++++++++++++ executor/simple_test.go | 34 +++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- planner/core/planbuilder.go | 10 +++++++++- 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/executor/simple.go b/executor/simple.go index 6a72297b6d705..621b228262e93 100644 --- a/executor/simple.go +++ b/executor/simple.go @@ -300,6 +300,44 @@ func (e *SimpleExec) executeDropUser(s *ast.DropUserStmt) error { continue } + // delete relationship from mysql.role_edges + sql = fmt.Sprintf(`DELETE FROM %s.%s WHERE TO_HOST = '%s' and TO_USER = '%s';`, mysql.SystemDB, mysql.RoleEdgeTable, user.Hostname, user.Username) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), sql); err != nil { + failedUsers = append(failedUsers, user.String()) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), "rollback"); err != nil { + return errors.Trace(err) + } + continue + } + + sql = fmt.Sprintf(`DELETE FROM %s.%s WHERE FROM_HOST = '%s' and FROM_USER = '%s';`, mysql.SystemDB, mysql.RoleEdgeTable, user.Hostname, user.Username) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), sql); err != nil { + failedUsers = append(failedUsers, user.String()) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), "rollback"); err != nil { + return errors.Trace(err) + } + continue + } + + // delete relationship from mysql.default_roles + sql = fmt.Sprintf(`DELETE FROM %s.%s WHERE DEFAULT_ROLE_HOST = '%s' and DEFAULT_ROLE_USER = '%s';`, mysql.SystemDB, mysql.DefaultRoleTable, user.Hostname, user.Username) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), sql); err != nil { + failedUsers = append(failedUsers, user.String()) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), "rollback"); err != nil { + return errors.Trace(err) + } + continue + } + + sql = fmt.Sprintf(`DELETE FROM %s.%s WHERE HOST = '%s' and USER = '%s';`, mysql.SystemDB, mysql.DefaultRoleTable, user.Hostname, user.Username) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), sql); err != nil { + failedUsers = append(failedUsers, user.String()) + if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), "rollback"); err != nil { + return errors.Trace(err) + } + continue + } + //TODO: need delete columns_priv once we implement columns_priv functionality. if _, err := e.ctx.(sqlexec.SQLExecutor).Execute(context.Background(), "commit"); err != nil { failedUsers = append(failedUsers, user.String()) diff --git a/executor/simple_test.go b/executor/simple_test.go index fb1f669d50090..94305e7762fc1 100644 --- a/executor/simple_test.go +++ b/executor/simple_test.go @@ -87,6 +87,40 @@ func inTxn(ctx sessionctx.Context) bool { return (ctx.GetSessionVars().Status & mysql.ServerStatusInTrans) > 0 } +func (s *testSuite3) TestRole(c *C) { + tk := testkit.NewTestKit(c, s.store) + // Make sure user test not in mysql.User. + result := tk.MustQuery(`SELECT Password FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(nil) + + createRoleSQL := `CREATE ROLE 'test'@'localhost';` + tk.MustExec(createRoleSQL) + // Make sure user test in mysql.User. + result = tk.MustQuery(`SELECT Password FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword(""))) + // Insert relation into mysql.role_edges + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test','%','root')") + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test1','localhost','test1')") + // Insert relation into mysql.default_roles + tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('%','root','localhost','test')") + tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('localhost','test','%','test1')") + + dropUserSQL := `DROP ROLE IF EXISTS 'test'@'localhost' ;` + _, err := tk.Exec(dropUserSQL) + c.Check(err, IsNil) + + result = tk.MustQuery(`SELECT Password FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE TO_USER="test" and TO_HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE FROM_USER="test" and FROM_HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE USER="test" and HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE DEFAULT_ROLE_USER="test" and DEFAULT_ROLE_HOST="localhost"`) + result.Check(nil) +} + func (s *testSuite3) TestUser(c *C) { tk := testkit.NewTestKit(c, s.store) // Make sure user test not in mysql.User. diff --git a/go.mod b/go.mod index c7f2527732a6f..87ca51eefc0b3 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 - github.com/pingcap/parser v0.0.0-20190311093336-9fca026d9c49 + github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7 diff --git a/go.sum b/go.sum index 28b2d92651502..5ba7c1710768d 100644 --- a/go.sum +++ b/go.sum @@ -113,8 +113,8 @@ github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 h1:32oF1/8lVnBR2JV github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 h1:t2OQTpPJnrPDGlvA+3FwJptMTt6MEPdzK1Wt99oaefQ= github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw= -github.com/pingcap/parser v0.0.0-20190311093336-9fca026d9c49 h1:dnac5Z4hlmf8Ocd+RasmkuEuI0ZJRH+QJAh3K6vMySQ= -github.com/pingcap/parser v0.0.0-20190311093336-9fca026d9c49/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b h1:NlvTrxqezIJh6CD5Leky12IZ8E/GtpEEmzgNNb34wbw= +github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE= github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible h1:e9Gi/LP9181HT3gBfSOeSBA+5JfemuE4aEAhqNgoE4k= diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4b776758a4003..41cdfc5a3b880 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1019,7 +1019,15 @@ func (b *PlanBuilder) buildSimple(node ast.StmtNode) (Plan, error) { err := ErrSpecificAccessDenied.GenWithStackByArgs("CREATE USER") b.visitInfo = appendVisitInfo(b.visitInfo, mysql.CreateUserPriv, "", "", "", err) } - case *ast.DropUserStmt, *ast.AlterUserStmt: + case *ast.DropUserStmt: + if raw.IsDropRole { + err := ErrSpecificAccessDenied.GenWithStackByArgs("DROP ROLE") + b.visitInfo = appendVisitInfo(b.visitInfo, mysql.DropRolePriv, "", "", "", err) + } else { + err := ErrSpecificAccessDenied.GenWithStackByArgs("CREATE USER") + b.visitInfo = appendVisitInfo(b.visitInfo, mysql.CreateUserPriv, "", "", "", err) + } + case *ast.AlterUserStmt: err := ErrSpecificAccessDenied.GenWithStackByArgs("CREATE USER") b.visitInfo = appendVisitInfo(b.visitInfo, mysql.CreateUserPriv, "", "", "", err) case *ast.GrantStmt: