mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-28294: set default role bypasses Replicate_Wild_Ignore_Table: mysql.%
Problem: ======== When replicating SET DEFAULT ROLE, the pre-update check (i.e. that in set_var_default_role::check()) tries to validate the existence of the given rules/user even when the targeted tables are ignored. When previously issued CREATE USER/ROLE commands are ignored by the replica because of the replication filtering rules, this results in an error because the targeted data does not exist. Solution: ======== Before checking that the given roles/user exist of a SET DEFAULT ROLE command, first ensure that the mysql.user and mysql.roles_mapping tables are not excluded by replication filters. Reviewed By: ============ Andrei Elkin <andrei.elkin@mariadb.com> Sergei Golubchik <serg@mariadb.com>
This commit is contained in:
committed by
Andrei
parent
c7f8cfc9e7
commit
8963d64ee8
@@ -0,0 +1,55 @@
|
|||||||
|
include/master-slave.inc
|
||||||
|
[connection master]
|
||||||
|
#
|
||||||
|
# Set replica to ignore system mysql tables
|
||||||
|
connection slave;
|
||||||
|
include/stop_slave.inc
|
||||||
|
SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
|
||||||
|
include/start_slave.inc
|
||||||
|
#
|
||||||
|
# Execute grant-based commands on primary which modify mysql system
|
||||||
|
# tables
|
||||||
|
connection master;
|
||||||
|
CREATE ROLE journalist;
|
||||||
|
CREATE USER testuser@localhost IDENTIFIED by '';
|
||||||
|
GRANT journalist to testuser@localhost;
|
||||||
|
#
|
||||||
|
# Execute SET commands which use the previous user/role data
|
||||||
|
SET DEFAULT ROLE journalist for testuser@localhost;
|
||||||
|
SET PASSWORD for testuser@localhost= PASSWORD('123');
|
||||||
|
include/save_master_gtid.inc
|
||||||
|
#
|
||||||
|
# Verify primary's grant tables have the correct user/role data
|
||||||
|
select count(*)=1 from mysql.user where User='testuser';
|
||||||
|
count(*)=1
|
||||||
|
1
|
||||||
|
select count(*)=1 from mysql.roles_mapping where User='testuser';
|
||||||
|
count(*)=1
|
||||||
|
1
|
||||||
|
#
|
||||||
|
# Ensure that the replica receives all of the primary's events without
|
||||||
|
# error
|
||||||
|
connection slave;
|
||||||
|
include/sync_with_master_gtid.inc
|
||||||
|
Last_SQL_Error =
|
||||||
|
Last_SQL_Errno = 0
|
||||||
|
#
|
||||||
|
# Verify that the replica did not execute the master's commands
|
||||||
|
select count(*)=0 from mysql.user where User='testuser';
|
||||||
|
count(*)=0
|
||||||
|
1
|
||||||
|
select count(*)=0 from mysql.roles_mapping where User='testuser';
|
||||||
|
count(*)=0
|
||||||
|
1
|
||||||
|
#
|
||||||
|
# Clean up
|
||||||
|
connection master;
|
||||||
|
DROP ROLE journalist;
|
||||||
|
DROP USER testuser@localhost;
|
||||||
|
include/save_master_gtid.inc
|
||||||
|
connection slave;
|
||||||
|
include/sync_with_master_gtid.inc
|
||||||
|
include/stop_slave.inc
|
||||||
|
SET @@GLOBAL.replicate_wild_ignore_table="";
|
||||||
|
include/start_slave.inc
|
||||||
|
include/rpl_end.inc
|
83
mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
Normal file
83
mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# This test ensures that the SET DEFAULT ROLE and SET PASSWORD commands can
|
||||||
|
# be ignored by replica filter rules. MDEV-28294 exposed a bug in which
|
||||||
|
# SET DEFAULT ROLE would check for the existence of the given roles/user even
|
||||||
|
# when the targeted tables are ignored, resulting in errors if the targeted
|
||||||
|
# data does not exist. More specifically, when previously issued
|
||||||
|
# CREATE USER/ROLE commands are ignored by the replica because of the
|
||||||
|
# replication filtering rules, SET DEFAULT ROLE would result in an error
|
||||||
|
# because the targeted data does not exist.
|
||||||
|
#
|
||||||
|
# Methodology:
|
||||||
|
# Using a replica configured with replicate_wild_ignore_table="mysql.%",
|
||||||
|
# execute SET DEFAULT ROLE and SET PASSWORD on the primary and ensure that the
|
||||||
|
# replica neither errors nor executes the commands which the primary sends.
|
||||||
|
#
|
||||||
|
# References:
|
||||||
|
# MDEV-28294: set default role bypasses Replicate_Wild_Ignore_Table: mysql.%
|
||||||
|
#
|
||||||
|
|
||||||
|
source include/master-slave.inc;
|
||||||
|
source include/have_binlog_format_mixed.inc;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Set replica to ignore system mysql tables
|
||||||
|
connection slave;
|
||||||
|
let $old_filter= query_get_value(SHOW SLAVE STATUS, Replicate_Wild_Ignore_Table, 1);
|
||||||
|
source include/stop_slave.inc;
|
||||||
|
SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
|
||||||
|
source include/start_slave.inc;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Execute grant-based commands on primary which modify mysql system
|
||||||
|
--echo # tables
|
||||||
|
connection master;
|
||||||
|
CREATE ROLE journalist;
|
||||||
|
CREATE USER testuser@localhost IDENTIFIED by '';
|
||||||
|
GRANT journalist to testuser@localhost;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Execute SET commands which use the previous user/role data
|
||||||
|
SET DEFAULT ROLE journalist for testuser@localhost;
|
||||||
|
SET PASSWORD for testuser@localhost= PASSWORD('123');
|
||||||
|
--source include/save_master_gtid.inc
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Verify primary's grant tables have the correct user/role data
|
||||||
|
select count(*)=1 from mysql.user where User='testuser';
|
||||||
|
select count(*)=1 from mysql.roles_mapping where User='testuser';
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Ensure that the replica receives all of the primary's events without
|
||||||
|
--echo # error
|
||||||
|
connection slave;
|
||||||
|
--source include/sync_with_master_gtid.inc
|
||||||
|
let $error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error, 1);
|
||||||
|
--echo Last_SQL_Error = $error
|
||||||
|
let $errno= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
|
||||||
|
--echo Last_SQL_Errno = $errno
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Verify that the replica did not execute the master's commands
|
||||||
|
select count(*)=0 from mysql.user where User='testuser';
|
||||||
|
select count(*)=0 from mysql.roles_mapping where User='testuser';
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Clean up
|
||||||
|
|
||||||
|
# The master has to drop the role/user combination while the slave still has
|
||||||
|
# its filters active; otherwise, the slave would try to drop users/roles that
|
||||||
|
# were never replicated.
|
||||||
|
--connection master
|
||||||
|
DROP ROLE journalist;
|
||||||
|
DROP USER testuser@localhost;
|
||||||
|
--source include/save_master_gtid.inc
|
||||||
|
|
||||||
|
--connection slave
|
||||||
|
--source include/sync_with_master_gtid.inc
|
||||||
|
source include/stop_slave.inc;
|
||||||
|
--eval SET @@GLOBAL.replicate_wild_ignore_table="$old_filter"
|
||||||
|
source include/start_slave.inc;
|
||||||
|
|
||||||
|
--source include/rpl_end.inc
|
@@ -1249,18 +1249,8 @@ class Grant_tables
|
|||||||
DBUG_ENTER("Grant_tables::open_and_lock");
|
DBUG_ENTER("Grant_tables::open_and_lock");
|
||||||
DBUG_ASSERT(first_table_in_list);
|
DBUG_ASSERT(first_table_in_list);
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
|
if (int ignore_ret= rpl_ignore_tables(thd))
|
||||||
thd->slave_thread && !thd->spcont)
|
DBUG_RETURN(ignore_ret);
|
||||||
{
|
|
||||||
/*
|
|
||||||
GRANT and REVOKE are applied the slave in/exclusion rules as they are
|
|
||||||
some kind of updates to the mysql.% tables.
|
|
||||||
*/
|
|
||||||
Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
|
|
||||||
if (rpl_filter->is_on() &&
|
|
||||||
!rpl_filter->tables_ok(0, &first_table_in_list->tl))
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
|
if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
|
||||||
MYSQL_LOCK_IGNORE_TIMEOUT))
|
MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||||
@@ -1290,6 +1280,32 @@ class Grant_tables
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_REPLICATION
|
||||||
|
/* Checks if the tables targeted by a grant command should be ignored because
|
||||||
|
of the configured replication filters
|
||||||
|
|
||||||
|
@retval 1 Tables are excluded for replication
|
||||||
|
@retval 0 tables are included for replication
|
||||||
|
*/
|
||||||
|
int rpl_ignore_tables(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Grant_tables::rpl_ignore_tables");
|
||||||
|
if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
|
||||||
|
thd->slave_thread && !thd->spcont)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
GRANT and REVOKE are applied the slave in/exclusion rules as they are
|
||||||
|
some kind of updates to the mysql.% tables.
|
||||||
|
*/
|
||||||
|
Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
|
||||||
|
if (rpl_filter->is_on() &&
|
||||||
|
!rpl_filter->tables_ok(0, &first_table_in_list->tl))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline const User_table& user_table() const
|
inline const User_table& user_table() const
|
||||||
{
|
{
|
||||||
return m_user_table;
|
return m_user_table;
|
||||||
@@ -3469,6 +3485,16 @@ int acl_check_set_default_role(THD *thd, const char *host, const char *user,
|
|||||||
const char *role)
|
const char *role)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("acl_check_set_default_role");
|
DBUG_ENTER("acl_check_set_default_role");
|
||||||
|
#ifdef HAVE_REPLICATION
|
||||||
|
/*
|
||||||
|
If the roles_mapping table is excluded by the replication filter, we return
|
||||||
|
successful without validating the user/role data because the command will
|
||||||
|
be ignored in a later call to `acl_set_default_role()` for a graceful exit.
|
||||||
|
*/
|
||||||
|
Grant_tables tables(Table_roles_mapping, TL_WRITE);
|
||||||
|
if (tables.rpl_ignore_tables(thd))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
#endif
|
||||||
DBUG_RETURN(check_alter_user(thd, host, user) ||
|
DBUG_RETURN(check_alter_user(thd, host, user) ||
|
||||||
check_user_can_set_role(thd, user, host, NULL, role, NULL));
|
check_user_can_set_role(thd, user, host, NULL, role, NULL));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user