diff --git a/executor/show.go b/executor/show.go index 9fffaf16bf44e..01017e8fefb5f 100644 --- a/executor/show.go +++ b/executor/show.go @@ -65,7 +65,7 @@ type ShowExec struct { IndexName model.CIStr // Used for show table regions. Flag int // Some flag parsed from sql, such as FULL. Full bool - User *auth.UserIdentity // Used for show grants. + User *auth.UserIdentity // Used by show grants, show create user. Roles []*auth.RoleIdentity // Used for show grants. IfNotExists bool // Used for `show create database if not exists` @@ -946,8 +946,23 @@ func (e *ShowExec) fetchShowCreateUser() error { if checker == nil { return errors.New("miss privilege checker") } + + userName, hostName := e.User.Username, e.User.Hostname + sessVars := e.ctx.GetSessionVars() + if e.User.CurrentUser { + userName = sessVars.User.AuthUsername + hostName = sessVars.User.AuthHostname + } else { + // Show create user requires the SELECT privilege on mysql.user. + // Ref https://dev.mysql.com/doc/refman/5.7/en/show-create-user.html + activeRoles := sessVars.ActiveRoles + if !checker.RequestVerification(activeRoles, mysql.SystemDB, mysql.UserTable, "", mysql.SelectPriv) { + return e.tableAccessDenied("SELECT", mysql.UserTable) + } + } + sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s';`, - mysql.SystemDB, mysql.UserTable, e.User.Username, e.User.Hostname) + mysql.SystemDB, mysql.UserTable, userName, hostName) rows, _, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) if err != nil { return errors.Trace(err) @@ -956,9 +971,8 @@ func (e *ShowExec) fetchShowCreateUser() error { return ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", fmt.Sprintf("'%s'@'%s'", e.User.Username, e.User.Hostname)) } - showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH 'mysql_native_password' AS '%s' %s", - e.User.Username, e.User.Hostname, checker.GetEncodedPassword(e.User.Username, e.User.Hostname), - "REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK") + showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH 'mysql_native_password' AS '%s' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK", + e.User.Username, e.User.Hostname, checker.GetEncodedPassword(e.User.Username, e.User.Hostname)) e.appendRow([]interface{}{showStr}) return nil } diff --git a/executor/show_test.go b/executor/show_test.go index a1c8afc6548f6..675a2ccf1901e 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -278,7 +278,7 @@ func (s *testSuite2) TestShow2(c *C) { tk.MustQuery("show grants for current_user").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'`)) } -func (s *testSuite2) TestShow3(c *C) { +func (s *testSuite2) TestShowCreateUser(c *C) { tk := testkit.NewTestKit(c, s.store) // Create a new user. tk.MustExec(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED BY 'root';`) @@ -296,6 +296,27 @@ func (s *testSuite2) TestShow3(c *C) { // Case: a user that doesn't exist err = tk.QueryToErr("show create user 'aaa'@'localhost';") c.Assert(err.Error(), Equals, executor.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'aaa'@'localhost'").Error()) + + tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "127.0.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, nil) + rows := tk.MustQuery("show create user current_user") + rows.Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK")) + + rows = tk.MustQuery("show create user current_user()") + rows.Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK")) + + tk.MustExec("create user 'check_priv'") + + // "show create user" for other user requires the SELECT privilege on mysql database. + tk1 := testkit.NewTestKit(c, s.store) + tk1.MustExec("use mysql") + succ := tk1.Se.Auth(&auth.UserIdentity{Username: "check_priv", Hostname: "127.0.0.1", AuthUsername: "test_show", AuthHostname: "asdf"}, nil, nil) + c.Assert(succ, IsTrue) + err = tk1.QueryToErr("show create user 'root'@'%'") + c.Assert(err, NotNil) + + // "show create user" for current user doesn't check privileges. + rows = tk1.MustQuery("show create user current_user") + rows.Check(testkit.Rows("CREATE USER 'check_priv'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK")) } func (s *testSuite2) TestUnprivilegedShow(c *C) {