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

Permissions for replay binlog for a read only database #1091

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions mysql-test/suite/binlog_gtid/r/rpl_repl_slave_set_gtid_next.result
Original file line number Diff line number Diff line change
@@ -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;
81 changes: 81 additions & 0 deletions mysql-test/suite/binlog_gtid/t/rpl_repl_slave_set_gtid_next.test
Original file line number Diff line number Diff line change
@@ -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;
64 changes: 64 additions & 0 deletions mysql-test/suite/rpl_gtid/r/rpl_repl_slave_acl.result
Original file line number Diff line number Diff line change
@@ -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 master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE 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 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, 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
103 changes: 103 additions & 0 deletions mysql-test/suite/rpl_gtid/t/rpl_repl_slave_acl.test
Original file line number Diff line number Diff line change
@@ -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 ON *.* FROM 'olm'@'localhost';

CREATE USER 'normal'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'normal'@'localhost';
REVOKE SUPER, CONNECTION_ADMIN, 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
6 changes: 5 additions & 1 deletion sql/sql_db.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -228,7 +231,8 @@ bool is_thd_db_read_only_by_name(THD *thd, const char *db) {

DBUG_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);
}

Expand Down