Skip to content

Commit

Permalink
cherry pick pingcap#26070 to release-4.0
Browse files Browse the repository at this point in the history
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
  • Loading branch information
morgo authored and ti-srebot committed Jul 16, 2021
1 parent e4a7573 commit 0e83ae7
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 7 deletions.
3 changes: 2 additions & 1 deletion executor/infoschema_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,8 @@ func (e *memtableRetriever) setDataForProcessList(ctx sessionctx.Context) {

func (e *memtableRetriever) setDataFromUserPrivileges(ctx sessionctx.Context) {
pm := privilege.GetPrivilegeManager(ctx)
e.rows = pm.UserPrivilegesTable()
// The results depend on the user querying the information.
e.rows = pm.UserPrivilegesTable(ctx.GetSessionVars().ActiveRoles, ctx.GetSessionVars().User.Username, ctx.GetSessionVars().User.Hostname)
}

func (e *memtableRetriever) setDataForMetricTables(ctx sessionctx.Context) {
Expand Down
25 changes: 25 additions & 0 deletions executor/infoschema_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,31 @@ func (s *testInfoschemaTableSuite) TestUserPrivileges(c *C) {
c.Assert(len(result.Rows()), Greater, 0)
}

<<<<<<< HEAD
=======
func (s *testInfoschemaTableSuite) TestUserPrivilegesTable(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk1 := testkit.NewTestKit(c, s.store)

// test the privilege of new user for information_schema.user_privileges
tk.MustExec("create user usageuser")
c.Assert(tk.Se.Auth(&auth.UserIdentity{
Username: "usageuser",
Hostname: "127.0.0.1",
}, nil, nil), IsTrue)
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee="'usageuser'@'%'"`).Check(testkit.Rows("'usageuser'@'%' def USAGE NO"))
// the usage row disappears when there is a non-dynamic privilege added
tk1.MustExec("GRANT SELECT ON *.* to usageuser")
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee="'usageuser'@'%'"`).Check(testkit.Rows("'usageuser'@'%' def Select NO"))
// test grant privilege
tk1.MustExec("GRANT SELECT ON *.* to usageuser WITH GRANT OPTION")
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee="'usageuser'@'%'"`).Check(testkit.Rows("'usageuser'@'%' def Select YES"))
// test DYNAMIC privs
tk1.MustExec("GRANT BACKUP_ADMIN ON *.* to usageuser")
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee="'usageuser'@'%'" ORDER BY privilege_type`).Check(testkit.Rows("'usageuser'@'%' def BACKUP_ADMIN NO", "'usageuser'@'%' def Select YES"))
}

>>>>>>> 723e2bc6d... executor, privileges: fix infoschema.user_privileges privilege requirements (#26070)
func (s *testInfoschemaTableSerialSuite) TestDataForTableStatsField(c *C) {
s.dom.SetStatsUpdating(true)
oldExpiryTime := executor.TableStatsCacheExpiry
Expand Down
5 changes: 5 additions & 0 deletions privilege/privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ type Manager interface {
// DBIsVisible returns true is the database is visible to current user.
DBIsVisible(activeRole []*auth.RoleIdentity, db string) bool

<<<<<<< HEAD
// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGE table.
UserPrivilegesTable() [][]types.Datum
=======
// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USER_PRIVILEGES table.
UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum
>>>>>>> 723e2bc6d... executor, privileges: fix infoschema.user_privileges privilege requirements (#26070)

// ActiveRoles active roles for current session.
// The first illegal role will be returned.
Expand Down
24 changes: 20 additions & 4 deletions privilege/privileges/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -1247,12 +1247,28 @@ func privToString(priv mysql.PrivilegeType, allPrivs []mysql.PrivilegeType, allP
return strings.Join(pstrs, ",")
}

// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGE table.
func (p *MySQLPrivilege) UserPrivilegesTable() [][]types.Datum {
// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGES table.
func (p *MySQLPrivilege) UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum {
// Seeing all users requires SELECT ON * FROM mysql.*
// The SUPER privilege (or any other dynamic privilege) doesn't help here.
// This is verified against MySQL.
showOtherUsers := p.RequestVerification(activeRoles, user, host, mysql.SystemDB, "", "", mysql.SelectPriv)
var rows [][]types.Datum
for _, user := range p.User {
rows = appendUserPrivilegesTableRow(rows, user)
for _, u := range p.User {
if showOtherUsers || u.match(user, host) {
rows = appendUserPrivilegesTableRow(rows, u)
}
}
<<<<<<< HEAD
=======
for _, dynamicPrivs := range p.Dynamic {
for _, dynamicPriv := range dynamicPrivs {
if showOtherUsers || dynamicPriv.match(user, host) {
rows = appendDynamicPrivRecord(rows, dynamicPriv)
}
}
}
>>>>>>> 723e2bc6d... executor, privileges: fix infoschema.user_privileges privilege requirements (#26070)
return rows
}

Expand Down
4 changes: 2 additions & 2 deletions privilege/privileges/privileges.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,9 @@ func (p *UserPrivileges) DBIsVisible(activeRoles []*auth.RoleIdentity, db string
}

// UserPrivilegesTable implements the Manager interface.
func (p *UserPrivileges) UserPrivilegesTable() [][]types.Datum {
func (p *UserPrivileges) UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum {
mysqlPriv := p.Handle.Get()
return mysqlPriv.UserPrivilegesTable()
return mysqlPriv.UserPrivilegesTable(activeRoles, user, host)
}

// ShowGrants implements privilege.Manager ShowGrants interface.
Expand Down
142 changes: 142 additions & 0 deletions privilege/privileges/privileges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,3 +1228,145 @@ func (s *testPrivilegeSuite) TestViewDefiner(c *C) {
tk.MustExec("select * from test_view")
tk.MustExec("select * from test_view2")
}
<<<<<<< HEAD
=======

func (s *testPrivilegeSuite) TestSecurityEnhancedModeRestrictedUsers(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("CREATE USER ruroot1, ruroot2, ruroot3")
tk.MustExec("CREATE ROLE notimportant")
tk.MustExec("GRANT SUPER, CREATE USER ON *.* to ruroot1 WITH GRANT OPTION")
tk.MustExec("GRANT SUPER, RESTRICTED_USER_ADMIN, CREATE USER ON *.* to ruroot2 WITH GRANT OPTION")
tk.MustExec("GRANT RESTRICTED_USER_ADMIN ON *.* to ruroot3")
tk.MustExec("GRANT notimportant TO ruroot2, ruroot3")

sem.Enable()
defer sem.Disable()

stmts := []string{
"SET PASSWORD for ruroot3 = 'newpassword'",
"REVOKE notimportant FROM ruroot3",
"REVOKE SUPER ON *.* FROM ruroot3",
"DROP USER ruroot3",
}

// ruroot1 has SUPER but in SEM will be restricted
tk.Se.Auth(&auth.UserIdentity{
Username: "ruroot1",
Hostname: "localhost",
AuthUsername: "uroot",
AuthHostname: "%",
}, nil, nil)

for _, stmt := range stmts {
err := tk.ExecToErr(stmt)
c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_USER_ADMIN privilege(s) for this operation")
}

// Switch to ruroot2, it should be permitted
tk.Se.Auth(&auth.UserIdentity{
Username: "ruroot2",
Hostname: "localhost",
AuthUsername: "uroot",
AuthHostname: "%",
}, nil, nil)

for _, stmt := range stmts {
err := tk.ExecToErr(stmt)
c.Assert(err, IsNil)
}
}

func (s *testPrivilegeSuite) TestDynamicPrivsRegistration(c *C) {
se := newSession(c, s.store, s.dbName)
pm := privilege.GetPrivilegeManager(se)

count := len(privileges.GetDynamicPrivileges())

c.Assert(pm.IsDynamicPrivilege("ACDC_ADMIN"), IsFalse)
c.Assert(privileges.RegisterDynamicPrivilege("ACDC_ADMIN"), IsNil)
c.Assert(pm.IsDynamicPrivilege("ACDC_ADMIN"), IsTrue)
c.Assert(len(privileges.GetDynamicPrivileges()), Equals, count+1)

c.Assert(pm.IsDynamicPrivilege("iAmdynamIC"), IsFalse)
c.Assert(privileges.RegisterDynamicPrivilege("IAMdynamic"), IsNil)
c.Assert(pm.IsDynamicPrivilege("IAMdyNAMIC"), IsTrue)
c.Assert(len(privileges.GetDynamicPrivileges()), Equals, count+2)

c.Assert(privileges.RegisterDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS").Error(), Equals, "privilege name is longer than 32 characters")
c.Assert(pm.IsDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS"), IsFalse)

tk := testkit.NewTestKit(c, s.store)
tk.MustExec("CREATE USER privassigntest")

// Check that all privileges registered are assignable to users,
// including the recently registered ACDC_ADMIN
for _, priv := range privileges.GetDynamicPrivileges() {
sqlGrant, err := sqlexec.EscapeSQL("GRANT %n ON *.* TO privassigntest", priv)
c.Assert(err, IsNil)
tk.MustExec(sqlGrant)
}
// Check that all privileges registered are revokable
for _, priv := range privileges.GetDynamicPrivileges() {
sqlGrant, err := sqlexec.EscapeSQL("REVOKE %n ON *.* FROM privassigntest", priv)
c.Assert(err, IsNil)
tk.MustExec(sqlGrant)
}
}

func (s *testPrivilegeSuite) TestInfoschemaUserPrivileges(c *C) {
// Being able to read all privileges from information_schema.user_privileges requires a very specific set of permissions.
// SUPER user is not sufficient. It was observed in MySQL to require SELECT on mysql.*
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("CREATE USER isnobody, isroot, isselectonmysqluser, isselectonmysql")
tk.MustExec("GRANT SUPER ON *.* TO isroot")
tk.MustExec("GRANT SELECT ON mysql.user TO isselectonmysqluser")
tk.MustExec("GRANT SELECT ON mysql.* TO isselectonmysql")

// First as Nobody
tk.Se.Auth(&auth.UserIdentity{
Username: "isnobody",
Hostname: "localhost",
}, nil, nil)

// I can see myself, but I can not see other users
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO"))
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows())
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows())

// Basically the same result as as isselectonmysqluser
tk.Se.Auth(&auth.UserIdentity{
Username: "isselectonmysqluser",
Hostname: "localhost",
}, nil, nil)

// Now as isselectonmysqluser
// Tests discovered issue that SELECT on mysql.user is not sufficient. It must be on mysql.*
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows())
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows())
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO"))
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysql'@'%'"`).Check(testkit.Rows())

// Now as root
tk.Se.Auth(&auth.UserIdentity{
Username: "isroot",
Hostname: "localhost",
}, nil, nil)

// I can see myself, but I can not see other users
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows())
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def Super NO"))
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows())

// Now as isselectonmysqluser
tk.Se.Auth(&auth.UserIdentity{
Username: "isselectonmysql",
Hostname: "localhost",
}, nil, nil)

// Now as isselectonmysqluser
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO"))
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def Super NO"))
tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO"))
}
>>>>>>> 723e2bc6d... executor, privileges: fix infoschema.user_privileges privilege requirements (#26070)

0 comments on commit 0e83ae7

Please sign in to comment.