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

*: create a user using tidb_auth_token authentication #38585

Merged
merged 31 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7f6f17f
Create user with `tidb_auth_token`
CbcWestwolf Oct 21, 2022
d21fab3
Add test for create user
CbcWestwolf Oct 25, 2022
28b9548
Merge branch 'master' into tidb_auth_token
CbcWestwolf Oct 25, 2022
7be6f51
Fix UT
CbcWestwolf Oct 25, 2022
ca7a000
Support `token_require` and `token_issuer`
CbcWestwolf Oct 25, 2022
a1e3c5f
show create user
CbcWestwolf Oct 25, 2022
d559001
bazel
CbcWestwolf Oct 26, 2022
ab25f46
Fix UT
CbcWestwolf Oct 26, 2022
10476c2
Add more test
CbcWestwolf Oct 26, 2022
13a4b0f
Merge branch 'master' of github.com:pingcap/tidb into tidb_auth_token
CbcWestwolf Oct 26, 2022
c50c762
Update
CbcWestwolf Oct 26, 2022
241aea7
Add warning while specifying `token_require` for other auth
CbcWestwolf Oct 26, 2022
16a8ec5
Fix parser
CbcWestwolf Oct 26, 2022
a5adcf7
Fix
CbcWestwolf Oct 26, 2022
23156c9
Fix
CbcWestwolf Oct 26, 2022
a2b0cf9
Fix UT
CbcWestwolf Oct 26, 2022
13c7567
Merge branch 'master' into tidb_auth_token
CbcWestwolf Oct 26, 2022
536ec01
Add more warnings
CbcWestwolf Oct 26, 2022
dd8b19a
Merge TLSOption and AuthTokenOption into AuthTokenOrTLSOption
CbcWestwolf Oct 26, 2022
01d21df
Merge branch 'tidb_auth_token' of github.com:CbcWestwolf/tidb into ti…
CbcWestwolf Oct 26, 2022
ff20723
Update
CbcWestwolf Oct 26, 2022
b09987b
Merge branch 'tidb_auth_token' of github.com:CbcWestwolf/tidb into ti…
CbcWestwolf Oct 27, 2022
b3034f4
Merge branch 'master' of github.com:pingcap/tidb into tidb_auth_token
CbcWestwolf Oct 27, 2022
c39474b
Merge branch 'master' of github.com:pingcap/tidb into tidb_auth_token
CbcWestwolf Oct 28, 2022
50b54bd
Fix build
CbcWestwolf Oct 28, 2022
a758fb6
Update
CbcWestwolf Oct 28, 2022
daed722
Rename
CbcWestwolf Oct 28, 2022
b2f1d75
Apply suggestions from code review
CbcWestwolf Oct 28, 2022
5f3294d
Update
CbcWestwolf Oct 28, 2022
340811b
Update UT
CbcWestwolf Oct 28, 2022
ffc937c
Merge branch 'master' into tidb_auth_token
ti-chi-bot Oct 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions executor/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ go_library(
"analyze_idx.go",
"analyze_incremental.go",
"analyze_utils.go",
"analyze_worker.go",
"apply_cache.go",
"batch_checker.go",
"batch_point_get.go",
Expand Down
13 changes: 9 additions & 4 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,7 @@ func (e *ShowExec) fetchShowCollation() error {
return nil
}

// fetchShowCreateUser composes show create create user result.
// fetchShowCreateUser composes 'show create user' result.
func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
checker := privilege.GetPrivilegeManager(e.ctx)
if checker == nil {
Expand All @@ -1528,7 +1528,7 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {

exec := e.ctx.(sqlexec.RestrictedSQLExecutor)

rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT plugin, Account_locked, JSON_UNQUOTE(JSON_EXTRACT(user_attributes, '$.metadata'))
rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT plugin, Account_locked, JSON_UNQUOTE(JSON_EXTRACT(user_attributes, '$.metadata')), Token_issuer
FROM %n.%n WHERE User=%? AND Host=%?`,
mysql.SystemDB, mysql.UserTable, userName, strings.ToLower(hostName))
if err != nil {
Expand Down Expand Up @@ -1557,6 +1557,11 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
userAttributes = " ATTRIBUTE " + userAttributes
}

tokenIssuer := rows[0].GetString(3)
if len(tokenIssuer) > 0 {
tokenIssuer = " TOKEN_REQUIRE token_issuer " + tokenIssuer
}

rows, _, err = exec.ExecRestrictedSQL(ctx, nil, `SELECT Priv FROM %n.%n WHERE User=%? AND Host=%?`, mysql.SystemDB, mysql.GlobalPrivTable, userName, hostName)
if err != nil {
return errors.Trace(err)
Expand All @@ -1580,8 +1585,8 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
}

// FIXME: the returned string is not escaped safely
showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH '%s'%s REQUIRE %s PASSWORD EXPIRE DEFAULT ACCOUNT %s%s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, accountLocked, userAttributes)
showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH '%s'%s REQUIRE %s%s PASSWORD EXPIRE DEFAULT ACCOUNT %s%s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, tokenIssuer, accountLocked, userAttributes)
e.appendRow([]interface{}{showStr})
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions executor/showtest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,12 @@ func TestShowCreateUser(t *testing.T) {
tk.MustQuery("SHOW CREATE USER commentUser").Check(testkit.Rows(`CREATE USER 'commentUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"comment": "1234"}`))
tk.MustExec(`CREATE USER attributeUser attribute '{"name": "Tom", "age": 19}'`)
tk.MustQuery("SHOW CREATE USER attributeUser").Check(testkit.Rows(`CREATE USER 'attributeUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"age": 19, "name": "Tom"}`))

// Creating users with 'IDENTIFIED WITH 'tidb_auth_token''
tk.MustExec(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' ATTRIBUTE '{"email": "user@pingcap.com"}'`)
tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"email": "user@pingcap.com"}`))
tk.MustExec(`ALTER USER 'token_user'@'%' TOKEN_REQUIRE token_issuer 'issuer-ABC'`)
tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE TOKEN_REQUIRE token_issuer issuer-ABC PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"email": "user@pingcap.com"}`))
}

func TestUnprivilegedShow(t *testing.T) {
Expand Down
22 changes: 18 additions & 4 deletions executor/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -836,8 +836,16 @@ func (e *SimpleExec) executeCreateUser(ctx context.Context, s *ast.CreateUserStm
}
}

tokenIssuer := ""
for _, authTokenOption := range s.AuthTokenOptions {
switch authTokenOption.Type {
case ast.TokenIssuer:
tokenIssuer = authTokenOption.Value
}
}

sql := new(strings.Builder)
sqlexec.MustFormatSQL(sql, `INSERT INTO %n.%n (Host, User, authentication_string, plugin, user_attributes, Account_locked) VALUES `, mysql.SystemDB, mysql.UserTable)
sqlexec.MustFormatSQL(sql, `INSERT INTO %n.%n (Host, User, authentication_string, plugin, user_attributes, Account_locked, Token_issuer) VALUES `, mysql.SystemDB, mysql.UserTable)

users := make([]*auth.UserIdentity, 0, len(s.Specs))
for _, spec := range s.Specs {
Expand Down Expand Up @@ -877,13 +885,13 @@ func (e *SimpleExec) executeCreateUser(ctx context.Context, s *ast.CreateUserStm
}

switch authPlugin {
case mysql.AuthNativePassword, mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password, mysql.AuthSocket:
case mysql.AuthNativePassword, mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password, mysql.AuthSocket, mysql.AuthTiDBAuthToken:
default:
return ErrPluginIsNotLoaded.GenWithStackByArgs(spec.AuthOpt.AuthPlugin)
}

hostName := strings.ToLower(spec.User.Hostname)
sqlexec.MustFormatSQL(sql, `(%?, %?, %?, %?, %?, %?)`, hostName, spec.User.Username, pwd, authPlugin, userAttributes, lockAccount)
sqlexec.MustFormatSQL(sql, `(%?, %?, %?, %?, %?, %?, %?)`, hostName, spec.User.Username, pwd, authPlugin, userAttributes, lockAccount, tokenIssuer)
users = append(users, spec.User)
}
if len(users) == 0 {
Expand Down Expand Up @@ -1036,7 +1044,7 @@ func (e *SimpleExec) executeAlterUser(ctx context.Context, s *ast.AlterUserStmt)
spec.AuthOpt.AuthPlugin = authplugin
}
switch spec.AuthOpt.AuthPlugin {
case mysql.AuthNativePassword, mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password, mysql.AuthSocket, "":
case mysql.AuthNativePassword, mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password, mysql.AuthSocket, mysql.AuthTiDBAuthToken, "":
default:
return ErrPluginIsNotLoaded.GenWithStackByArgs(spec.AuthOpt.AuthPlugin)
}
Expand Down Expand Up @@ -1064,6 +1072,12 @@ func (e *SimpleExec) executeAlterUser(ctx context.Context, s *ast.AlterUserStmt)
fields = append(fields, alterField{"user_attributes=json_merge_patch(user_attributes, %?)", newAttributesStr})
}

if len(s.AuthTokenOptions) > 0 {
for _, authTokenOption := range s.AuthTokenOptions {
fields = append(fields, alterField{authTokenOption.Type.String() + "=%?", authTokenOption.Value})
}
}

if len(fields) > 0 {
sql := new(strings.Builder)
sqlexec.MustFormatSQL(sql, "UPDATE %n.%n SET ", mysql.SystemDB, mysql.UserTable)
Expand Down
4 changes: 4 additions & 0 deletions executor/simpletest/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,10 @@ func TestUser(t *testing.T) {
result.Check(testkit.Rows(auth.EncodePassword("")))
dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost' ;`
tk.MustExec(dropUserSQL)
tk.MustExec(`CREATE USER token_user IDENTIFIED WITH 'tidb_auth_token' TOKEN_REQUIRE token_issuer 'issuer-abc'`)
tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-abc"))
tk.MustExec(`ALTER USER token_user TOKEN_REQUIRE token_issuer 'issuer-123'`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it allowed to specify token_issuer for other auth plugins in create user or alter user statements?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the specified token_issuer is stored into mysql.user for all users. But it would not affect the authentication of the other auth plugin users

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, if a user is identified with mysql_native_password, and the root user alters his token_issuer, this is meaningless. Should we report an error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can give a warning when create user/alter user meet such a scenario.

tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-123"))

// Test alter user.
createUserSQL = `CREATE USER 'test1'@'localhost' IDENTIFIED BY '123', 'test2'@'localhost' IDENTIFIED BY '123', 'test3'@'localhost' IDENTIFIED BY '123', 'test4'@'localhost' IDENTIFIED BY '123';`
Expand Down
58 changes: 58 additions & 0 deletions parser/ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,37 @@ func (t *TLSOption) Restore(ctx *format.RestoreCtx) error {
return nil
}

const (
TokenIssuer AuthTokenOptionType = iota
)

type AuthTokenOptionType int

type AuthTokenOption struct {
Type AuthTokenOptionType
Value string
}

func (t *AuthTokenOption) Restore(ctx *format.RestoreCtx) error {
switch t.Type {
case TokenIssuer:
ctx.WriteKeyWord("TOKEN_ISSUER ")
ctx.WriteString(t.Value)
default:
return errors.Errorf("Unsupported AuthTokenOption.Type %d", t.Type)
}
return nil
}

func (t AuthTokenOptionType) String() string {
switch t {
case TokenIssuer:
return "TOKEN_ISSUER"
default:
return "UNKNOWN"
}
}

const (
MaxQueriesPerHour = iota + 1
MaxUpdatesPerHour
Expand Down Expand Up @@ -1523,6 +1554,7 @@ type CreateUserStmt struct {
IfNotExists bool
Specs []*UserSpec
TLSOptions []*TLSOption
AuthTokenOptions []*AuthTokenOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
CommentOrAttributeOption *CommentOrAttributeOption
Expand Down Expand Up @@ -1560,6 +1592,18 @@ func (n *CreateUserStmt) Restore(ctx *format.RestoreCtx) error {
}
}

if len(n.AuthTokenOptions) != 0 {
ctx.WriteKeyWord(" TOKEN_REQUIRE ")
}
for i, option := range n.AuthTokenOptions {
if i != 0 {
ctx.WriteKeyWord(" AND ")
}
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.AuthTokenOptions[%d]", i)
}
}

if len(n.ResourceOptions) != 0 {
ctx.WriteKeyWord(" WITH")
}
Expand Down Expand Up @@ -1617,6 +1661,7 @@ type AlterUserStmt struct {
CurrentAuth *AuthOption
Specs []*UserSpec
TLSOptions []*TLSOption
AuthTokenOptions []*AuthTokenOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
CommentOrAttributeOption *CommentOrAttributeOption
Expand Down Expand Up @@ -1657,6 +1702,19 @@ func (n *AlterUserStmt) Restore(ctx *format.RestoreCtx) error {
}
}

if len(n.AuthTokenOptions) != 0 {
ctx.WriteKeyWord(" TOKEN_REQUIRE ")
}

for i, option := range n.AuthTokenOptions {
if i != 0 {
ctx.WriteKeyWord(" AND ")
}
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.AuthTokenOptions[%d]", i)
}
}

if len(n.ResourceOptions) != 0 {
ctx.WriteKeyWord(" WITH")
}
Expand Down
2 changes: 2 additions & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@ var tokenMap = map[string]int{
"TINYTEXT": tinytextType,
"TLS": tls,
"TO": to,
"TOKEN_REQUIRE": tokenRequire,
"TOKEN_ISSUER": tokenIssuer,
"TOKUDB_DEFAULT": tokudbDefault,
"TOKUDB_FAST": tokudbFast,
"TOKUDB_LZMA": tokudbLzma,
Expand Down
2 changes: 2 additions & 0 deletions parser/mysql/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,10 @@ const (
AuthNativePassword = "mysql_native_password" // #nosec G101
AuthCachingSha2Password = "caching_sha2_password" // #nosec G101
AuthTiDBSM3Password = "tidb_sm3_password" // #nosec G101
AuthMySQLClearPassword = "mysql_clear_password"
AuthSocket = "auth_socket"
AuthTiDBSessionToken = "tidb_session_token"
AuthTiDBAuthToken = "tidb_auth_token"
)

// MySQL database and tables.
Expand Down
Loading