mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-23729 MDEV-32218 INFORMATION_SCHEMA table for user data
* A new table INFORMATION_SCHEMA.USERS is introduced. * It stores auxiliary user data * An unprivileged user can access their own data, and that is the main difference with what mysql.global_priv provides * The fields are currently: USER, PASSWORD_ERRORS, PASSWORD_EXPIRATION_TIME * If password_errors is ignored for the user, PASSWORD_ERRORS is NULL * PASSWORD_EXPIRATION_TIME is a timestamp with exact point in time, calculated from password_last_changed and password_lifetime (i.e. days) stored for the user
This commit is contained in:
@ -83,3 +83,86 @@ TABLE_SCHEMA TABLE_NAME INDEX_NAME ROWS_READ QUERIES
|
|||||||
select * from information_schema.table_statistics where table_schema='test' and table_name='just_a_test';
|
select * from information_schema.table_statistics where table_schema='test' and table_name='just_a_test';
|
||||||
TABLE_SCHEMA TABLE_NAME ROWS_READ ROWS_CHANGED ROWS_CHANGED_X_INDEXES ROWS_INSERTED ROWS_UPDATED ROWS_DELETED KEY_READ_HITS KEY_READ_MISSES
|
TABLE_SCHEMA TABLE_NAME ROWS_READ ROWS_CHANGED ROWS_CHANGED_X_INDEXES ROWS_INSERTED ROWS_UPDATED ROWS_DELETED KEY_READ_HITS KEY_READ_MISSES
|
||||||
set global userstat=@save_userstat;
|
set global userstat=@save_userstat;
|
||||||
|
#
|
||||||
|
# MDEV-23729 INFORMATION_SCHEMA Table info. about user locked due to
|
||||||
|
# max_password_errors
|
||||||
|
#
|
||||||
|
# MDEV-32218 message to notify end-user N-days prior the password get
|
||||||
|
# expired
|
||||||
|
#
|
||||||
|
set @old_max_password_errors=@@max_password_errors;
|
||||||
|
set global max_password_errors=2;
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'root'@'neo' 0 NULL
|
||||||
|
set timestamp= 123;
|
||||||
|
create user nice_user;
|
||||||
|
create user naughty_user identified by 'naughty_user_passwd';
|
||||||
|
alter user naughty_user password expire interval 10 day;
|
||||||
|
select 3600*24;
|
||||||
|
3600*24
|
||||||
|
86400
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'naughty_user'@'%' 0 864123
|
||||||
|
'nice_user'@'%' 0 NULL
|
||||||
|
'root'@HOSTNAME 0 NULL
|
||||||
|
alter user nice_user password expire interval 10 day;
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'naughty_user'@'%' 0 864123
|
||||||
|
'nice_user'@'%' 0 864123
|
||||||
|
'root'@HOSTNAME 0 NULL
|
||||||
|
connect(localhost,naughty_user,wrong_passwd,test,MASTER_PORT,MASTER_SOCKET);
|
||||||
|
connect con1, localhost, naughty_user, wrong_passwd;
|
||||||
|
ERROR 28000: Access denied for user 'naughty_user'@'localhost' (using password: YES)
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'naughty_user'@'%' 1 864123
|
||||||
|
'nice_user'@'%' 0 864123
|
||||||
|
'root'@HOSTNAME 0 NULL
|
||||||
|
connect(localhost,naughty_user,wrong_passwd,test,MASTER_PORT,MASTER_SOCKET);
|
||||||
|
connect con1, localhost, naughty_user, wrong_passwd;
|
||||||
|
ERROR 28000: Access denied for user 'naughty_user'@'localhost' (using password: YES)
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'naughty_user'@'%' 2 864123
|
||||||
|
'nice_user'@'%' 0 864123
|
||||||
|
'root'@HOSTNAME 0 NULL
|
||||||
|
# Show all users that are blocked due to max_password_errors reached.
|
||||||
|
select user from information_schema.users
|
||||||
|
where password_errors >= @@global.max_password_errors;
|
||||||
|
user
|
||||||
|
'naughty_user'@'%'
|
||||||
|
set global max_password_errors=3;
|
||||||
|
connect con1, localhost, naughty_user, naughty_user_passwd;
|
||||||
|
connection default;
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'mariadb.sys'@'localhost' 0 NULL
|
||||||
|
'naughty_user'@'%' 0 864123
|
||||||
|
'nice_user'@'%' 0 864123
|
||||||
|
'root'@HOSTNAME 0 NULL
|
||||||
|
disconnect con1;
|
||||||
|
# Test unprivileged output
|
||||||
|
connect con2, localhost, nice_user;
|
||||||
|
set timestamp= 123;
|
||||||
|
set password= password('nice_passwd');
|
||||||
|
select * from information_schema.users;
|
||||||
|
USER PASSWORD_ERRORS PASSWORD_EXPIRATION_TIME
|
||||||
|
'nice_user'@'%' 0 864123
|
||||||
|
# Delete user while some connection is still alive, then select.
|
||||||
|
connection default;
|
||||||
|
drop user nice_user;
|
||||||
|
connection con2;
|
||||||
|
select * from information_schema.users;
|
||||||
|
ERROR 0L000: The current user is invalid
|
||||||
|
disconnect con2;
|
||||||
|
connection default;
|
||||||
|
drop user naughty_user;
|
||||||
|
set global max_password_errors=@old_max_password_errors;
|
||||||
|
@ -54,3 +54,89 @@ select * from information_schema.index_statistics where table_schema='test' and
|
|||||||
select * from information_schema.table_statistics where table_schema='test' and table_name='just_a_test';
|
select * from information_schema.table_statistics where table_schema='test' and table_name='just_a_test';
|
||||||
set global userstat=@save_userstat;
|
set global userstat=@save_userstat;
|
||||||
--enable_ps2_protocol
|
--enable_ps2_protocol
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-23729 INFORMATION_SCHEMA Table info. about user locked due to
|
||||||
|
--echo # max_password_errors
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-32218 message to notify end-user N-days prior the password get
|
||||||
|
--echo # expired
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
set @old_max_password_errors=@@max_password_errors;
|
||||||
|
set global max_password_errors=2;
|
||||||
|
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
let $hostname= `select concat('@\'', @@hostname, '\'')`;
|
||||||
|
# set the password_last_changed value
|
||||||
|
set timestamp= 123;
|
||||||
|
|
||||||
|
create user nice_user;
|
||||||
|
create user naughty_user identified by 'naughty_user_passwd';
|
||||||
|
|
||||||
|
alter user naughty_user password expire interval 10 day;
|
||||||
|
|
||||||
|
select 3600*24;
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
eval select * from information_schema.users;
|
||||||
|
|
||||||
|
alter user nice_user password expire interval 10 day;
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
|
||||||
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
|
connect(con1, localhost, naughty_user, wrong_passwd);
|
||||||
|
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
|
||||||
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
|
connect(con1, localhost, naughty_user, wrong_passwd);
|
||||||
|
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
|
||||||
|
--echo # Show all users that are blocked due to max_password_errors reached.
|
||||||
|
select user from information_schema.users
|
||||||
|
where password_errors >= @@global.max_password_errors;
|
||||||
|
|
||||||
|
|
||||||
|
set global max_password_errors=3;
|
||||||
|
|
||||||
|
connect(con1, localhost, naughty_user, naughty_user_passwd);
|
||||||
|
connection default;
|
||||||
|
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
select * from information_schema.users;
|
||||||
|
disconnect con1;
|
||||||
|
|
||||||
|
--echo # Test unprivileged output
|
||||||
|
|
||||||
|
connect(con2, localhost, nice_user);
|
||||||
|
set timestamp= 123;
|
||||||
|
# timestamp was normal at the login moment, so the password was expired
|
||||||
|
set password= password('nice_passwd');
|
||||||
|
|
||||||
|
--replace_result $hostname @HOSTNAME
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
--echo # Delete user while some connection is still alive, then select.
|
||||||
|
connection default;
|
||||||
|
drop user nice_user;
|
||||||
|
connection con2;
|
||||||
|
# and here you are, select from your table
|
||||||
|
--error ER_INVALID_CURRENT_USER
|
||||||
|
select * from information_schema.users;
|
||||||
|
|
||||||
|
disconnect con2;
|
||||||
|
connection default;
|
||||||
|
drop user naughty_user;
|
||||||
|
set global max_password_errors=@old_max_password_errors;
|
||||||
|
|
||||||
|
#
|
||||||
|
# End of 11.5 tests
|
||||||
|
#
|
||||||
|
@ -1110,6 +1110,7 @@ enum enum_schema_tables
|
|||||||
SCH_TABLE_NAMES,
|
SCH_TABLE_NAMES,
|
||||||
SCH_TABLE_PRIVILEGES,
|
SCH_TABLE_PRIVILEGES,
|
||||||
SCH_TRIGGERS,
|
SCH_TRIGGERS,
|
||||||
|
SCH_USERS,
|
||||||
SCH_USER_PRIVILEGES,
|
SCH_USER_PRIVILEGES,
|
||||||
SCH_VIEWS,
|
SCH_VIEWS,
|
||||||
SCH_ENUM_SIZE
|
SCH_ENUM_SIZE
|
||||||
|
@ -13017,6 +13017,95 @@ err:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Show
|
||||||
|
{
|
||||||
|
ST_FIELD_INFO users_fields_info[] =
|
||||||
|
{
|
||||||
|
Column("USER", Userhost(), NOT_NULL),
|
||||||
|
Column("PASSWORD_ERRORS", SLonglong(), NULLABLE),
|
||||||
|
Column("PASSWORD_EXPIRATION_TIME", SLonglong(), NULLABLE),
|
||||||
|
CEnd()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool ignore_max_password_errors(const ACL_USER *acl_user);
|
||||||
|
|
||||||
|
static int fill_users_schema_record(THD *thd, TABLE * table, ACL_USER *user)
|
||||||
|
{
|
||||||
|
ulonglong lifetime= user->password_lifetime < 0
|
||||||
|
? default_password_lifetime
|
||||||
|
: user->password_lifetime;
|
||||||
|
|
||||||
|
bool ignore_password_errors= ignore_max_password_errors(user);
|
||||||
|
bool ignore_expiration_date= lifetime == 0;
|
||||||
|
|
||||||
|
/* Skip user if nothing to show */
|
||||||
|
if (ignore_password_errors && ignore_expiration_date)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Grantee_str grantee(user->user,
|
||||||
|
Lex_cstring_strlen(safe_str(user->host.hostname)));
|
||||||
|
table->field[0]->store(grantee, strlen(grantee), system_charset_info);
|
||||||
|
if (ignore_password_errors)
|
||||||
|
{
|
||||||
|
table->field[1]->set_null();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table->field[1]->set_notnull();
|
||||||
|
table->field[1]->store(user->password_errors);
|
||||||
|
}
|
||||||
|
if (ignore_expiration_date)
|
||||||
|
{
|
||||||
|
table->field[2]->set_null();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table->field[2]->set_notnull();
|
||||||
|
table->field[2]->store(user->password_last_changed
|
||||||
|
+ user->password_lifetime * 3600 * 24, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema_table_store_record(thd, table);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fill_users_schema_table(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||||
|
{
|
||||||
|
int res= 0;
|
||||||
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
bool see_whole_table= check_access(thd, SELECT_ACL, "mysql", NULL, NULL,
|
||||||
|
true, true) == 0;
|
||||||
|
TABLE *table= tables->table;
|
||||||
|
|
||||||
|
if (!see_whole_table)
|
||||||
|
{
|
||||||
|
Security_context *sctx= thd->security_ctx;
|
||||||
|
mysql_mutex_lock(&acl_cache->lock);
|
||||||
|
ACL_USER *cur_user= find_user_exact(Lex_cstring_strlen(sctx->priv_host),
|
||||||
|
Lex_cstring_strlen(sctx->priv_user));
|
||||||
|
if (!cur_user)
|
||||||
|
{
|
||||||
|
mysql_mutex_unlock(&acl_cache->lock);
|
||||||
|
my_error(ER_INVALID_CURRENT_USER, MYF(0));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res= fill_users_schema_record(thd, table, cur_user);
|
||||||
|
mysql_mutex_unlock(&acl_cache->lock);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_mutex_lock(&acl_cache->lock);
|
||||||
|
for (size_t i= 0; res == 0 && i < acl_users.elements; i++)
|
||||||
|
{
|
||||||
|
ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER*);
|
||||||
|
res= fill_users_schema_record(thd, table, user);
|
||||||
|
}
|
||||||
|
mysql_mutex_unlock(&acl_cache->lock);
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
/*
|
/*
|
||||||
|
@ -150,6 +150,7 @@ bool check_routine_level_acl(THD *thd, privilege_t acl,
|
|||||||
const char *db, const char *name,
|
const char *db, const char *name,
|
||||||
const Sp_handler *sph);
|
const Sp_handler *sph);
|
||||||
bool is_acl_user(const LEX_CSTRING &host, const LEX_CSTRING &user);
|
bool is_acl_user(const LEX_CSTRING &host, const LEX_CSTRING &user);
|
||||||
|
int fill_users_schema_table(THD *thd, TABLE_LIST *tables, COND *cond);
|
||||||
int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
||||||
int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
||||||
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
||||||
|
@ -10243,6 +10243,7 @@ ST_FIELD_INFO files_fields_info[]=
|
|||||||
CEnd()
|
CEnd()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern ST_FIELD_INFO users_fields_info[];
|
||||||
}; // namespace Show
|
}; // namespace Show
|
||||||
|
|
||||||
|
|
||||||
@ -10556,6 +10557,8 @@ ST_SCHEMA_TABLE schema_tables[]=
|
|||||||
{"TRIGGERS"_Lex_ident_i_s_table, Show::triggers_fields_info, 0,
|
{"TRIGGERS"_Lex_ident_i_s_table, Show::triggers_fields_info, 0,
|
||||||
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
|
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
|
||||||
OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
|
OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
|
||||||
|
{"USERS"_Lex_ident_i_s_table, Show::users_fields_info, 0, fill_users_schema_table,
|
||||||
|
0, 0, -1, -1, 0, 0},
|
||||||
{"USER_PRIVILEGES"_Lex_ident_i_s_table,
|
{"USER_PRIVILEGES"_Lex_ident_i_s_table,
|
||||||
Show::user_privileges_fields_info, 0,
|
Show::user_privileges_fields_info, 0,
|
||||||
fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
|
fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
|
||||||
|
Reference in New Issue
Block a user