Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

executor, privileges: fix infoschema.user_privileges privilege requirements #26070

Merged
merged 10 commits into from
Jul 16, 2021
3 changes: 2 additions & 1 deletion executor/infoschema_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,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
12 changes: 9 additions & 3 deletions executor/infoschema_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,17 +395,23 @@ func (s *testInfoschemaTableSuite) TestUserPrivileges(c *C) {

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
tk.MustExec("GRANT SELECT ON *.* to usageuser")
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
tk.MustExec("GRANT SELECT ON *.* to usageuser WITH GRANT OPTION")
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
tk.MustExec("GRANT BACKUP_ADMIN ON *.* to usageuser")
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"))
}

Expand Down
2 changes: 1 addition & 1 deletion privilege/privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type Manager interface {
DBIsVisible(activeRole []*auth.RoleIdentity, db string) bool

// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USER_PRIVILEGES table.
UserPrivilegesTable() [][]types.Datum
UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum

// ActiveRoles active roles for current session.
// The first illegal role will be returned.
Expand Down
18 changes: 13 additions & 5 deletions privilege/privileges/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -1420,15 +1420,23 @@ 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)
}
}
for _, dynamicPrivs := range p.Dynamic {
for _, dynamicPriv := range dynamicPrivs {
rows = appendDynamicPrivRecord(rows, dynamicPriv)
if showOtherUsers || dynamicPriv.match(user, host) {
rows = appendDynamicPrivRecord(rows, dynamicPriv)
}
}
}
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 @@ -507,9 +507,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
56 changes: 56 additions & 0 deletions privilege/privileges/privileges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1745,3 +1745,59 @@ func (s *testPrivilegeSuite) TestDynamicPrivsRegistration(c *C) {
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"))
}