Skip to content

Commit

Permalink
executor, privileges: fix infoschema.user_privileges privilege requir…
Browse files Browse the repository at this point in the history
…ements (#26070)
  • Loading branch information
morgo authored Jul 16, 2021
1 parent 5869dbf commit 723e2bc
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 12 deletions.
3 changes: 2 additions & 1 deletion executor/infoschema_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,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 @@ -67,7 +67,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 @@ -1430,15 +1430,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 @@ -522,9 +522,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 @@ -1805,3 +1805,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"))
}

0 comments on commit 723e2bc

Please sign in to comment.