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

*: support "show create user" #9240

Merged
merged 19 commits into from Feb 21, 2019
Merged
Changes from 13 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -525,6 +525,9 @@ func (b *executorBuilder) buildShow(v *plannercore.Show) Executor {
if e.Tp == ast.ShowGrants && e.User == nil {
e.User = e.ctx.GetSessionVars().User
}
if e.Tp == ast.ShowCreateUser && e.User == nil {
e.User = e.ctx.GetSessionVars().User
}
This conversation was marked as resolved by morgo

This comment has been minimized.

Copy link
@morgo

morgo Feb 14, 2019

Contributor

For show grants, you can execute this command to see your own grants. For show create user though, this syntax is not supported. Is this required here?

This comment has been minimized.

Copy link
@morgo

morgo Feb 15, 2019

Contributor

(I checked out the code, and it should be safe to just remove these 3 lines).

This comment has been minimized.

Copy link
@lnhote

lnhote Feb 15, 2019

Author Contributor

I have removed these lines.

if e.Tp == ast.ShowMasterStatus {
// show master status need start ts.
if _, err := e.ctx.Txn(true); err != nil {
@@ -107,6 +107,8 @@ func (e *ShowExec) fetchAll() error {
return e.fetchShowColumns()
case ast.ShowCreateTable:
return e.fetchShowCreateTable()
case ast.ShowCreateUser:
return e.fetchShowCreateUser()
case ast.ShowCreateDatabase:
return e.fetchShowCreateDatabase()
case ast.ShowDatabases:
@@ -843,6 +845,29 @@ func (e *ShowExec) fetchShowCollation() error {
return nil
}

// fetchShowCreateUser composes show create create user result.
func (e *ShowExec) fetchShowCreateUser() error {
checker := privilege.GetPrivilegeManager(e.ctx)
if checker == nil {
return errors.New("miss privilege checker")
}
sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s';`,
mysql.SystemDB, mysql.UserTable, e.User.Username, e.User.Hostname)
rows, _, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
if err != nil {
return errors.Trace(err)
}
if len(rows) == 0 {
return ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER",
This conversation was marked as resolved by morgo

This comment has been minimized.

Copy link
@morgo

morgo Feb 15, 2019

Contributor

This is the wrong error code for MySQL compatibility. It should be:

return ErrNonexistingGrant.GenWithStackByArgs(e.User.Username, e.User.Hostname)

You will need to add this error to ./executor/errors.go:

@@ -48,6 +48,7 @@ var (
        ErrDBaccessDenied              = terror.ClassExecutor.New(mysql.ErrDBaccessDenied, mysql.MySQLErrName[mysql.ErrDBaccessDenied])
        ErrTableaccessDenied           = terror.ClassExecutor.New(mysql.ErrTableaccessDenied, mysql.MySQLErrName[mysql.ErrTableaccessDenied])
        ErrBadDB                       = terror.ClassExecutor.New(mysql.ErrBadDB, mysql.MySQLErrName[mysql.ErrBadDB])
+       ErrNonexistingGrant            = terror.ClassExecutor.New(mysql.ErrNonexistingGrant, mysql.MySQLErrName[mysql.ErrNonexistingGrant])
 )
 
 func init() {
@@ -63,6 +64,7 @@ func init() {
                mysql.ErrDBaccessDenied:              mysql.ErrDBaccessDenied,
                mysql.ErrTableaccessDenied:           mysql.ErrTableaccessDenied,
                mysql.ErrBadDB:                       mysql.ErrBadDB,
+               mysql.ErrNonexistingGrant:            mysql.ErrNonexistingGrant,
        }
        terror.ErrClassToMySQLCodes[terror.ClassExecutor] = tableMySQLErrCodes
 }

This comment has been minimized.

Copy link
@lnhote

lnhote Feb 15, 2019

Author Contributor

This is what I see in mysql of version 5.7.17. What is your mysql version?

mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.7.17 Homebrew

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show create user 'aaa'@'aaa';
ERROR 1396 (HY000): Operation SHOW CREATE USER failed for 'aaa'@'aaa'

This comment has been minimized.

Copy link
@morgo

morgo Feb 15, 2019

Contributor

@lnhote I checked 5.7 again, you are correct :-) Sorry about that.

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")
e.appendRow([]interface{}{showStr})
return nil
}

func (e *ShowExec) fetchShowGrants() error {
// Get checker
checker := privilege.GetPrivilegeManager(e.ctx)
@@ -16,13 +16,13 @@ package executor_test
import (
"context"
"fmt"

This comment has been minimized.

Copy link
@jackysp

jackysp Feb 20, 2019

Member

Please do not remove this line, keep the sys libs separate with the 3rd party libs.

This comment has been minimized.

Copy link
@lnhote

lnhote Feb 20, 2019

Author Contributor

Adding the blank line will cause check_dev to fail.

This comment has been minimized.

Copy link
@jackysp

jackysp Feb 21, 2019

Member

Why check_dev could pass in master branch? I think you've added a blank line with some white spaces, try to keep it as it used to be.

This comment has been minimized.

Copy link
@lnhote

lnhote Feb 21, 2019

Author Contributor

I fixed it.

. "github.com/pingcap/check"
"github.com/pingcap/errors"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/domain"
"github.com/pingcap/tidb/executor"
plannercore "github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx"
@@ -201,6 +201,26 @@ 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) {
tk := testkit.NewTestKit(c, s.store)
// Create a new user.
tk.MustExec(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED BY 'root';`)
tk.MustQuery("show create user 'test_show_create_user'@'%'").
Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK`))

tk.MustExec(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED BY 'test';`)
tk.MustQuery("show create user 'test_show_create_user'@'localhost';").
Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK`))

// Case: the user exists but the host portion doesn't match
err := tk.QueryToErr("show create user 'test_show_create_user'@'asdf';")
c.Assert(err.Error(), Equals, executor.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'test_show_create_user'@'asdf'").Error())

// 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())
}

func (s *testSuite2) TestUnprivilegedShow(c *C) {

tk := testkit.NewTestKit(c, s.store)
@@ -1744,6 +1744,10 @@ func buildShowSchema(s *ast.ShowStmt, isView bool) (schema *expression.Schema) {
} else {
names = []string{"View", "Create View", "character_set_client", "collation_connection"}
}
case ast.ShowCreateUser:
if s.User != nil {
names = []string{fmt.Sprintf("CREATE USER for %s", s.User)}
}
case ast.ShowCreateDatabase:
names = []string{"Database", "Create Database"}
case ast.ShowGrants:
@@ -43,6 +43,7 @@ func (s *testPlanBuilderSuite) TestShow(c *C) {
ast.ShowStatus,
ast.ShowCollation,
ast.ShowCreateTable,
ast.ShowCreateUser,
ast.ShowGrants,
ast.ShowTriggers,
ast.ShowProcedureStatus,
@@ -31,6 +31,9 @@ type Manager interface {
// ShowGrants shows granted privileges for user.
ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity) ([]string, error)

// GetEncodedPassword shows the encoded password for user.
GetEncodedPassword(user, host string) string

// RequestVerification verifies user privilege for the request.
// If table is "", only check global/db scope privileges.
// If table is not "", check global/db/table scope privileges.
@@ -77,6 +77,22 @@ func (p *UserPrivileges) RequestVerificationWithUser(db, table, column string, p
return mysqlPriv.RequestVerification(user.Username, user.Hostname, db, table, column, priv)
}

// GetEncodedPassword implements the Manager interface.
func (p *UserPrivileges) GetEncodedPassword(user, host string) string {
mysqlPriv := p.Handle.Get()
record := mysqlPriv.connectionVerification(user, host)
if record == nil {
log.Errorf("Get user privilege record fail: user %v, host %v", user, host)
return ""
}
pwd := record.Password
if len(pwd) != 0 && len(pwd) != mysql.PWDHashLen+1 {
log.Errorf("User [%s] password from SystemDB not like a sha1sum", user)
return ""
}
return pwd
}

// ConnectionVerification implements the Manager interface.
func (p *UserPrivileges) ConnectionVerification(user, host string, authentication, salt []byte) (u string, h string, success bool) {

@@ -456,6 +456,13 @@ func (s *testPrivilegeSuite) TestAdminCommand(c *C) {
c.Assert(err, IsNil)
}

func (s *testPrivilegeSuite) TestGetEncodedPassword(c *C) {
se := newSession(c, s.store, s.dbName)
mustExec(c, se, `CREATE USER 'test_encode_u'@'localhost' identified by 'root';`)
pc := privilege.GetPrivilegeManager(se)
c.Assert(pc.GetEncodedPassword("test_encode_u", "localhost"), Equals, "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B")
}

func mustExec(c *C, se session.Session, sql string) {
_, err := se.Execute(context.Background(), sql)
c.Assert(err, IsNil)
@@ -185,6 +185,17 @@ func (tk *TestKit) MustQuery(sql string, args ...interface{}) *Result {
return tk.ResultSetToResult(rs, comment)
}

// QueryToErr executes a sql statement and discard results.
func (tk *TestKit) QueryToErr(sql string, args ...interface{}) error {
comment := check.Commentf("sql:%s, args:%v", sql, args)
res, err := tk.Exec(sql, args...)
tk.c.Assert(errors.ErrorStack(err), check.Equals, "", comment)
tk.c.Assert(res, check.NotNil, comment)
_, resErr := session.GetRows4Test(context.Background(), tk.Se, res)
tk.c.Assert(res.Close(), check.IsNil)
return resErr
}

// ExecToErr executes a sql statement and discard results.
func (tk *TestKit) ExecToErr(sql string, args ...interface{}) error {
res, err := tk.Exec(sql, args...)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.