Skip to content

Commit

Permalink
*: support password expiration policy (pingcap#39035)
Browse files Browse the repository at this point in the history
  • Loading branch information
CbcWestwolf authored and ti-chi-bot committed Dec 12, 2022
1 parent 4f93081 commit eb13203
Show file tree
Hide file tree
Showing 37 changed files with 6,494 additions and 5,949 deletions.
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ type Security struct {
AuthTokenJWKS string `toml:"auth-token-jwks" json:"auth-token-jwks"`
// The refresh time interval of JWKS
AuthTokenRefreshInterval string `toml:"auth-token-refresh-interval" json:"auth-token-refresh-interval"`
// Disconnect directly when the password is expired
DisconnectOnExpiredPassword bool `toml:"disconnect-on-expired-password" json:"disconnect-on-expired-password"`
}

// The ErrConfigValidationFailed error is used so that external callers can do a type assertion
Expand Down Expand Up @@ -975,6 +977,7 @@ var defaultConf = Config{
RSAKeySize: 4096,
AuthTokenJWKS: "",
AuthTokenRefreshInterval: DefAuthTokenRefreshInterval.String(),
DisconnectOnExpiredPassword: true,
},
DeprecateIntegerDisplayWidth: false,
EnableEnumLengthLimit: true,
Expand Down
1 change: 1 addition & 0 deletions errno/errcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ const (
ErrMaxExecTimeExceeded = 1907
ErrForeignKeyCascadeDepthExceeded = 3008
ErrInvalidFieldSize = 3013
ErrPasswordExpireAnonymousUser = 3016
ErrInvalidArgumentForLogarithm = 3020
ErrAggregateOrderNonAggQuery = 3029
ErrUserLockWrongName = 3057
Expand Down
3 changes: 2 additions & 1 deletion errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrInnodbIndexCorrupt: mysql.Message("Index corrupt: %s", nil),
ErrInvalidYearColumnLength: mysql.Message("Supports only YEAR or YEAR(4) column", nil),
ErrNotValidPassword: mysql.Message("Your password does not satisfy the current policy requirements", nil),
ErrMustChangePassword: mysql.Message("You must SET PASSWORD before executing this statement", nil),
ErrMustChangePassword: mysql.Message("You must reset your password using ALTER USER statement before executing this statement", nil),
ErrFkNoIndexChild: mysql.Message("Failed to add the foreign key constraint. Missing index for constraint '%s' in the foreign table '%s'", nil),
ErrForeignKeyNoIndexInParent: mysql.Message("Failed to add the foreign key constraint. Missing index for constraint '%s' in the referenced table '%s'", nil),
ErrFkFailAddSystem: mysql.Message("Failed to add the foreign key constraint '%s' to system tables", nil),
Expand Down Expand Up @@ -840,6 +840,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrUnresolvedHintName: mysql.Message("Unresolved name '%s' for %s hint", nil),
ErrForeignKeyCascadeDepthExceeded: mysql.Message("Foreign key cascade delete/update exceeds max depth of %v.", nil),
ErrInvalidFieldSize: mysql.Message("Invalid size for column '%s'.", nil),
ErrPasswordExpireAnonymousUser: mysql.Message("The password for anonymous user cannot be expired.", nil),
ErrInvalidArgumentForLogarithm: mysql.Message("Invalid argument for logarithm", nil),
ErrAggregateOrderNonAggQuery: mysql.Message("Expression #%d of ORDER BY contains aggregate function and applies to the result of a non-aggregated query", nil),
ErrIncorrectType: mysql.Message("Incorrect type for argument %s in function %s.", nil),
Expand Down
15 changes: 15 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,11 @@ error = '''
Foreign key cascade delete/update exceeds max depth of %v.
'''

["executor:3016"]
error = '''
The password for anonymous user cannot be expired.
'''

["executor:3523"]
error = '''
Unknown authorization ID %.256s
Expand Down Expand Up @@ -2266,6 +2271,11 @@ error = '''
There is no such grant defined for user '%-.48s' on host '%-.255s'
'''

["privilege:1862"]
error = '''
Your password has expired. To log in you must change it using a client that supports expired passwords.
'''

["privilege:3530"]
error = '''
%s is not granted to %s
Expand Down Expand Up @@ -2761,6 +2771,11 @@ error = '''
Datetime function: %-.32s field overflow
'''

["types:1525"]
error = '''
Incorrect %-.32s value: '%-.128s'
'''

["types:1690"]
error = '''
%s value is out of range in '%s'
Expand Down
2 changes: 2 additions & 0 deletions executor/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ var (
ErrFuncNotEnabled = dbterror.ClassExecutor.NewStdErr(mysql.ErrNotSupportedYet, parser_mysql.Message("%-.32s is not supported. To enable this experimental feature, set '%-.32s' in the configuration file.", nil))
errSavepointNotExists = dbterror.ClassExecutor.NewStd(mysql.ErrSpDoesNotExist)
ErrForeignKeyCascadeDepthExceeded = dbterror.ClassExecutor.NewStd(mysql.ErrForeignKeyCascadeDepthExceeded)
ErrPasswordExpireAnonymousUser = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordExpireAnonymousUser)
errMustChangePassword = dbterror.ClassExecutor.NewStd(mysql.ErrMustChangePassword)

ErrWrongStringLength = dbterror.ClassDDL.NewStd(mysql.ErrWrongStringLength)
errUnsupportedFlashbackTmpTable = dbterror.ClassDDL.NewStdErr(mysql.ErrUnsupportedDDLOperation, parser_mysql.Message("Recover/flashback table is not supported on temporary tables", nil))
Expand Down
23 changes: 19 additions & 4 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -1512,8 +1512,9 @@ 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')), Token_issuer,
Password_reuse_history, Password_reuse_time FROM %n.%n WHERE User=%? AND Host=%?`,
rows, _, err := exec.ExecRestrictedSQL(ctx, nil,
`SELECT plugin, Account_locked, JSON_UNQUOTE(JSON_EXTRACT(user_attributes, '$.metadata')), Token_issuer, Password_reuse_history, Password_reuse_time, Password_expired, Password_lifetime
FROM %n.%n WHERE User=%? AND Host=%?`,
mysql.SystemDB, mysql.UserTable, userName, strings.ToLower(hostName))
if err != nil {
return errors.Trace(err)
Expand Down Expand Up @@ -1560,6 +1561,20 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
passwordReuseInterval = strconv.FormatUint(rows[0].GetUint64(5), 10) + " DAY"
}

passwordExpired := rows[0].GetEnum(6).String()
passwordLifetime := int64(-1)
if !rows[0].IsNull(7) {
passwordLifetime = rows[0].GetInt64(7)
}
passwordExpiredStr := "PASSWORD EXPIRE DEFAULT"
if passwordExpired == "Y" {
passwordExpiredStr = "PASSWORD EXPIRE"
} else if passwordLifetime == 0 {
passwordExpiredStr = "PASSWORD EXPIRE NEVER"
} else if passwordLifetime > 0 {
passwordExpiredStr = fmt.Sprintf("PASSWORD EXPIRE INTERVAL %d DAY", passwordLifetime)
}

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 @@ -1583,8 +1598,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%s PASSWORD EXPIRE DEFAULT ACCOUNT %s%s PASSWORD HISTORY %s PASSWORD REUSE INTERVAL %s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, tokenIssuer, accountLocked, userAttributes, passwordHistory, passwordReuseInterval)
showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH '%s'%s REQUIRE %s%s %s ACCOUNT %s%s PASSWORD HISTORY %s PASSWORD REUSE INTERVAL %s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, tokenIssuer, passwordExpiredStr, accountLocked, userAttributes, passwordHistory, passwordReuseInterval)
e.appendRow([]interface{}{showStr})
return nil
}
Expand Down
9 changes: 9 additions & 0 deletions executor/showtest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,15 @@ func TestShowCreateUser(t *testing.T) {
tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 3 DAY`))
tk.MustExec(`ALTER USER 'reuse_user'@'%' PASSWORD REUSE INTERVAL 31 DAY`)
tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 31 DAY`))

tk.MustExec("CREATE USER 'jeffrey1'@'localhost' PASSWORD EXPIRE")
tk.MustQuery("SHOW CREATE USER 'jeffrey1'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey1'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE ACCOUNT UNLOCK PASSWORD HISTORY DEFALUT PASSWORD REUSE INTERVAL DEFALUT`))
tk.MustExec("CREATE USER 'jeffrey2'@'localhost' PASSWORD EXPIRE DEFAULT")
tk.MustQuery("SHOW CREATE USER 'jeffrey2'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey2'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFALUT PASSWORD REUSE INTERVAL DEFALUT`))
tk.MustExec("CREATE USER 'jeffrey3'@'localhost' PASSWORD EXPIRE NEVER")
tk.MustQuery("SHOW CREATE USER 'jeffrey3'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey3'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE NEVER ACCOUNT UNLOCK PASSWORD HISTORY DEFALUT PASSWORD REUSE INTERVAL DEFALUT`))
tk.MustExec("CREATE USER 'jeffrey4'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY")
tk.MustQuery("SHOW CREATE USER 'jeffrey4'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey4'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE INTERVAL 180 DAY ACCOUNT UNLOCK PASSWORD HISTORY DEFALUT PASSWORD REUSE INTERVAL DEFALUT`))
}

func TestUnprivilegedShow(t *testing.T) {
Expand Down
Loading

0 comments on commit eb13203

Please sign in to comment.