From 7c63212b076fbedaaecace0728e0fe8792cb824c Mon Sep 17 00:00:00 2001 From: Lingyu Song Date: Sat, 9 Nov 2019 11:03:14 +0800 Subject: [PATCH] privilege: fix `REVOKE` privilege check incompatibility with MySQL (#13014) --- planner/core/logical_plan_test.go | 15 +++++++++-- planner/core/planbuilder.go | 35 ++++++++++++++++++++++++- privilege/privileges/privileges_test.go | 18 +++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index c54f4f2b42b92..e33c95f9e5193 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1804,9 +1804,20 @@ func (s *testPlanSuite) TestVisitInfo(c *C) { }, }, { - sql: `revoke all privileges on *.* from 'test'@'%'`, + sql: `revoke all privileges on test.* from 'test'@'%'`, ans: []visitInfo{ - {mysql.SuperPriv, "", "", "", nil}, + {mysql.SelectPriv, "test", "", "", nil}, + {mysql.InsertPriv, "test", "", "", nil}, + {mysql.UpdatePriv, "test", "", "", nil}, + {mysql.DeletePriv, "test", "", "", nil}, + {mysql.CreatePriv, "test", "", "", nil}, + {mysql.DropPriv, "test", "", "", nil}, + {mysql.GrantPriv, "test", "", "", nil}, + {mysql.AlterPriv, "test", "", "", nil}, + {mysql.ExecutePriv, "test", "", "", nil}, + {mysql.IndexPriv, "test", "", "", nil}, + {mysql.CreateViewPriv, "test", "", "", nil}, + {mysql.ShowViewPriv, "test", "", "", nil}, }, }, { diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index c2315c75ebb8e..bce9dbcc03bfa 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1573,7 +1573,7 @@ func (b *PlanBuilder) buildSimple(node ast.StmtNode) (Plan, error) { err := ErrSpecificAccessDenied.GenWithStackByArgs("GRANT ROLE") b.visitInfo = appendVisitInfo(b.visitInfo, mysql.GrantPriv, "", "", "", err) case *ast.RevokeStmt: - b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SuperPriv, "", "", "", nil) + b.visitInfo = collectVisitInfoFromRevokeStmt(b.ctx, b.visitInfo, raw) case *ast.RevokeRoleStmt: b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SuperPriv, "", "", "", nil) case *ast.KillStmt: @@ -1598,6 +1598,39 @@ func (b *PlanBuilder) buildSimple(node ast.StmtNode) (Plan, error) { return p, nil } +func collectVisitInfoFromRevokeStmt(sctx sessionctx.Context, vi []visitInfo, stmt *ast.RevokeStmt) []visitInfo { + // To use REVOKE, you must have the GRANT OPTION privilege, + // and you must have the privileges that you are granting. + dbName := stmt.Level.DBName + tableName := stmt.Level.TableName + if dbName == "" { + dbName = sctx.GetSessionVars().CurrentDB + } + vi = appendVisitInfo(vi, mysql.GrantPriv, dbName, tableName, "", nil) + + var allPrivs []mysql.PrivilegeType + for _, item := range stmt.Privs { + if item.Priv == mysql.AllPriv { + switch stmt.Level.Level { + case ast.GrantLevelGlobal: + allPrivs = mysql.AllGlobalPrivs + case ast.GrantLevelDB: + allPrivs = mysql.AllDBPrivs + case ast.GrantLevelTable: + allPrivs = mysql.AllTablePrivs + } + break + } + vi = appendVisitInfo(vi, item.Priv, dbName, tableName, "", nil) + } + + for _, priv := range allPrivs { + vi = appendVisitInfo(vi, priv, dbName, tableName, "", nil) + } + + return vi +} + func collectVisitInfoFromGrantStmt(sctx sessionctx.Context, vi []visitInfo, stmt *ast.GrantStmt) []visitInfo { // To use GRANT, you must have the GRANT OPTION privilege, // and you must have the privileges that you are granting. diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 67714f80cee4a..2bcdbab8f0116 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -543,6 +543,24 @@ func (s *testPrivilegeSuite) TestUseDB(c *C) { c.Assert(err, NotNil) } +func (s *testPrivilegeSuite) TestRevokePrivileges(c *C) { + se := newSession(c, s.store, s.dbName) + mustExec(c, se, "CREATE USER 'hasgrant'") + mustExec(c, se, "CREATE USER 'withoutgrant'") + mustExec(c, se, "GRANT ALL ON *.* TO 'hasgrant'") + mustExec(c, se, "GRANT ALL ON mysql.* TO 'withoutgrant'") + // Without grant option + c.Assert(se.Auth(&auth.UserIdentity{Username: "hasgrant", Hostname: "localhost", AuthUsername: "hasgrant", AuthHostname: "%"}, nil, nil), IsTrue) + _, e := se.Execute(context.Background(), "REVOKE SELECT ON mysql.* FROM 'withoutgrant'") + c.Assert(e, NotNil) + // With grant option + se = newSession(c, s.store, s.dbName) + mustExec(c, se, "GRANT ALL ON *.* TO 'hasgrant' WITH GRANT OPTION") + c.Assert(se.Auth(&auth.UserIdentity{Username: "hasgrant", Hostname: "localhost", AuthUsername: "hasgrant", AuthHostname: "%"}, nil, nil), IsTrue) + mustExec(c, se, "REVOKE SELECT ON mysql.* FROM 'withoutgrant'") + mustExec(c, se, "REVOKE ALL ON mysql.* FROM withoutgrant") +} + func (s *testPrivilegeSuite) TestSetGlobal(c *C) { se := newSession(c, s.store, s.dbName) mustExec(c, se, `CREATE USER setglobal_a@localhost`)