diff --git a/mysql-test/suite/binlog_gtid/r/rpl_repl_slave_set_gtid_next.result b/mysql-test/suite/binlog_gtid/r/rpl_repl_slave_set_gtid_next.result new file mode 100644 index 000000000000..a9a73de6a5b9 --- /dev/null +++ b/mysql-test/suite/binlog_gtid/r/rpl_repl_slave_set_gtid_next.result @@ -0,0 +1,20 @@ +create user 'olm'@'localhost' IDENTIFIED BY 'password'; +grant all privileges on *.* to 'olm'@'localhost'; +revoke super on *.* from 'olm'@'localhost'; +Warnings: +Warning 1287 The SUPER privilege identifier is deprecated +create user 'normal'@'localhost' IDENTIFIED BY 'password'; +grant all privileges on *.* to 'normal'@'localhost'; +revoke super on *.* from 'normal'@'localhost'; +Warnings: +Warning 1287 The SUPER privilege identifier is deprecated +revoke BINLOG_ADMIN on *.* from 'normal'@'localhost'; +alter database test read_only=True; +# Without BINLOG_ADMIN, non-super user can't execute set gtid_next when replaying binlog +select * from test.tbl; +ERROR 42S02: Table 'test.tbl' doesn't exist +# With BINLOG_ADMIN, non-super user can execute set gtid_next when replaying binlog +include/assert.inc [tbl should have 1 row] +DROP user 'olm'@'localhost'; +DROP user 'normal'@'localhost'; +DROP table tbl; diff --git a/mysql-test/suite/binlog_gtid/t/rpl_repl_slave_set_gtid_next.test b/mysql-test/suite/binlog_gtid/t/rpl_repl_slave_set_gtid_next.test new file mode 100644 index 000000000000..b2064d6b4e59 --- /dev/null +++ b/mysql-test/suite/binlog_gtid/t/rpl_repl_slave_set_gtid_next.test @@ -0,0 +1,81 @@ +source include/assert_gtid_mode_on.inc; + +# Replay an 8.0.X mysqlbinlog stream +write_file $MYSQLTEST_VARDIR/tmp/binlog_output; +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +# at 4 +#191029 14:39:57 server id 1 end_log_pos 120 Start: binlog v 4, server v 5.6.35-fb-debug-log created 191029 14:39:57 at startup +# Warning: this binlog is either in use or was not closed properly. +ROLLBACK/*!*/; +BINLOG ' +rbG4XQ8BAAAAdAAAAHgAAAABAAQANS42LjM1LWZiLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAACtsbhdEzgNAAgAEgAEBAQEEgAAXAAEGggAAAAICAgCAAAACgoKGRkAANOj +IFM= +'/*!*/; +/*!50616 SET @@SESSION.GTID_NEXT='AUTOMATIC'*//*!*/; +# at 120 +# at 147 +# at 191 +#191029 14:40:59 server id 1 end_log_pos 297 Query thread_id=3 exec_time=0 error_code=0 +use `test`/*!*/; +SET TIMESTAMP=1572385259/*!*/; +SET @@session.pseudo_thread_id=3/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; +SET @@session.sql_mode=1073741824/*!*/; +SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +SET @@session.lc_time_names=0/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +create table tbl (a int primary key) +/*!*/; +# at 297 +# at 341 +#191029 14:42:37 server id 1 end_log_pos 416 Query thread_id=3 exec_time=0 error_code=0 +SET TIMESTAMP=1572385357/*!*/; +BEGIN +/*!*/; +# at 416 +#191029 14:42:37 server id 1 end_log_pos 511 Query thread_id=3 exec_time=0 error_code=0 +SET TIMESTAMP=1572385357/*!*/; +insert into tbl values(1) +/*!*/; +# at 511 +#191029 14:42:37 server id 1 end_log_pos 538 Xid = 17 +COMMIT/*!*/; +DELIMITER ; +# End of log file +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; +EOF + +create user 'olm'@'localhost' IDENTIFIED BY 'password'; +grant all privileges on *.* to 'olm'@'localhost'; +revoke super on *.* from 'olm'@'localhost'; + +create user 'normal'@'localhost' IDENTIFIED BY 'password'; +grant all privileges on *.* to 'normal'@'localhost'; +revoke super on *.* from 'normal'@'localhost'; +revoke BINLOG_ADMIN on *.* from 'normal'@'localhost'; + +alter database test read_only=True; + +--echo # Without BINLOG_ADMIN, non-super user can't execute set gtid_next when replaying binlog +--disable_abort_on_error +--exec $MYSQL --binary-mode -P $MASTER_MYPORT -u normal -h 127.0.0.1 -D test --password='password' < $MYSQLTEST_VARDIR/tmp/binlog_output +--enable_abort_on_error +--error ER_NO_SUCH_TABLE +select * from test.tbl; + +--echo # With BINLOG_ADMIN, non-super user can execute set gtid_next when replaying binlog +--exec $MYSQL --binary-mode -P $MASTER_MYPORT -u olm -h 127.0.0.1 -D test --password='password' < $MYSQLTEST_VARDIR/tmp/binlog_output +--let $assert_text = tbl should have 1 row +--let $assert_cond = [SELECT COUNT(*) from tbl] = 1 +--source include/assert.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/binlog_output +DROP user 'olm'@'localhost'; +DROP user 'normal'@'localhost'; +DROP table tbl; diff --git a/mysql-test/suite/rpl_gtid/r/rpl_repl_slave_acl.result b/mysql-test/suite/rpl_gtid/r/rpl_repl_slave_acl.result new file mode 100644 index 000000000000..1ec63200016f --- /dev/null +++ b/mysql-test/suite/rpl_gtid/r/rpl_repl_slave_acl.result @@ -0,0 +1,64 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the connection metadata repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START REPLICA; see the 'START REPLICA Syntax' in the MySQL Manual for more information. +[connection master] +CREATE TABLE tbl (id INT PRIMARY KEY, value INT); +INSERT INTO tbl VALUES (1, 1); +UPDATE tbl SET value=value+1 WHERE id=1; +INSERT INTO tbl VALUES (2, 10); +FLUSH LOGS; +include/assert.inc [tbl should have 2 rows] +DROP TABLE tbl; +include/sync_slave_sql_with_master.inc +[connection master] +CREATE USER 'olm'@'localhost' IDENTIFIED BY 'password'; +GRANT ALL PRIVILEGES ON *.* TO 'olm'@'localhost'; +REVOKE SUPER, CONNECTION_ADMIN, REPLICATION_APPLIER ON *.* FROM 'olm'@'localhost'; +Warnings: +Warning 1287 The SUPER privilege identifier is deprecated +CREATE USER 'normal'@'localhost' IDENTIFIED BY 'password'; +GRANT ALL PRIVILEGES ON *.* TO 'normal'@'localhost'; +REVOKE SUPER, CONNECTION_ADMIN, REPLICATION_APPLIER, BINLOG_ADMIN ON *.* FROM 'normal'@'localhost'; +Warnings: +Warning 1287 The SUPER privilege identifier is deprecated +--------------------------------- +1. Tests with read/write database +--------------------------------- +# User without BINLOG_ADMIN privilege can't replay binlog +SELECT * FROM tbl; +ERROR 42S02: Table 'test.tbl' doesn't exist +# User with BINLOG_ADMIN privilege can replay binlog +include/assert.inc [tbl should have 2 rows] +DROP TABLE tbl; +-------------------------------------------------- +2. Tests with "ALTER DATABASE test read_only=True" +-------------------------------------------------- +ALTER DATABASE test read_only=True; +# User without BINLOG_ADMIN privilege can't replay binlog for a read only database instance +SELECT * FROM tbl; +ERROR 42S02: Table 'test.tbl' doesn't exist +# User with BINLOG_ADMIN privilege can replay binlog for a read only database instance +include/assert.inc [tbl should have 2 rows] +DROP TABLE tbl; +ALTER DATABASE test read_only=False; +-------------------------------------- +3. Tests with "SET GLOBAL read_only=1" +-------------------------------------- +SET GLOBAL read_only=1; +# User with BINLOG_ADMIN but without CONNECTION_ADMIN privilege can't replay binlog when the server uses a "read-only" mode +SELECT * FROM tbl; +ERROR 42S02: Table 'test.tbl' doesn't exist +GRANT CONNECTION_ADMIN ON *.* TO 'olm'@'localhost'; +# User who has BINLOG_ADMIN and CONNECTION_ADMIN privilege can replay binlog when the server uses a "read-only" mode +include/assert.inc [tbl should have 2 rows] +SET GLOBAL read_only=0; +# With BINLOG_ADMIN privilege, reply binlog should generate gtid for read_only database +include/sync_slave_sql_with_master.inc +[connection slave] +include/assert.inc [tbl should have 2 rows] +[connection master] +DROP user 'normal'@'localhost'; +DROP user 'olm'@'localhost'; +DROP TABLE tbl; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl_gtid/t/rpl_repl_slave_acl.test b/mysql-test/suite/rpl_gtid/t/rpl_repl_slave_acl.test new file mode 100644 index 000000000000..e3329281d016 --- /dev/null +++ b/mysql-test/suite/rpl_gtid/t/rpl_repl_slave_acl.test @@ -0,0 +1,103 @@ +--source include/assert_gtid_mode_on.inc +--source include/master-slave.inc + +CREATE TABLE tbl (id INT PRIMARY KEY, value INT); +INSERT INTO tbl VALUES (1, 1); +UPDATE tbl SET value=value+1 WHERE id=1; +INSERT INTO tbl VALUES (2, 10); + +let $MYSQLD_DATADIR= `SELECT @@datadir;`; +let $BINLOG_NAME = query_get_value(show master status, File, 1); +--copy_file $MYSQLD_DATADIR/$BINLOG_NAME $MYSQLD_DATADIR/master-bin.saved + +FLUSH LOGS; +--let $assert_text = tbl should have 2 rows +--let $assert_cond = [SELECT COUNT(*) from tbl] = 2 +--source include/assert.inc +DROP TABLE tbl; +--source include/sync_slave_sql_with_master.inc + +--source include/rpl_connection_master.inc +CREATE USER 'olm'@'localhost' IDENTIFIED BY 'password'; +GRANT ALL PRIVILEGES ON *.* TO 'olm'@'localhost'; +REVOKE SUPER, CONNECTION_ADMIN, REPLICATION_APPLIER ON *.* FROM 'olm'@'localhost'; + +CREATE USER 'normal'@'localhost' IDENTIFIED BY 'password'; +GRANT ALL PRIVILEGES ON *.* TO 'normal'@'localhost'; +REVOKE SUPER, CONNECTION_ADMIN, REPLICATION_APPLIER, BINLOG_ADMIN ON *.* FROM 'normal'@'localhost'; + + +--echo --------------------------------- +--echo 1. Tests with read/write database +--echo --------------------------------- +--echo # User without BINLOG_ADMIN privilege can't replay binlog +--disable_abort_on_error +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='normal' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--enable_abort_on_error +# Table 'test.tbl' doesn't exist +--error ER_NO_SUCH_TABLE +SELECT * FROM tbl; + +--echo # User with BINLOG_ADMIN privilege can replay binlog +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='olm' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--let $assert_text = tbl should have 2 rows +--let $assert_cond = [SELECT COUNT(*) from tbl] = 2 +--source include/assert.inc +DROP TABLE tbl; + + +--echo -------------------------------------------------- +--echo 2. Tests with "ALTER DATABASE test read_only=True" +--echo -------------------------------------------------- +ALTER DATABASE test read_only=True; +--echo # User without BINLOG_ADMIN privilege can't replay binlog for a read only database instance +--disable_abort_on_error +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='normal' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--enable_abort_on_error +# Table 'test.tbl' doesn't exist +--error ER_NO_SUCH_TABLE +SELECT * FROM tbl; + +--echo # User with BINLOG_ADMIN privilege can replay binlog for a read only database instance +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='olm' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--let $assert_text = tbl should have 2 rows +--let $assert_cond = [SELECT COUNT(*) from tbl] = 2 +--source include/assert.inc +DROP TABLE tbl; +ALTER DATABASE test read_only=False; + + +--echo -------------------------------------- +--echo 3. Tests with "SET GLOBAL read_only=1" +--echo -------------------------------------- +SET GLOBAL read_only=1; +--echo # User with BINLOG_ADMIN but without CONNECTION_ADMIN privilege can't replay binlog when the server uses a "read-only" mode +--disable_abort_on_error +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='olm' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--enable_abort_on_error +# Table 'test.tbl' doesn't exist +--error ER_NO_SUCH_TABLE +SELECT * FROM tbl; + +GRANT CONNECTION_ADMIN ON *.* TO 'olm'@'localhost'; +--echo # User who has BINLOG_ADMIN and CONNECTION_ADMIN privilege can replay binlog when the server uses a "read-only" mode +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='olm' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1 +--let $assert_text = tbl should have 2 rows +--let $assert_cond = [SELECT COUNT(*) from tbl] = 2 +--source include/assert.inc +SET GLOBAL read_only=0; + + +--echo # With BINLOG_ADMIN privilege, reply binlog should generate gtid for read_only database +--source include/sync_slave_sql_with_master.inc +--source include/rpl_connection_slave.inc +--let $assert_text = tbl should have 2 rows +--let $assert_cond = [SELECT COUNT(*) from tbl] = 2 +--source include/assert.inc + +--source include/rpl_connection_master.inc +DROP user 'normal'@'localhost'; +DROP user 'olm'@'localhost'; +DROP TABLE tbl; +--remove_file $MYSQLD_DATADIR/master-bin.saved +--source include/rpl_end.inc diff --git a/sql/sql_db.cc b/sql/sql_db.cc index fa0a36c8ceaf..f78673789086 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -208,6 +208,9 @@ static bool fill_db_read_only_from_dd(THD *thd, const char *db, bool is_thd_db_read_only_by_name(THD *thd, const char *db) { DBUG_ENTER("is_thd_db_read_only_by_name"); bool super = thd->m_main_security_ctx.check_access(SUPER_ACL); + bool binlog_admin = + thd->m_main_security_ctx.has_global_grant(STRING_WITH_LEN("BINLOG_ADMIN")) + .first; enum enum_db_read_only flag = DB_READ_ONLY_NULL; // Check cached info in THD first. @@ -228,7 +231,8 @@ bool is_thd_db_read_only_by_name(THD *thd, const char *db) { assert(flag >= DB_READ_ONLY_NO && flag <= DB_READ_ONLY_SUPER); - if (flag == DB_READ_ONLY_SUPER || (flag == DB_READ_ONLY_YES && !super)) { + if (flag == DB_READ_ONLY_SUPER || + (flag == DB_READ_ONLY_YES && !super && !binlog_admin)) { DBUG_RETURN(true); }