1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-14443 DENY statement

Implements DENY/REVOKE DENY and associated tasks.
This commit is contained in:
Vladislav Vaintroub
2026-06-17 23:04:28 +02:00
parent 7322a6656a
commit 934108aab5
75 changed files with 7380 additions and 699 deletions

View File

@@ -787,7 +787,7 @@ int check_embedded_connection(MYSQL *mysql, const char *db)
strmake_buf(sctx->priv_user, mysql->user);
sctx->user= my_strdup(PSI_NOT_INSTRUMENTED, mysql->user, MYF(0));
sctx->proxy_user[0]= 0;
sctx->master_access= GLOBAL_ACLS; // Full rights
sctx->master_access= access_t(GLOBAL_ACLS); // Full rights
emb_transfer_connect_attrs(mysql);
/* Change database if necessary */

View File

@@ -0,0 +1,71 @@
#
# Test DENY for global administrative privileges
#
CREATE USER user1@localhost;
CREATE USER user2@localhost;
# Test RELOAD privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY RELOAD ON *.* TO user1@localhost;
connect con1, localhost, user1,,;
FLUSH TABLES;
ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
FLUSH LOGS;
ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
connection default;
disconnect con1;
# Test SHUTDOWN privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY SHUTDOWN ON *.* TO user1@localhost;
connect con1, localhost, user1,,;
SHUTDOWN;
ERROR 42000: Access denied; you need (at least one of) the SHUTDOWN privilege(s) for this operation
connection default;
disconnect con1;
# Test PROCESS privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY PROCESS ON *.* TO user1@localhost;
connect con1, localhost, user1,,;
SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER <> 'user1';
COUNT(*)
0
connection default;
disconnect con1;
# Test FILE privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY FILE ON *.* TO user1@localhost;
connect con1, localhost, user1,,;
SELECT 'test' INTO OUTFILE 'test.txt';
ERROR 42000: Access denied; you need (at least one of) the FILE privilege(s) for this operation
connection default;
disconnect con1;
# Test combination of DENY on multiple global privileges
GRANT ALL PRIVILEGES ON *.* TO user2@localhost;
DENY RELOAD, SHUTDOWN ON *.* TO user2@localhost;
connect con2, localhost, user2,,;
FLUSH TABLES;
ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
SHUTDOWN;
ERROR 42000: Access denied; you need (at least one of) the SHUTDOWN privilege(s) for this operation
connection default;
disconnect con2;
# Cleanup
DROP USER user1@localhost;
DROP USER user2@localhost;
#
# Test CONNECTION ADMIN privilege behaviors with DENY
#
CREATE USER user_super@localhost;
GRANT ALL PRIVILEGES ON *.* TO user_super@localhost;
DENY CONNECTION ADMIN ON *.* TO user_super@localhost;
connect con_super, localhost, user_super,,;
# Should not be able to kill other connections
connection default;
connection con_super;
KILL ID;
ERROR HY000: You are not owner of thread ID
# Should not be able to set global variables
SET GLOBAL max_connections = 200;
ERROR 42000: Access denied; you need (at least one of) the CONNECTION ADMIN privilege(s) for this operation
connection default;
disconnect con_super;
DROP USER user_super@localhost;

View File

@@ -0,0 +1,105 @@
--source include/not_embedded.inc
--source include/no_protocol.inc
--echo #
--echo # Test DENY for global administrative privileges
--echo #
CREATE USER user1@localhost;
CREATE USER user2@localhost;
--echo # Test RELOAD privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY RELOAD ON *.* TO user1@localhost;
connect (con1, localhost, user1,,);
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
FLUSH TABLES;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
FLUSH LOGS;
connection default;
disconnect con1;
--echo # Test SHUTDOWN privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY SHUTDOWN ON *.* TO user1@localhost;
connect (con1, localhost, user1,,);
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
SHUTDOWN;
connection default;
disconnect con1;
--echo # Test PROCESS privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY PROCESS ON *.* TO user1@localhost;
let $count_sessions= 1;
--source include/wait_until_count_sessions.inc
connect (con1, localhost, user1,,);
SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER <> 'user1';
connection default;
disconnect con1;
--echo # Test FILE privilege
GRANT ALL PRIVILEGES ON *.* TO user1@localhost;
DENY FILE ON *.* TO user1@localhost;
connect (con1, localhost, user1,,);
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
SELECT 'test' INTO OUTFILE 'test.txt';
connection default;
disconnect con1;
--echo # Test combination of DENY on multiple global privileges
GRANT ALL PRIVILEGES ON *.* TO user2@localhost;
DENY RELOAD, SHUTDOWN ON *.* TO user2@localhost;
connect (con2, localhost, user2,,);
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
FLUSH TABLES;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
SHUTDOWN;
connection default;
disconnect con2;
--echo # Cleanup
DROP USER user1@localhost;
DROP USER user2@localhost;
--echo #
--echo # Test CONNECTION ADMIN privilege behaviors with DENY
--echo #
CREATE USER user_super@localhost;
GRANT ALL PRIVILEGES ON *.* TO user_super@localhost;
DENY CONNECTION ADMIN ON *.* TO user_super@localhost;
connect (con_super, localhost, user_super,,);
--echo # Should not be able to kill other connections
connection default;
let $default_id = `SELECT CONNECTION_ID()`;
connection con_super;
--replace_result $default_id ID
--error ER_KILL_DENIED_ERROR
eval KILL $default_id;
--echo # Should not be able to set global variables
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
SET GLOBAL max_connections = 200;
connection default;
disconnect con_super;
DROP USER user_super@localhost;

View File

@@ -0,0 +1,166 @@
#
# MDEV-40014 Access to freed/poisoned memory in SHOW GRANTS
# after REVOKE DENY, if only negative grants are present
#
CREATE TABLE t1(val int);
CREATE USER u;
DENY EXECUTE ON test.* TO u;
DENY UPDATE (val) ON t1 TO u;
REVOKE DENY EXECUTE ON test.* FROM u;
SHOW GRANTS FOR u;
Grants for u@%
GRANT USAGE ON *.* TO `u`@`%`
DENY UPDATE (`val`) ON `test`.`t1` TO `u`@`%`
DROP USER u;
DROP TABLE t1;
#
# MDEV-40028 Assertion `rights.allow_bits() == merged->cols' failed in int update_role_columns
#
CREATE TABLE t (c1 INT, c2 INT, c3 int);
CREATE ROLE r;
DENY SELECT (c2) ON t TO r;
SHOW GRANTS FOR r;
Grants for r
GRANT USAGE ON *.* TO `r`
DENY SELECT (`c2`) ON `test`.`t` TO `r`
REVOKE DENY SELECT (c2) ON t FROM r;
SHOW GRANTS FOR r;
Grants for r
GRANT USAGE ON *.* TO `r`
DROP ROLE r;
DROP TABLE t;
#
# MDEV-36503 Single routine-level DENY EXECUTE hides other routine metadata in the database
#
CREATE DATABASE d1;
CREATE DEFINER=root@localhost PROCEDURE d1.p1() BEGIN SELECT 1; END$$
CREATE DEFINER=root@localhost PROCEDURE d1.p2() BEGIN SELECT 2; END$$
CREATE USER u@localhost;
GRANT EXECUTE ON d1.* TO u@localhost;
DENY EXECUTE ON PROCEDURE d1.p1 TO u@localhost;
DENY ALTER ROUTINE ON PROCEDURE d1.p2 TO u@localhost;
FLUSH PRIVILEGES;
connect con_bug36503, localhost, u,,d1;
# p2 must be callable and both p1, p2 must appear in INFORMATION_SCHEMA.ROUTINES
CALL p2();
2
2
SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='d1' ORDER BY ROUTINE_NAME;
ROUTINE_NAME
p2
# p1 is denied, calling it must fail
CALL p1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'd1.p1'
disconnect con_bug36503;
connection default;
DROP DATABASE d1;
DROP USER u@localhost;
#
# MDEV-40116 Column-level DENY SELECT hides accessible columns and table metadata
#
CREATE DATABASE d1;
CREATE TABLE d1.t1 (a INT, b INT, c INT, KEY(a));
INSERT INTO d1.t1 VALUES (1,2,3);
CREATE USER u@localhost;
GRANT SELECT ON d1.t1 TO u@localhost;
DENY SELECT (b) ON d1.t1 TO u@localhost;
FLUSH PRIVILEGES;
connect con_bug40116, localhost, u,,d1;
# a and c are accessible, b is denied
SELECT a, c FROM d1.t1;
a c
1 3
SELECT * FROM d1.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for column 'b' in table 't1'
# metadata commands must work and show a, c (not b)
SHOW COLUMNS FROM d1.t1;
Field Type Null Key Default Extra
a int(11) YES MUL NULL
c int(11) YES NULL
SHOW INDEX FROM d1.t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t1 1 a 1 a A NULL NULL NULL YES BTREE NO
SHOW CREATE TABLE d1.t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
SELECT COLUMN_NAME FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA='d1' AND TABLE_NAME='t1' ORDER BY COLUMN_NAME;
COLUMN_NAME
a
c
disconnect con_bug40116;
connection default;
DROP DATABASE d1;
DROP USER u@localhost;
#
# MDEV-40026 SIGSEGV in acl_insert_db on FLUSH PRIVILEGES with overlong DENY names
#
CREATE USER u@localhost;
CALL mtr.add_suppression('.*Malformed DENY entry in mysql\\.global_priv at array position [0-2] for ''u''@''localhost'' ignored');
UPDATE mysql.global_priv SET Priv= json_set(Priv, '$.denies', json_array(
json_object('type','db', 'db',REPEAT('a',300),'bits',1),
json_object('type','table','db','d1','table',REPEAT('b',300),'bits',1),
json_object('type','column','db','d1','table','t1','column',REPEAT('c',300),'bits',1)
)) WHERE User='u' AND Host='localhost';
FLUSH PRIVILEGES;
FOUND 1 /Malformed DENY entry in mysql.global_priv at array position 0 for 'u'@'localhost' ignored/ in mysqld.1.err
FOUND 1 /Malformed DENY entry in mysql.global_priv at array position 1 for 'u'@'localhost' ignored/ in mysqld.1.err
FOUND 1 /Malformed DENY entry in mysql.global_priv at array position 2 for 'u'@'localhost' ignored/ in mysqld.1.err
DROP USER u@localhost;
#
# MDEV-40131 DENY not reloaded, granted to a ROLEs exclusively
#
CREATE DATABASE d1;
CREATE TABLE d1.t1 (a INT);
INSERT INTO d1.t1 VALUES (1);
CREATE ROLE r;
GRANT SELECT ON d1.* TO r;
DENY SELECT ON d1.t1 TO r;
CREATE USER u@localhost;
GRANT r TO u@localhost;
SET DEFAULT ROLE r FOR u@localhost;
# BEFORE reload: role DENY correctly enforced
connect p, localhost, u,, d1;
SELECT * FROM d1.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `d1`.`t1`
connection default;
disconnect p;
FLUSH PRIVILEGES;
# role DENY must survive the reload
SHOW GRANTS FOR r;
Grants for r
GRANT USAGE ON *.* TO `r`
GRANT SELECT ON `d1`.* TO `r`
DENY SELECT ON `d1`.`t1` TO `r`
# AFTER reload: role DENY must still be enforced
connect p, localhost, u,, d1;
SELECT * FROM d1.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `d1`.`t1`
connection default;
disconnect p;
DROP ROLE r;
DROP DATABASE d1;
DROP USER u@localhost;
#
# MDEV-40300 Assertion in update_role_columns on DROP ROLE
#
CREATE TABLE t1 (c1 INT,c2 INT,c3 INT);
CREATE ROLE r1;
CREATE ROLE r2;
GRANT r2 TO r1;
DENY DELETE ON t1 TO r1;
DENY SELECT (c2) ON t1 TO r1;
GRANT SELECT (c3) ON t1 TO r2;
REVOKE DENY ALL PRIVILEGES ON t1 FROM r1;
DROP ROLE IF EXISTS r2;
SHOW GRANTS FOR r1;
Grants for r1
GRANT USAGE ON *.* TO `r1`
DENY SELECT (`c2`) ON `test`.`t1` TO `r1`
DROP ROLE r1;
DROP TABLE t1;

View File

@@ -0,0 +1,159 @@
--source include/not_embedded.inc
--echo #
--echo # MDEV-40014 Access to freed/poisoned memory in SHOW GRANTS
--echo # after REVOKE DENY, if only negative grants are present
--echo #
CREATE TABLE t1(val int);
CREATE USER u;
DENY EXECUTE ON test.* TO u;
DENY UPDATE (val) ON t1 TO u;
REVOKE DENY EXECUTE ON test.* FROM u;
SHOW GRANTS FOR u;
DROP USER u;
DROP TABLE t1;
--echo #
--echo # MDEV-40028 Assertion `rights.allow_bits() == merged->cols' failed in int update_role_columns
--echo #
CREATE TABLE t (c1 INT, c2 INT, c3 int);
CREATE ROLE r;
DENY SELECT (c2) ON t TO r;
SHOW GRANTS FOR r;
REVOKE DENY SELECT (c2) ON t FROM r;
SHOW GRANTS FOR r;
DROP ROLE r;
DROP TABLE t;
--echo #
--echo # MDEV-36503 Single routine-level DENY EXECUTE hides other routine metadata in the database
--echo #
CREATE DATABASE d1;
DELIMITER $$;
CREATE DEFINER=root@localhost PROCEDURE d1.p1() BEGIN SELECT 1; END$$
CREATE DEFINER=root@localhost PROCEDURE d1.p2() BEGIN SELECT 2; END$$
DELIMITER ;$$
CREATE USER u@localhost;
GRANT EXECUTE ON d1.* TO u@localhost;
DENY EXECUTE ON PROCEDURE d1.p1 TO u@localhost;
DENY ALTER ROUTINE ON PROCEDURE d1.p2 TO u@localhost;
FLUSH PRIVILEGES;
connect (con_bug36503, localhost, u,,d1);
--echo # p2 must be callable and both p1, p2 must appear in INFORMATION_SCHEMA.ROUTINES
CALL p2();
SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='d1' ORDER BY ROUTINE_NAME;
--echo # p1 is denied, calling it must fail
--error ER_PROCACCESS_DENIED_ERROR
CALL p1();
disconnect con_bug36503;
connection default;
DROP DATABASE d1;
DROP USER u@localhost;
--echo #
--echo # MDEV-40116 Column-level DENY SELECT hides accessible columns and table metadata
--echo #
CREATE DATABASE d1;
CREATE TABLE d1.t1 (a INT, b INT, c INT, KEY(a));
INSERT INTO d1.t1 VALUES (1,2,3);
CREATE USER u@localhost;
GRANT SELECT ON d1.t1 TO u@localhost;
DENY SELECT (b) ON d1.t1 TO u@localhost;
FLUSH PRIVILEGES;
connect (con_bug40116, localhost, u,,d1);
--echo # a and c are accessible, b is denied
SELECT a, c FROM d1.t1;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT * FROM d1.t1;
--echo # metadata commands must work and show a, c (not b)
SHOW COLUMNS FROM d1.t1;
SHOW INDEX FROM d1.t1;
SHOW CREATE TABLE d1.t1;
SELECT COLUMN_NAME FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA='d1' AND TABLE_NAME='t1' ORDER BY COLUMN_NAME;
disconnect con_bug40116;
connection default;
DROP DATABASE d1;
DROP USER u@localhost;
--echo #
--echo # MDEV-40026 SIGSEGV in acl_insert_db on FLUSH PRIVILEGES with overlong DENY names
--echo #
CREATE USER u@localhost;
CALL mtr.add_suppression('.*Malformed DENY entry in mysql\\.global_priv at array position [0-2] for ''u''@''localhost'' ignored');
UPDATE mysql.global_priv SET Priv= json_set(Priv, '$.denies', json_array(
json_object('type','db', 'db',REPEAT('a',300),'bits',1),
json_object('type','table','db','d1','table',REPEAT('b',300),'bits',1),
json_object('type','column','db','d1','table','t1','column',REPEAT('c',300),'bits',1)
)) WHERE User='u' AND Host='localhost';
FLUSH PRIVILEGES;
--let SEARCH_FILE= `SELECT @@log_error`
--let SEARCH_PATTERN= Malformed DENY entry in mysql.global_priv at array position 0 for 'u'@'localhost' ignored
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= Malformed DENY entry in mysql.global_priv at array position 1 for 'u'@'localhost' ignored
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= Malformed DENY entry in mysql.global_priv at array position 2 for 'u'@'localhost' ignored
--source include/search_pattern_in_file.inc
DROP USER u@localhost;
--echo #
--echo # MDEV-40131 DENY not reloaded, granted to a ROLEs exclusively
--echo #
CREATE DATABASE d1;
CREATE TABLE d1.t1 (a INT);
INSERT INTO d1.t1 VALUES (1);
CREATE ROLE r;
GRANT SELECT ON d1.* TO r;
DENY SELECT ON d1.t1 TO r;
CREATE USER u@localhost;
GRANT r TO u@localhost;
SET DEFAULT ROLE r FOR u@localhost;
--echo # BEFORE reload: role DENY correctly enforced
connect (p, localhost, u,, d1);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM d1.t1;
connection default;
disconnect p;
FLUSH PRIVILEGES;
--echo # role DENY must survive the reload
SHOW GRANTS FOR r;
--echo # AFTER reload: role DENY must still be enforced
connect (p, localhost, u,, d1);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM d1.t1;
connection default;
disconnect p;
DROP ROLE r;
DROP DATABASE d1;
DROP USER u@localhost;
--echo #
--echo # MDEV-40300 Assertion in update_role_columns on DROP ROLE
--echo #
CREATE TABLE t1 (c1 INT,c2 INT,c3 INT);
CREATE ROLE r1;
CREATE ROLE r2;
GRANT r2 TO r1;
DENY DELETE ON t1 TO r1;
DENY SELECT (c2) ON t1 TO r1;
GRANT SELECT (c3) ON t1 TO r2;
REVOKE DENY ALL PRIVILEGES ON t1 FROM r1;
DROP ROLE IF EXISTS r2;
SHOW GRANTS FOR r1;
DROP ROLE r1;
DROP TABLE t1;

View File

@@ -0,0 +1,160 @@
#
# Column-level DENY privilege test
#
CREATE DATABASE deny_col_db;
CREATE TABLE deny_col_db.sensitive_data (
id INT,
public_info VARCHAR(50),
salary DECIMAL(10,2),
ssn VARCHAR(11),
address VARCHAR(100)
);
INSERT INTO deny_col_db.sensitive_data VALUES
(1, 'John Doe', 75000.00, '123-45-6789', '123 Main St'),
(2, 'Jane Smith', 85000.00, '987-65-4321', '456 Oak Ave');
CREATE USER col_user1@localhost;
CREATE USER col_user2@localhost;
#
# Basic column-level DENY
#
GRANT SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary, ssn) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect con1, localhost, col_user1,,deny_col_db;
SELECT id, public_info FROM sensitive_data;
id public_info
1 John Doe
2 Jane Smith
SELECT salary FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
SELECT ssn FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'ssn' in table 'sensitive_data'
SELECT * FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
SELECT id, salary FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
disconnect con1;
connection default;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE DENY SELECT (salary, ssn) ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# Column DENY takes precedence over explicit column GRANT
#
GRANT SELECT (salary,id,public_info) ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect con3, localhost, col_user1,,deny_col_db;
SELECT id, public_info FROM sensitive_data;
id public_info
1 John Doe
2 Jane Smith
SELECT salary FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
disconnect con3;
connection default;
REVOKE SELECT (salary,id,public_info) ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE DENY SELECT (salary) ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# REVOKE DENY on a column
#
GRANT SELECT (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary,ssn) ON deny_col_db.sensitive_data TO col_user1@localhost;
REVOKE DENY SELECT (salary) ON deny_col_db.sensitive_data FROM col_user1@localhost;
connect con4, localhost, col_user1,,deny_col_db;
SELECT salary FROM sensitive_data;
salary
75000.00
85000.00
SELECT ssn FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'ssn' in table 'sensitive_data'
disconnect con4;
connection default;
REVOKE DENY SELECT (ssn) ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# Multiple column DENYs
#
GRANT SELECT ON deny_col_db.sensitive_data TO col_user2@localhost;
DENY SELECT (salary, ssn, address) ON deny_col_db.sensitive_data TO col_user2@localhost;
connect con5, localhost, col_user2,,deny_col_db;
SELECT id, public_info FROM sensitive_data;
id public_info
1 John Doe
2 Jane Smith
SELECT salary FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user2'@'localhost' for column 'salary' in table 'sensitive_data'
SELECT address FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user2'@'localhost' for column 'address' in table 'sensitive_data'
SELECT * FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user2'@'localhost' for column 'salary' in table 'sensitive_data'
disconnect con5;
connection default;
REVOKE DENY SELECT (salary, ssn, address) ON deny_col_db.sensitive_data FROM col_user2@localhost;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user2@localhost;
#
# Column DENY with INSERT/UPDATE operations
#
CREATE TABLE deny_col_db.audit_log (
id INT AUTO_INCREMENT PRIMARY KEY,
action VARCHAR(50),
modified_by VARCHAR(50),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
GRANT SELECT, INSERT ON deny_col_db.audit_log TO col_user1@localhost;
DENY INSERT (modified_by) ON deny_col_db.audit_log TO col_user1@localhost;
connect con8, localhost, col_user1,,deny_col_db;
INSERT INTO audit_log (action) VALUES ('test_action');
INSERT INTO audit_log (action, modified_by) VALUES ('test', 'hacker');
ERROR 42000: INSERT command denied to user 'col_user1'@'localhost' for column 'modified_by' in table 'audit_log'
SELECT * FROM audit_log;
id action modified_by timestamp
1 test_action NULL TIMESTAMP
disconnect con8;
connection default;
#
# Column DENY with UPDATE
#
GRANT UPDATE ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY UPDATE (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
GRANT SELECT(id,public_info) on deny_col_db.sensitive_data TO col_user1@localhost;
connect con9, localhost, col_user1,,deny_col_db;
UPDATE sensitive_data SET public_info = 'Updated' WHERE id = 1;
UPDATE sensitive_data SET salary = 100000 WHERE id = 1;
ERROR 42000: UPDATE command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
UPDATE sensitive_data SET public_info = 'X', salary = 90000 WHERE id = 2;
ERROR 42000: UPDATE command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
disconnect con9;
connection default;
REVOKE SELECT (id,public_info) ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# Table-level DENY SELECT: UPDATE without WHERE must succeed (no read needed)
#
DENY SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
connect con9b, localhost, col_user1,,deny_col_db;
# UPDATE with WHERE reads id -> SELECT needed -> DENY fires
UPDATE sensitive_data SET public_info = 'ps_test' WHERE id = 1;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'id' in table 'sensitive_data'
# UPDATE without WHERE: no SELECT needed, must succeed
UPDATE sensitive_data SET public_info = 'ps_test';
disconnect con9b;
connection default;
REVOKE DENY SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE UPDATE ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# Column DENY is case-insensitive
#
GRANT SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (SaLaRy) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect con10, localhost, col_user1,,deny_col_db;
SELECT salary FROM sensitive_data;
ERROR 42000: SELECT command denied to user 'col_user1'@'localhost' for column 'salary' in table 'sensitive_data'
disconnect con10;
connection default;
REVOKE DENY SELECT (SaLaRy) ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
#
# Cleanup
#
DROP USER col_user1@localhost;
DROP USER col_user2@localhost;
DROP DATABASE deny_col_db;
#
# End of column-level DENY tests
#

View File

@@ -0,0 +1,180 @@
--source include/not_embedded.inc
--echo #
--echo # Column-level DENY privilege test
--echo #
CREATE DATABASE deny_col_db;
CREATE TABLE deny_col_db.sensitive_data (
id INT,
public_info VARCHAR(50),
salary DECIMAL(10,2),
ssn VARCHAR(11),
address VARCHAR(100)
);
INSERT INTO deny_col_db.sensitive_data VALUES
(1, 'John Doe', 75000.00, '123-45-6789', '123 Main St'),
(2, 'Jane Smith', 85000.00, '987-65-4321', '456 Oak Ave');
CREATE USER col_user1@localhost;
CREATE USER col_user2@localhost;
--echo #
--echo # Basic column-level DENY
--echo #
GRANT SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary, ssn) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect (con1, localhost, col_user1,,deny_col_db);
SELECT id, public_info FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT salary FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT ssn FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT * FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT id, salary FROM sensitive_data;
disconnect con1;
connection default;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE DENY SELECT (salary, ssn) ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # Column DENY takes precedence over explicit column GRANT
--echo #
GRANT SELECT (salary,id,public_info) ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect (con3, localhost, col_user1,,deny_col_db);
SELECT id, public_info FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT salary FROM sensitive_data;
disconnect con3;
connection default;
REVOKE SELECT (salary,id,public_info) ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE DENY SELECT (salary) ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # REVOKE DENY on a column
--echo #
GRANT SELECT (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (salary,ssn) ON deny_col_db.sensitive_data TO col_user1@localhost;
REVOKE DENY SELECT (salary) ON deny_col_db.sensitive_data FROM col_user1@localhost;
connect (con4, localhost, col_user1,,deny_col_db);
SELECT salary FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT ssn FROM sensitive_data;
disconnect con4;
connection default;
REVOKE DENY SELECT (ssn) ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # Multiple column DENYs
--echo #
GRANT SELECT ON deny_col_db.sensitive_data TO col_user2@localhost;
DENY SELECT (salary, ssn, address) ON deny_col_db.sensitive_data TO col_user2@localhost;
connect (con5, localhost, col_user2,,deny_col_db);
SELECT id, public_info FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT salary FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT address FROM sensitive_data;
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT * FROM sensitive_data;
disconnect con5;
connection default;
REVOKE DENY SELECT (salary, ssn, address) ON deny_col_db.sensitive_data FROM col_user2@localhost;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user2@localhost;
--echo #
--echo # Column DENY with INSERT/UPDATE operations
--echo #
CREATE TABLE deny_col_db.audit_log (
id INT AUTO_INCREMENT PRIMARY KEY,
action VARCHAR(50),
modified_by VARCHAR(50),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
GRANT SELECT, INSERT ON deny_col_db.audit_log TO col_user1@localhost;
DENY INSERT (modified_by) ON deny_col_db.audit_log TO col_user1@localhost;
connect (con8, localhost, col_user1,,deny_col_db);
INSERT INTO audit_log (action) VALUES ('test_action');
--error ER_COLUMNACCESS_DENIED_ERROR
INSERT INTO audit_log (action, modified_by) VALUES ('test', 'hacker');
--replace_column 4 TIMESTAMP
SELECT * FROM audit_log;
disconnect con8;
connection default;
--echo #
--echo # Column DENY with UPDATE
--echo #
GRANT UPDATE ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY UPDATE (salary) ON deny_col_db.sensitive_data TO col_user1@localhost;
GRANT SELECT(id,public_info) on deny_col_db.sensitive_data TO col_user1@localhost;
connect (con9, localhost, col_user1,,deny_col_db);
UPDATE sensitive_data SET public_info = 'Updated' WHERE id = 1;
--error ER_COLUMNACCESS_DENIED_ERROR
UPDATE sensitive_data SET salary = 100000 WHERE id = 1;
--error ER_COLUMNACCESS_DENIED_ERROR
UPDATE sensitive_data SET public_info = 'X', salary = 90000 WHERE id = 2;
disconnect con9;
connection default;
REVOKE SELECT (id,public_info) ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # Table-level DENY SELECT: UPDATE without WHERE must succeed (no read needed)
--echo #
DENY SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
connect (con9b, localhost, col_user1,,deny_col_db);
--echo # UPDATE with WHERE reads id -> SELECT needed -> DENY fires
--error ER_COLUMNACCESS_DENIED_ERROR
UPDATE sensitive_data SET public_info = 'ps_test' WHERE id = 1;
--echo # UPDATE without WHERE: no SELECT needed, must succeed
UPDATE sensitive_data SET public_info = 'ps_test';
disconnect con9b;
connection default;
REVOKE DENY SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE UPDATE ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # Column DENY is case-insensitive
--echo #
GRANT SELECT ON deny_col_db.sensitive_data TO col_user1@localhost;
DENY SELECT (SaLaRy) ON deny_col_db.sensitive_data TO col_user1@localhost;
connect (con10, localhost, col_user1,,deny_col_db);
--error ER_COLUMNACCESS_DENIED_ERROR
SELECT salary FROM sensitive_data;
disconnect con10;
connection default;
REVOKE DENY SELECT (SaLaRy) ON deny_col_db.sensitive_data FROM col_user1@localhost;
REVOKE SELECT ON deny_col_db.sensitive_data FROM col_user1@localhost;
--echo #
--echo # Cleanup
--echo #
DROP USER col_user1@localhost;
DROP USER col_user2@localhost;
DROP DATABASE deny_col_db;
--echo #
--echo # End of column-level DENY tests
--echo #

View File

@@ -0,0 +1,17 @@
#
# DENY EVENT privilege tests
#
CREATE DATABASE ev_db;
CREATE USER ev_user@localhost;
GRANT ALL PRIVILEGES ON ev_db.* TO ev_user@localhost;
#
# DENY EVENT: blocks CREATE EVENT in database ev_db
#
DENY EVENT ON ev_db.* TO ev_user@localhost;
connect con1, localhost, ev_user,,ev_db;
CREATE EVENT e1 ON SCHEDULE EVERY 1 HOUR DO SELECT 1;
ERROR 42000: Access denied for user 'ev_user'@'localhost' to database 'ev_db'
disconnect con1;
connection default;
DROP DATABASE ev_db;
DROP USER ev_user@localhost;

View File

@@ -0,0 +1,23 @@
--source include/not_embedded.inc
--echo #
--echo # DENY EVENT privilege tests
--echo #
CREATE DATABASE ev_db;
CREATE USER ev_user@localhost;
GRANT ALL PRIVILEGES ON ev_db.* TO ev_user@localhost;
--echo #
--echo # DENY EVENT: blocks CREATE EVENT in database ev_db
--echo #
DENY EVENT ON ev_db.* TO ev_user@localhost;
connect (con1, localhost, ev_user,,ev_db);
--error ER_DBACCESS_DENIED_ERROR
CREATE EVENT e1 ON SCHEDULE EVERY 1 HOUR DO SELECT 1;
disconnect con1;
connection default;
DROP DATABASE ev_db;
DROP USER ev_user@localhost;

View File

@@ -0,0 +1,151 @@
#
# Test for Global DENY
#
USE test;
CREATE TABLE t1 (id INT, data VARCHAR(50));
CREATE TABLE t2 (id INT, info VARCHAR(50));
INSERT INTO t1 VALUES (1, 'data1'), (2, 'data2');
INSERT INTO t2 VALUES (1, 'info1'), (2, 'info2');
#
# Global DENY SELECT - Direct access
#
CREATE USER user1;
GRANT ALL PRIVILEGES ON *.* TO user1;
DENY SELECT ON *.* TO user1;
connect con1, localhost, user1,,test;
# Should FAIL - global DENY SELECT
SELECT * FROM t1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table `test`.`t1`
# Should FAIL - in any database
SELECT * FROM mysql.user;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table `mysql`.`user`
# Should SUCCEED - INSERT not denied
INSERT INTO t1 VALUES (3, 'data3');
INSERT INTO t1 VALUES (1, 'data1'), (2, 'data2');
# Should SUCCEED, no db objects used
SELECT 1;
1
1
# Should SUCCEED, I_S tables are not GRANT/DENYable
SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE 0;
VARIABLE_NAME
connection default;
disconnect con1;
DROP USER user1;
#
# Global DENY with VIEW - DEFINER is denied
#
CREATE USER denied_definer;
CREATE USER normal_user;
GRANT ALL PRIVILEGES ON test.* TO denied_definer;
DENY SELECT ON *.* TO denied_definer;
GRANT SELECT ON test.* TO normal_user;
connect con1, localhost, denied_definer,,test;
# Can create view (doesn't require SELECT at CREATE time)
CREATE SQL SECURITY DEFINER VIEW v2 AS SELECT * FROM t2;
disconnect con1;
connection default;
GRANT SELECT ON test.v2 TO normal_user;
connect con1, localhost, normal_user,,test;
# Should FAIL - view uses denied_definer's context (which is denied)
SELECT * FROM v2;
ERROR HY000: View 'test.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
# Should SUCCEED - direct table access works for normal_user
SELECT * FROM t2;
id info
1 info1
2 info2
connection default;
disconnect con1;
DROP VIEW v2;
DROP USER denied_definer;
DROP USER normal_user;
#
# SHOW COLUMNS with global DENY (any_combination_will_do)
#
CREATE USER show_user;
GRANT ALL PRIVILEGES ON test.* TO show_user;
DENY ALL PRIVILEGES ON *.* TO show_user;
REVOKE DENY EXECUTE ON *.* FROM show_user;
connect con1, localhost, show_user,,test;
# Should FAIL - SHOW COLUMNS requires column privileges
SHOW COLUMNS FROM t1;
ERROR 42000: SELECT command denied to user 'show_user'@'localhost' for table `test`.`t1`
# Should FAIL - DESCRIBE is same as SHOW COLUMNS
DESCRIBE t1;
ERROR 42000: SELECT command denied to user 'show_user'@'localhost' for table `test`.`t1`
# Should FAIL - SHOW INDEX
SHOW INDEX FROM t1;
ERROR 42000: SELECT command denied to user 'show_user'@'localhost' for table `test`.`t1`
connection default;
disconnect con1;
DROP USER show_user;
#
# DENY precedence - DENY overrides GRANT
#
CREATE USER prec_user;
# First GRANT, then DENY
GRANT SELECT,INSERT ON *.* TO prec_user;
DENY SELECT ON *.* TO prec_user;
connect con1, localhost, prec_user,,test;
SELECT * FROM t1;
ERROR 42000: SELECT command denied to user 'prec_user'@'localhost' for table `test`.`t1`
connection default;
disconnect con1;
# Reverse order: DENY first, then GRANT
REVOKE SELECT ON *.* FROM prec_user;
DENY SELECT ON *.* TO prec_user;
GRANT SELECT ON *.* TO prec_user;
connect con1, localhost, prec_user,,test;
# Should STILL FAIL - DENY takes precedence
SELECT * FROM t1;
ERROR 42000: SELECT command denied to user 'prec_user'@'localhost' for table `test`.`t1`
connection default;
disconnect con1;
DROP USER prec_user;
#
# Mixed operations - some denied, some allowed
#
CREATE USER mixed_user;
GRANT ALL PRIVILEGES ON test.* TO mixed_user;
DENY SELECT, DELETE ON *.* TO mixed_user;
connect con1, localhost, mixed_user,,test;
SELECT * FROM t1;
ERROR 42000: SELECT command denied to user 'mixed_user'@'localhost' for table `test`.`t1`
DELETE FROM t1 WHERE id=1;
ERROR 42000: DELETE command denied to user 'mixed_user'@'localhost' for table `test`.`t1`
# Should SUCCEED
INSERT INTO t1 VALUES (4, 'data4');
# Denied because of WHERE clause in UPDATE
UPDATE t1 SET data='modified' WHERE id=4;
ERROR 42000: SELECT command denied to user 'mixed_user'@'localhost' for column 'id' in table 't1'
connection default;
disconnect con1;
DROP USER mixed_user;
#
# DENY with subquery
#
CREATE USER sub_user;
GRANT ALL PRIVILEGES ON test.* TO sub_user;
DENY SELECT ON *.* TO sub_user;
connect con1, localhost, sub_user,,test;
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);
ERROR 42000: SELECT command denied to user 'sub_user'@'localhost' for table `test`.`t1`
SELECT * FROM (SELECT * FROM t1) AS dt;
ERROR 42000: SELECT command denied to user 'sub_user'@'localhost' for table `test`.`t1`
disconnect con1;
connection default;
#
# Check that FLUSH PRIVILEGES retains DENYs
#
FLUSH PRIVILEGES;
connect con1, localhost, sub_user,,test;
SELECT * FROM (SELECT * FROM t1) AS dt;
ERROR 42000: SELECT command denied to user 'sub_user'@'localhost' for table `test`.`t1`
disconnect con1;
connection default;
DROP USER sub_user;
#
# Cleanup
#
DROP TABLE t1, t2;

View File

@@ -0,0 +1,207 @@
--source include/not_embedded.inc
--source include/no_protocol.inc
--echo #
--echo # Test for Global DENY
--echo #
USE test;
CREATE TABLE t1 (id INT, data VARCHAR(50));
CREATE TABLE t2 (id INT, info VARCHAR(50));
INSERT INTO t1 VALUES (1, 'data1'), (2, 'data2');
INSERT INTO t2 VALUES (1, 'info1'), (2, 'info2');
--echo #
--echo # Global DENY SELECT - Direct access
--echo #
CREATE USER user1;
GRANT ALL PRIVILEGES ON *.* TO user1;
DENY SELECT ON *.* TO user1;
connect (con1, localhost, user1,,test);
--echo # Should FAIL - global DENY SELECT
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM t1;
--echo # Should FAIL - in any database
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM mysql.user;
--echo # Should SUCCEED - INSERT not denied
INSERT INTO t1 VALUES (3, 'data3');
INSERT INTO t1 VALUES (1, 'data1'), (2, 'data2');
--echo # Should SUCCEED, no db objects used
SELECT 1;
--echo # Should SUCCEED, I_S tables are not GRANT/DENYable
SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE 0;
connection default;
disconnect con1;
DROP USER user1;
--echo #
--echo # Global DENY with VIEW - DEFINER is denied
--echo #
CREATE USER denied_definer;
CREATE USER normal_user;
GRANT ALL PRIVILEGES ON test.* TO denied_definer;
DENY SELECT ON *.* TO denied_definer;
GRANT SELECT ON test.* TO normal_user;
connect (con1, localhost, denied_definer,,test);
--echo # Can create view (doesn't require SELECT at CREATE time)
CREATE SQL SECURITY DEFINER VIEW v2 AS SELECT * FROM t2;
disconnect con1;
connection default;
GRANT SELECT ON test.v2 TO normal_user;
connect (con1, localhost, normal_user,,test);
--echo # Should FAIL - view uses denied_definer's context (which is denied)
--error ER_VIEW_INVALID
SELECT * FROM v2;
--echo # Should SUCCEED - direct table access works for normal_user
SELECT * FROM t2;
connection default;
disconnect con1;
DROP VIEW v2;
DROP USER denied_definer;
DROP USER normal_user;
--echo #
--echo # SHOW COLUMNS with global DENY (any_combination_will_do)
--echo #
CREATE USER show_user;
GRANT ALL PRIVILEGES ON test.* TO show_user;
DENY ALL PRIVILEGES ON *.* TO show_user;
# For connect to db
REVOKE DENY EXECUTE ON *.* FROM show_user;
connect (con1, localhost, show_user,,test);
--echo # Should FAIL - SHOW COLUMNS requires column privileges
--error ER_TABLEACCESS_DENIED_ERROR
SHOW COLUMNS FROM t1;
--echo # Should FAIL - DESCRIBE is same as SHOW COLUMNS
--error ER_TABLEACCESS_DENIED_ERROR
DESCRIBE t1;
--echo # Should FAIL - SHOW INDEX
--error ER_TABLEACCESS_DENIED_ERROR
SHOW INDEX FROM t1;
connection default;
disconnect con1;
DROP USER show_user;
--echo #
--echo # DENY precedence - DENY overrides GRANT
--echo #
CREATE USER prec_user;
--echo # First GRANT, then DENY
GRANT SELECT,INSERT ON *.* TO prec_user;
DENY SELECT ON *.* TO prec_user;
connect (con1, localhost, prec_user,,test);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM t1;
connection default;
disconnect con1;
--echo # Reverse order: DENY first, then GRANT
REVOKE SELECT ON *.* FROM prec_user;
DENY SELECT ON *.* TO prec_user;
GRANT SELECT ON *.* TO prec_user;
connect (con1, localhost, prec_user,,test);
--echo # Should STILL FAIL - DENY takes precedence
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM t1;
connection default;
disconnect con1;
DROP USER prec_user;
--echo #
--echo # Mixed operations - some denied, some allowed
--echo #
CREATE USER mixed_user;
GRANT ALL PRIVILEGES ON test.* TO mixed_user;
DENY SELECT, DELETE ON *.* TO mixed_user;
connect (con1, localhost, mixed_user,,test);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM t1;
--error ER_TABLEACCESS_DENIED_ERROR
DELETE FROM t1 WHERE id=1;
--echo # Should SUCCEED
INSERT INTO t1 VALUES (4, 'data4');
--echo # Denied because of WHERE clause in UPDATE
--error ER_COLUMNACCESS_DENIED_ERROR
UPDATE t1 SET data='modified' WHERE id=4;
connection default;
disconnect con1;
DROP USER mixed_user;
--echo #
--echo # DENY with subquery
--echo #
CREATE USER sub_user;
GRANT ALL PRIVILEGES ON test.* TO sub_user;
DENY SELECT ON *.* TO sub_user;
connect (con1, localhost, sub_user,,test);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM (SELECT * FROM t1) AS dt;
disconnect con1;
connection default;
--echo #
--echo # Check that FLUSH PRIVILEGES retains DENYs
--echo #
FLUSH PRIVILEGES;
connect (con1, localhost, sub_user,,test);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM (SELECT * FROM t1) AS dt;
disconnect con1;
connection default;
DROP USER sub_user;
--echo #
--echo # Cleanup
--echo #
DROP TABLE t1, t2;

View File

@@ -0,0 +1,136 @@
#
# Test DENY privilege hierarchy: GLOBAL > DB > TABLE > COLUMN
#
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE t1 (id INT, name VARCHAR(50), salary INT);
CREATE USER 'u1'@'localhost';
#
# DB-level DENY blocks TABLE-level GRANT
#
DENY SELECT ON testdb.* TO 'u1'@'localhost';
GRANT SELECT ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.* FROM 'u1'@'localhost';
REVOKE SELECT ON testdb.t1 FROM 'u1'@'localhost';
#
# GLOBAL DENY blocks DB and TABLE grants
#
DENY UPDATE ON *.* TO 'u1'@'localhost';
GRANT UPDATE ON testdb.* TO 'u1'@'localhost';
GRANT UPDATE ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
UPDATE testdb.t1 SET name='test' WHERE id=1;
ERROR 42000: UPDATE command denied to user 'u1'@'localhost' for table `testdb`.`t1`
connection default;
disconnect con1;
REVOKE DENY UPDATE ON *.* FROM 'u1'@'localhost';
REVOKE UPDATE ON testdb.* FROM 'u1'@'localhost';
REVOKE UPDATE ON testdb.t1 FROM 'u1'@'localhost';
#
# TABLE DENY blocks COLUMN grants
#
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
GRANT SELECT (name) ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT name FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT (name) ON testdb.t1 FROM 'u1'@'localhost';
#
# DENY on same table masks only denied privilege
#
GRANT SELECT, INSERT ON testdb.t1 TO 'u1'@'localhost';
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
INSERT INTO testdb.t1 VALUES (1, 'test', 1000);
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT, INSERT ON testdb.t1 FROM 'u1'@'localhost';
DELETE FROM testdb.t1;
#
# Multiple DENY levels cumulative
#
DENY SELECT ON *.* TO 'u1'@'localhost';
DENY INSERT ON testdb.* TO 'u1'@'localhost';
DENY UPDATE ON testdb.t1 TO 'u1'@'localhost';
GRANT ALL PRIVILEGES ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
INSERT INTO testdb.t1 VALUES (1, 'test', 1000);
ERROR 42000: INSERT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
UPDATE testdb.t1 SET name='test';
ERROR 42000: UPDATE command denied to user 'u1'@'localhost' for table `testdb`.`t1`
DELETE FROM testdb.t1 WHERE id=999;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for column 'id' in table 't1'
DELETE FROM testdb.t1;
connection default;
disconnect con1;
REVOKE DENY SELECT ON *.* FROM 'u1'@'localhost';
REVOKE DENY INSERT ON testdb.* FROM 'u1'@'localhost';
REVOKE DENY UPDATE ON testdb.t1 FROM 'u1'@'localhost';
REVOKE ALL PRIVILEGES ON testdb.t1 FROM 'u1'@'localhost';
#
# REVOKE DENY restores access
#
DENY SELECT ON testdb.* TO 'u1'@'localhost';
GRANT SELECT ON testdb.t1 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.* FROM 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
id name salary
connection default;
disconnect con1;
REVOKE SELECT ON testdb.t1 FROM 'u1'@'localhost';
#
# Cache invalidation on DENY change
#
GRANT SELECT ON testdb.* TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
id name salary
connection default;
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
connection con1;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
connection default;
disconnect con1;
REVOKE SELECT ON testdb.* FROM 'u1'@'localhost';
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
#
# DENY on one table, GRANT on another
#
CREATE TABLE t2 (dept_id INT);
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
GRANT SELECT ON testdb.t2 TO 'u1'@'localhost';
connect con1, localhost, u1,,;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
SELECT * FROM testdb.t2;
dept_id
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT ON testdb.t2 FROM 'u1'@'localhost';
#
# Cleanup
#
DROP USER 'u1'@'localhost';
DROP TABLE t1, t2;
DROP DATABASE testdb;

View File

@@ -0,0 +1,167 @@
--source include/not_embedded.inc
--source include/no_protocol.inc
--echo #
--echo # Test DENY privilege hierarchy: GLOBAL > DB > TABLE > COLUMN
--echo #
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE t1 (id INT, name VARCHAR(50), salary INT);
CREATE USER 'u1'@'localhost';
--echo #
--echo # DB-level DENY blocks TABLE-level GRANT
--echo #
DENY SELECT ON testdb.* TO 'u1'@'localhost';
GRANT SELECT ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.* FROM 'u1'@'localhost';
REVOKE SELECT ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # GLOBAL DENY blocks DB and TABLE grants
--echo #
DENY UPDATE ON *.* TO 'u1'@'localhost';
GRANT UPDATE ON testdb.* TO 'u1'@'localhost';
GRANT UPDATE ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
UPDATE testdb.t1 SET name='test' WHERE id=1;
connection default;
disconnect con1;
REVOKE DENY UPDATE ON *.* FROM 'u1'@'localhost';
REVOKE UPDATE ON testdb.* FROM 'u1'@'localhost';
REVOKE UPDATE ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # TABLE DENY blocks COLUMN grants
--echo #
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
GRANT SELECT (name) ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT name FROM testdb.t1;
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT (name) ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # DENY on same table masks only denied privilege
--echo #
GRANT SELECT, INSERT ON testdb.t1 TO 'u1'@'localhost';
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
INSERT INTO testdb.t1 VALUES (1, 'test', 1000);
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT, INSERT ON testdb.t1 FROM 'u1'@'localhost';
DELETE FROM testdb.t1;
--echo #
--echo # Multiple DENY levels cumulative
--echo #
DENY SELECT ON *.* TO 'u1'@'localhost';
DENY INSERT ON testdb.* TO 'u1'@'localhost';
DENY UPDATE ON testdb.t1 TO 'u1'@'localhost';
GRANT ALL PRIVILEGES ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
--error ER_TABLEACCESS_DENIED_ERROR
INSERT INTO testdb.t1 VALUES (1, 'test', 1000);
--error ER_TABLEACCESS_DENIED_ERROR
UPDATE testdb.t1 SET name='test';
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM testdb.t1 WHERE id=999; # select required
DELETE FROM testdb.t1; # Should work
connection default;
disconnect con1;
REVOKE DENY SELECT ON *.* FROM 'u1'@'localhost';
REVOKE DENY INSERT ON testdb.* FROM 'u1'@'localhost';
REVOKE DENY UPDATE ON testdb.t1 FROM 'u1'@'localhost';
REVOKE ALL PRIVILEGES ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # REVOKE DENY restores access
--echo #
DENY SELECT ON testdb.* TO 'u1'@'localhost';
GRANT SELECT ON testdb.t1 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.* FROM 'u1'@'localhost';
connect (con1, localhost, u1,,);
SELECT * FROM testdb.t1; # Now works
connection default;
disconnect con1;
REVOKE SELECT ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # Cache invalidation on DENY change
--echo #
GRANT SELECT ON testdb.* TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
SELECT * FROM testdb.t1; # Works
connection default;
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
connection con1;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1; # Now fails (cache cleared)
connection default;
disconnect con1;
REVOKE SELECT ON testdb.* FROM 'u1'@'localhost';
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
--echo #
--echo # DENY on one table, GRANT on another
--echo #
CREATE TABLE t2 (dept_id INT);
DENY SELECT ON testdb.t1 TO 'u1'@'localhost';
GRANT SELECT ON testdb.t2 TO 'u1'@'localhost';
connect (con1, localhost, u1,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
SELECT * FROM testdb.t2; # Works
connection default;
disconnect con1;
REVOKE DENY SELECT ON testdb.t1 FROM 'u1'@'localhost';
REVOKE SELECT ON testdb.t2 FROM 'u1'@'localhost';
--echo #
--echo # Cleanup
--echo #
DROP USER 'u1'@'localhost';
DROP TABLE t1, t2;
DROP DATABASE testdb;

View File

@@ -0,0 +1,15 @@
#
# Malformed DENY JSON entry should warn
#
CREATE USER u@localhost;
CALL mtr.add_suppression('.*Malformed DENY entry in mysql\.global_priv at array position 0 for ''u''@''localhost'' ignored');
UPDATE mysql.global_priv
SET Priv= json_set(Priv, '$.denies',
json_array(json_object('type','column',
'db','test',
'table','t',
'bits',1)))
WHERE User='u' AND Host='localhost';
FLUSH PRIVILEGES;
DROP USER u@localhost;
FOUND 1 /Malformed DENY entry in mysql.global_priv at array position 0 for 'u'@'localhost' ignored/ in mysqld.1.err

View File

@@ -0,0 +1,25 @@
--source include/not_embedded.inc
--echo #
--echo # Malformed DENY JSON entry should warn
--echo #
CREATE USER u@localhost;
--eval CALL mtr.add_suppression('.*Malformed DENY entry in mysql\\.global_priv at array position 0 for ''u''@''localhost'' ignored')
UPDATE mysql.global_priv
SET Priv= json_set(Priv, '$.denies',
json_array(json_object('type','column',
'db','test',
'table','t',
'bits',1)))
WHERE User='u' AND Host='localhost';
FLUSH PRIVILEGES;
DROP USER u@localhost;
--let SEARCH_FILE= `SELECT @@log_error`
--let SEARCH_PATTERN= Malformed DENY entry in mysql.global_priv at array position 0 for 'u'@'localhost' ignored
--source include/search_pattern_in_file.inc

View File

@@ -0,0 +1,23 @@
#
# DENY case-sensitive matching (lower_case_table_names=0)
#
CREATE USER u_deny@localhost;
CREATE DATABASE DbCase;
CREATE DATABASE dbcase;
CREATE TABLE DbCase.T (a int);
CREATE TABLE DbCase.t (a int);
CREATE TABLE dbcase.t (a int);
DENY SELECT ON DbCase.T TO u_deny@localhost;
DENY SELECT ON DbCase.t TO u_deny@localhost;
DENY INSERT, UPDATE ON dbcase.t TO u_deny@localhost;
# SHOW GRANTS should list distinct denies for case-different objects
SHOW GRANTS FOR u_deny@localhost;
Grants for u_deny@localhost
DENY INSERT, UPDATE ON `dbcase`.`t` TO `u_deny`@`localhost`
DENY SELECT ON `DbCase`.`T` TO `u_deny`@`localhost`
DENY SELECT ON `DbCase`.`t` TO `u_deny`@`localhost`
GRANT USAGE ON *.* TO `u_deny`@`localhost`
# END SHOW GRANTS
DROP USER u_deny@localhost;
DROP DATABASE DbCase;
DROP DATABASE dbcase;

View File

@@ -0,0 +1,26 @@
--source include/not_embedded.inc
--source include/have_lowercase0.inc
--echo #
--echo # DENY case-sensitive matching (lower_case_table_names=0)
--echo #
CREATE USER u_deny@localhost;
CREATE DATABASE DbCase;
CREATE DATABASE dbcase;
CREATE TABLE DbCase.T (a int);
CREATE TABLE DbCase.t (a int);
CREATE TABLE dbcase.t (a int);
DENY SELECT ON DbCase.T TO u_deny@localhost;
DENY SELECT ON DbCase.t TO u_deny@localhost;
DENY INSERT, UPDATE ON dbcase.t TO u_deny@localhost;
--echo # SHOW GRANTS should list distinct denies for case-different objects
--sorted_result
SHOW GRANTS FOR u_deny@localhost;
--echo # END SHOW GRANTS
DROP USER u_deny@localhost;
DROP DATABASE DbCase;
DROP DATABASE dbcase;

View File

@@ -0,0 +1 @@
--lower-case-table-names=1

View File

@@ -0,0 +1,54 @@
#
# DENY case-insensitive matching (lower_case_table_names=1)
#
CREATE USER u_deny@localhost;
CREATE DATABASE DbCase;
CREATE TABLE DbCase.T (a int);
DENY SELECT ON DbCase.T TO u_deny@localhost;
DENY INSERT, UPDATE ON dbcase.t TO u_deny@localhost;
GRANT SELECT, INSERT, UPDATE ON DbCase.T TO u_deny@localhost;
# DENY entry in mysql.global_priv should be lowercased
SELECT JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].db')) AS db,
JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].table')) AS tbl,
JSON_EXTRACT(Priv, '$.denies[0].bits') AS bits
FROM mysql.global_priv
WHERE User='u_deny' AND Host='localhost';
db tbl bits
dbcase t 7
# Force mixed-case JSON to simulate migration from case-sensitive to lowercase=1
UPDATE mysql.global_priv
SET Priv=JSON_SET(Priv, '$.denies[0].db','DbCase', '$.denies[0].table','T')
WHERE User='u_deny' AND Host='localhost';
SELECT JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].db')) AS db,
JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].table')) AS tbl,
JSON_EXTRACT(Priv, '$.denies[0].bits') AS bits
FROM mysql.global_priv
WHERE User='u_deny' AND Host='localhost';
db tbl bits
DbCase T 7
FLUSH PRIVILEGES;
connect con1,localhost,u_deny,,DbCase;
SELECT * FROM T;
ERROR 42000: SELECT command denied to user 'u_deny'@'localhost' for table `dbcase`.`t`
INSERT INTO T VALUES (1);
ERROR 42000: INSERT command denied to user 'u_deny'@'localhost' for table `dbcase`.`t`
UPDATE T SET a=2 WHERE a=1;
ERROR 42000: UPDATE command denied to user 'u_deny'@'localhost' for table `dbcase`.`t`
disconnect con1;
connection default;
# SHOW GRANTS should collapse to a single deny entry
SHOW GRANTS FOR u_deny@localhost;
Grants for u_deny@localhost
DENY SELECT, INSERT, UPDATE ON `dbcase`.`t` TO `u_deny`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `dbcase`.`t` TO `u_deny`@`localhost`
GRANT USAGE ON *.* TO `u_deny`@`localhost`
# END SHOW GRANTS
REVOKE DENY SELECT, INSERT, UPDATE ON DBCASE.T FROM u_deny@localhost;
# SHOW GRANTS should no longer list a DENY entry
SHOW GRANTS FOR u_deny@localhost;
Grants for u_deny@localhost
GRANT SELECT, INSERT, UPDATE ON `dbcase`.`t` TO `u_deny`@`localhost`
GRANT USAGE ON *.* TO `u_deny`@`localhost`
# END SHOW GRANTS
DROP USER u_deny@localhost;
DROP DATABASE DbCase;

View File

@@ -0,0 +1,59 @@
--source include/not_embedded.inc
--source include/have_lowercase1.inc
--echo #
--echo # DENY case-insensitive matching (lower_case_table_names=1)
--echo #
CREATE USER u_deny@localhost;
CREATE DATABASE DbCase;
CREATE TABLE DbCase.T (a int);
DENY SELECT ON DbCase.T TO u_deny@localhost;
DENY INSERT, UPDATE ON dbcase.t TO u_deny@localhost;
GRANT SELECT, INSERT, UPDATE ON DbCase.T TO u_deny@localhost;
--echo # DENY entry in mysql.global_priv should be lowercased
SELECT JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].db')) AS db,
JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].table')) AS tbl,
JSON_EXTRACT(Priv, '$.denies[0].bits') AS bits
FROM mysql.global_priv
WHERE User='u_deny' AND Host='localhost';
--echo # Force mixed-case JSON to simulate migration from case-sensitive to lowercase=1
UPDATE mysql.global_priv
SET Priv=JSON_SET(Priv, '$.denies[0].db','DbCase', '$.denies[0].table','T')
WHERE User='u_deny' AND Host='localhost';
SELECT JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].db')) AS db,
JSON_UNQUOTE(JSON_EXTRACT(Priv, '$.denies[0].table')) AS tbl,
JSON_EXTRACT(Priv, '$.denies[0].bits') AS bits
FROM mysql.global_priv
WHERE User='u_deny' AND Host='localhost';
FLUSH PRIVILEGES;
--connect (con1,localhost,u_deny,,DbCase)
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM T;
--error ER_TABLEACCESS_DENIED_ERROR
INSERT INTO T VALUES (1);
--error ER_TABLEACCESS_DENIED_ERROR
UPDATE T SET a=2 WHERE a=1;
disconnect con1;
connection default;
--echo # SHOW GRANTS should collapse to a single deny entry
--sorted_result
SHOW GRANTS FOR u_deny@localhost;
--echo # END SHOW GRANTS
REVOKE DENY SELECT, INSERT, UPDATE ON DBCASE.T FROM u_deny@localhost;
--echo # SHOW GRANTS should no longer list a DENY entry
--sorted_result
SHOW GRANTS FOR u_deny@localhost;
--echo # END SHOW GRANTS
DROP USER u_deny@localhost;
DROP DATABASE DbCase;

View File

@@ -0,0 +1,78 @@
#
# DENY metadata behavior
#
#
# Direct DENY should affect SHOW COLUMNS
#
CREATE TABLE t1 (id INT);
CREATE TABLE t2 (c1 INT);
CREATE USER u@localhost;
CREATE ROLE r;
GRANT SELECT ON test.t1 TO r;
GRANT SELECT ON test.t2 TO r;
DENY SELECT ON test.t1 TO r;
GRANT r TO u@localhost;
connect con1, localhost, u,,;
SET ROLE r;
SHOW COLUMNS FROM test.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `test`.`t1`
SHOW COLUMNS FROM test.t2;
Field Type Null Key Default Extra
c1 int(11) YES NULL
disconnect con1;
connection default;
DROP ROLE r;
DROP USER u@localhost;
DROP TABLE t1;
#
# Column-level GRANT+DENY should block SHOW COLUMNS
#
CREATE TABLE t1 (a INT, b INT);
CREATE USER u@localhost;
GRANT SELECT (a) ON test.t1 TO u@localhost;
DENY SELECT (a) ON test.t1 TO u@localhost;
connect con2, localhost, u,,;
SHOW COLUMNS FROM test.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `test`.`t1`
disconnect con2;
connection default;
DROP USER u@localhost;
DROP TABLE t1;
#
# Mixed column GRANT+DENY should allow SHOW COLUMNS
#
CREATE TABLE t1 (a INT, b INT);
CREATE USER u@localhost;
GRANT SELECT (a) ON test.t1 TO u@localhost;
DENY SELECT (b) ON test.t1 TO u@localhost;
connect con2, localhost, u,,;
SHOW COLUMNS FROM test.t1;
Field Type Null Key Default Extra
a int(11) YES NULL
disconnect con2;
connection default;
DROP USER u@localhost;
DROP TABLE t1;
#
# Higher level DENY should block SHOW COLUMNS
#
CREATE TABLE t1 (a INT);
CREATE USER u@localhost;
CREATE ROLE r;
GRANT SELECT ON test.t1 TO r;
GRANT r TO u@localhost;
#
# Database-level DENY
#
DENY SELECT ON test.* TO r;
connect con3, localhost, u,,;
SET ROLE r;
SHOW COLUMNS FROM test.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `test`.`t1`
disconnect con3;
connection default;
REVOKE DENY SELECT ON test.* FROM r;
DROP ROLE r;
DROP USER u@localhost;
DROP TABLE t1;
DROP TABLE t2;

View File

@@ -0,0 +1,89 @@
--source include/not_embedded.inc
--echo #
--echo # DENY metadata behavior
--echo #
--echo #
--echo # Direct DENY should affect SHOW COLUMNS
--echo #
CREATE TABLE t1 (id INT);
CREATE TABLE t2 (c1 INT);
CREATE USER u@localhost;
CREATE ROLE r;
GRANT SELECT ON test.t1 TO r;
GRANT SELECT ON test.t2 TO r;
DENY SELECT ON test.t1 TO r;
GRANT r TO u@localhost;
connect (con1, localhost, u,,);
SET ROLE r;
--error ER_TABLEACCESS_DENIED_ERROR
SHOW COLUMNS FROM test.t1;
SHOW COLUMNS FROM test.t2;
disconnect con1;
connection default;
DROP ROLE r;
DROP USER u@localhost;
DROP TABLE t1;
--echo #
--echo # Column-level GRANT+DENY should block SHOW COLUMNS
--echo #
CREATE TABLE t1 (a INT, b INT);
CREATE USER u@localhost;
GRANT SELECT (a) ON test.t1 TO u@localhost;
DENY SELECT (a) ON test.t1 TO u@localhost;
connect (con2, localhost, u,,);
--error ER_TABLEACCESS_DENIED_ERROR
SHOW COLUMNS FROM test.t1;
disconnect con2;
connection default;
DROP USER u@localhost;
DROP TABLE t1;
--echo #
--echo # Mixed column GRANT+DENY should allow SHOW COLUMNS
--echo #
CREATE TABLE t1 (a INT, b INT);
CREATE USER u@localhost;
GRANT SELECT (a) ON test.t1 TO u@localhost;
DENY SELECT (b) ON test.t1 TO u@localhost;
connect (con2, localhost, u,,);
SHOW COLUMNS FROM test.t1;
disconnect con2;
connection default;
DROP USER u@localhost;
DROP TABLE t1;
--echo #
--echo # Higher level DENY should block SHOW COLUMNS
--echo #
CREATE TABLE t1 (a INT);
CREATE USER u@localhost;
CREATE ROLE r;
GRANT SELECT ON test.t1 TO r;
GRANT r TO u@localhost;
--echo #
--echo # Database-level DENY
--echo #
DENY SELECT ON test.* TO r;
connect (con3, localhost, u,,);
SET ROLE r;
--error ER_TABLEACCESS_DENIED_ERROR
SHOW COLUMNS FROM test.t1;
disconnect con3;
connection default;
REVOKE DENY SELECT ON test.* FROM r;
DROP ROLE r;
DROP USER u@localhost;
DROP TABLE t1;
DROP TABLE t2;

View File

@@ -0,0 +1,34 @@
#
# Check that privilege hashes are populated with DENY entries on startup
#
USE test;
CREATE TABLE t1 (id INT);
CREATE USER u@localhost;
GRANT SELECT ON test.t1 TO u@localhost;
DENY SELECT ON test.t1 TO u@localhost;
# Before restart: DENY is enforced
connect con1, localhost, u,,;
SELECT * FROM test.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `test`.`t1`
disconnect con1;
connection default;
# Before restart: DENY is visible and persisted
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT ON `test`.`t1` TO `u`@`localhost`
DENY SELECT ON `test`.`t1` TO `u`@`localhost`
# Restart and verify again
# restart
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT ON `test`.`t1` TO `u`@`localhost`
DENY SELECT ON `test`.`t1` TO `u`@`localhost`
connect con1, localhost, u,,;
SELECT * FROM test.t1;
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `test`.`t1`
disconnect con1;
connection default;
DROP USER u@localhost;
DROP TABLE test.t1;

View File

@@ -0,0 +1,36 @@
--source include/not_embedded.inc
--echo #
--echo # Check that privilege hashes are populated with DENY entries on startup
--echo #
USE test;
CREATE TABLE t1 (id INT);
CREATE USER u@localhost;
GRANT SELECT ON test.t1 TO u@localhost;
DENY SELECT ON test.t1 TO u@localhost;
--echo # Before restart: DENY is enforced
connect (con1, localhost, u,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM test.t1;
disconnect con1;
connection default;
--echo # Before restart: DENY is visible and persisted
SHOW GRANTS FOR u@localhost;
--echo # Restart and verify again
--source include/restart_mysqld.inc
SHOW GRANTS FOR u@localhost;
connect (con1, localhost, u,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM test.t1;
disconnect con1;
connection default;
DROP USER u@localhost;
DROP TABLE test.t1;

View File

@@ -0,0 +1,162 @@
#
# REVOKE DENY tests
#
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1() BEGIN SET @x=1; END;
$$
CREATE FUNCTION f1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
CREATE USER u@localhost;
GRANT ALL PRIVILEGES ON *.* to u@localhost;
PREPARE show_denies FROM "SELECT IF(JSON_EXTRACT(Priv, '$.denies') IS NULL OR JSON_LENGTH(JSON_EXTRACT(Priv, '$.denies')) = 0, 'no entries', JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies'))) AS denies FROM mysql.global_priv WHERE User='u' AND Host='localhost'";
#
# Global
#
DENY SELECT ON *.* TO u@localhost;
REVOKE DENY SELECT ON *.* FROM u@localhost;
EXECUTE show_denies;
denies
no entries
#
# Database
#
DENY INSERT ON test.* TO u@localhost;
REVOKE DENY INSERT ON test.* FROM u@localhost;
EXECUTE show_denies;
denies
no entries
# Second revoke should error
REVOKE DENY INSERT ON test.* FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost'
# Revoke with existing GRANT without existing DENY
GRANT INSERT ON test.* TO u@localhost;
REVOKE DENY INSERT ON test.* FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost'
REVOKE INSERT ON test.* FROM u@localhost;
#
# Table
#
DENY UPDATE ON test.t1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT ALL PRIVILEGES ON *.* TO `u`@`localhost`
DENY UPDATE ON `test`.`t1` TO `u`@`localhost`
# END SHOW GRANTS
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
EXECUTE show_denies;
denies
no entries
# Second revoke should error
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on table 't1'
# Revoke with existing GRANT without existing DENY
GRANT UPDATE ON test.t1 TO u@localhost;
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on table 't1'
REVOKE UPDATE ON test.t1 FROM u@localhost;
#
# Column
#
DENY SELECT (a) ON test.t1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT ALL PRIVILEGES ON *.* TO `u`@`localhost`
DENY SELECT (`a`) ON `test`.`t1` TO `u`@`localhost`
# END SHOW GRANTS
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
EXECUTE show_denies;
denies
no entries
# Second revoke should error
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on table 't1'
# Revoke with existing GRANT without existing DENY
GRANT SELECT(a) ON test.t1 TO u@localhost;
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on table 't1'
REVOKE SELECT(a) ON test.t1 FROM u@localhost;
#
# Procedure
#
DENY EXECUTE ON PROCEDURE test.p1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT ALL PRIVILEGES ON *.* TO `u`@`localhost`
DENY EXECUTE ON PROCEDURE `test`.`p1` TO `u`@`localhost`
# END SHOW GRANTS
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
EXECUTE show_denies;
denies
no entries
# Second revoke should error
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on routine 'p1'
# Revoke with existing GRANT without existing DENY
GRANT EXECUTE ON PROCEDURE test.p1 TO u@localhost;
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on routine 'p1'
REVOKE EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
#
# Function
#
DENY EXECUTE ON FUNCTION test.f1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT ALL PRIVILEGES ON *.* TO `u`@`localhost`
DENY EXECUTE ON FUNCTION `test`.`f1` TO `u`@`localhost`
# END SHOW GRANTS
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
EXECUTE show_denies;
denies
no entries
# Second revoke should error
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on routine 'f1'
# Revoke with existing GRANT without existing DENY
GRANT EXECUTE ON FUNCTION test.f1 TO u@localhost;
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
ERROR 42000: There is no such grant defined for user 'u' on host 'localhost' on routine 'f1'
REVOKE EXECUTE ON FUNCTION test.f1 FROM u@localhost;
#
# REVOKE ALL, GRANT OPTION clears grants and denies
#
DENY SELECT ON test.* TO u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT ALL PRIVILEGES ON *.* TO `u`@`localhost`
DENY SELECT ON `test`.* TO `u`@`localhost`
# END SHOW GRANTS
EXECUTE show_denies;
denies
[
{
"type": "db",
"db": "test",
"bits": 1
}
]
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u@localhost;
SHOW GRANTS FOR u@localhost;
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
# END SHOW GRANTS
EXECUTE show_denies;
denies
no entries
DEALLOCATE PREPARE show_denies;
DROP USER u@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP FUNCTION f1;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@@ -0,0 +1,140 @@
--source include/not_embedded.inc
--echo #
--echo # REVOKE DENY tests
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1() BEGIN SET @x=1; END;
$$
CREATE FUNCTION f1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
DELIMITER ;$$
CREATE USER u@localhost;
GRANT ALL PRIVILEGES ON *.* to u@localhost;
PREPARE show_denies FROM "SELECT IF(JSON_EXTRACT(Priv, '$.denies') IS NULL OR JSON_LENGTH(JSON_EXTRACT(Priv, '$.denies')) = 0, 'no entries', JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies'))) AS denies FROM mysql.global_priv WHERE User='u' AND Host='localhost'";
--echo #
--echo # Global
--echo #
DENY SELECT ON *.* TO u@localhost;
REVOKE DENY SELECT ON *.* FROM u@localhost;
EXECUTE show_denies;
--echo #
--echo # Database
--echo #
DENY INSERT ON test.* TO u@localhost;
REVOKE DENY INSERT ON test.* FROM u@localhost;
EXECUTE show_denies;
--echo # Second revoke should error
--error ER_NONEXISTING_GRANT
REVOKE DENY INSERT ON test.* FROM u@localhost;
--echo # Revoke with existing GRANT without existing DENY
GRANT INSERT ON test.* TO u@localhost;
--error ER_NONEXISTING_GRANT
REVOKE DENY INSERT ON test.* FROM u@localhost;
REVOKE INSERT ON test.* FROM u@localhost;
--echo #
--echo # Table
--echo #
DENY UPDATE ON test.t1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
EXECUTE show_denies;
--echo # Second revoke should error
--error ER_NONEXISTING_TABLE_GRANT
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
--echo # Revoke with existing GRANT without existing DENY
GRANT UPDATE ON test.t1 TO u@localhost;
--error ER_NONEXISTING_TABLE_GRANT
REVOKE DENY UPDATE ON test.t1 FROM u@localhost;
REVOKE UPDATE ON test.t1 FROM u@localhost;
--echo #
--echo # Column
--echo #
DENY SELECT (a) ON test.t1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
EXECUTE show_denies;
--echo # Second revoke should error
--error ER_NONEXISTING_TABLE_GRANT
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
--echo # Revoke with existing GRANT without existing DENY
GRANT SELECT(a) ON test.t1 TO u@localhost;
--error ER_NONEXISTING_TABLE_GRANT
REVOKE DENY SELECT (a) ON test.t1 FROM u@localhost;
REVOKE SELECT(a) ON test.t1 FROM u@localhost;
--echo #
--echo # Procedure
--echo #
DENY EXECUTE ON PROCEDURE test.p1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
EXECUTE show_denies;
--echo # Second revoke should error
--error ER_NONEXISTING_PROC_GRANT
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
--echo # Revoke with existing GRANT without existing DENY
GRANT EXECUTE ON PROCEDURE test.p1 TO u@localhost;
--error ER_NONEXISTING_PROC_GRANT
REVOKE DENY EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
REVOKE EXECUTE ON PROCEDURE test.p1 FROM u@localhost;
--echo #
--echo # Function
--echo #
DENY EXECUTE ON FUNCTION test.f1 TO u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
EXECUTE show_denies;
--echo # Second revoke should error
--error ER_NONEXISTING_PROC_GRANT
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
--echo # Revoke with existing GRANT without existing DENY
GRANT EXECUTE ON FUNCTION test.f1 TO u@localhost;
--error ER_NONEXISTING_PROC_GRANT
REVOKE DENY EXECUTE ON FUNCTION test.f1 FROM u@localhost;
REVOKE EXECUTE ON FUNCTION test.f1 FROM u@localhost;
--echo #
--echo # REVOKE ALL, GRANT OPTION clears grants and denies
--echo #
DENY SELECT ON test.* TO u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
EXECUTE show_denies;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u@localhost;
SHOW GRANTS FOR u@localhost;
--echo # END SHOW GRANTS
EXECUTE show_denies;
DEALLOCATE PREPARE show_denies;
DROP USER u@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP FUNCTION f1;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@@ -0,0 +1,99 @@
#
# Test DENY + role inheritance: denies on a role propagate
# through the role graph to users and other roles.
#
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE t1 (id INT, val VARCHAR(50));
INSERT INTO t1 VALUES (1,'a'), (2,'b');
CREATE USER u1@localhost;
CREATE ROLE r_grant, r_deny, r_combined;
#
# Setup: r_grant has SELECT, r_deny has DENY SELECT,
# r_combined inherits both. User u1 gets r_combined.
#
GRANT SELECT ON testdb.t1 TO r_grant;
DENY SELECT ON testdb.t1 TO r_deny;
GRANT r_grant TO r_combined;
GRANT r_deny TO r_combined;
GRANT r_combined TO u1@localhost;
#
# DENY from r_deny should win over GRANT from r_grant
#
connect con1, localhost, u1,,;
SET ROLE r_combined;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `testdb`.`t1`
disconnect con1;
connection default;
#
# After revoking the deny, SELECT should work again
#
REVOKE DENY SELECT ON testdb.t1 FROM r_deny;
connect con1, localhost, u1,,;
SET ROLE r_combined;
SELECT * FROM testdb.t1;
id val
1 a
2 b
disconnect con1;
connection default;
# Cleanup scenario 1
REVOKE SELECT ON testdb.t1 FROM r_grant;
DROP ROLE r_grant, r_deny, r_combined;
DROP USER u1@localhost;
#
# DB-level DENY on a role blocks table-level GRANT
# inherited through a different role in the DAG.
#
CREATE USER u2@localhost;
CREATE ROLE r_db_deny, r_tbl_grant, r_top;
DENY SELECT ON testdb.* TO r_db_deny;
GRANT SELECT ON testdb.t1 TO r_tbl_grant;
GRANT r_db_deny TO r_top;
GRANT r_tbl_grant TO r_top;
GRANT r_top TO u2@localhost;
connect con2, localhost, u2,,;
SET ROLE r_top;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u2'@'localhost' for table `testdb`.`t1`
disconnect con2;
connection default;
# Cleanup scenario 2
DROP ROLE r_db_deny, r_tbl_grant, r_top;
DROP USER u2@localhost;
#
# DENY on a leaf role, grant on a higher role:
# deny should still win (deny always wins over grant).
#
CREATE USER u3@localhost;
CREATE ROLE r_child, r_parent;
GRANT r_child TO r_parent;
GRANT r_parent TO u3@localhost;
GRANT SELECT ON testdb.t1 TO r_parent;
DENY SELECT ON testdb.t1 TO r_child;
connect con3, localhost, u3,,;
SET ROLE r_parent;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u3'@'localhost' for table `testdb`.`t1`
disconnect con3;
connection default;
#
# Reverse: deny on parent, grant on child — deny still wins.
#
REVOKE DENY SELECT ON testdb.t1 FROM r_child;
REVOKE SELECT ON testdb.t1 FROM r_parent;
GRANT SELECT ON testdb.t1 TO r_child;
DENY SELECT ON testdb.t1 TO r_parent;
connect con3, localhost, u3,,;
SET ROLE r_parent;
SELECT * FROM testdb.t1;
ERROR 42000: SELECT command denied to user 'u3'@'localhost' for table `testdb`.`t1`
disconnect con3;
connection default;
# Cleanup scenario 3
DROP ROLE r_child, r_parent;
DROP USER u3@localhost;
# Final cleanup
DROP TABLE testdb.t1;
DROP DATABASE testdb;

View File

@@ -0,0 +1,115 @@
--source include/not_embedded.inc
--source include/no_protocol.inc
--echo #
--echo # Test DENY + role inheritance: denies on a role propagate
--echo # through the role graph to users and other roles.
--echo #
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE t1 (id INT, val VARCHAR(50));
INSERT INTO t1 VALUES (1,'a'), (2,'b');
CREATE USER u1@localhost;
CREATE ROLE r_grant, r_deny, r_combined;
--echo #
--echo # Setup: r_grant has SELECT, r_deny has DENY SELECT,
--echo # r_combined inherits both. User u1 gets r_combined.
--echo #
GRANT SELECT ON testdb.t1 TO r_grant;
DENY SELECT ON testdb.t1 TO r_deny;
GRANT r_grant TO r_combined;
GRANT r_deny TO r_combined;
GRANT r_combined TO u1@localhost;
--echo #
--echo # DENY from r_deny should win over GRANT from r_grant
--echo #
connect (con1, localhost, u1,,);
SET ROLE r_combined;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
disconnect con1;
connection default;
--echo #
--echo # After revoking the deny, SELECT should work again
--echo #
REVOKE DENY SELECT ON testdb.t1 FROM r_deny;
connect (con1, localhost, u1,,);
SET ROLE r_combined;
SELECT * FROM testdb.t1;
disconnect con1;
connection default;
--echo # Cleanup scenario 1
REVOKE SELECT ON testdb.t1 FROM r_grant;
DROP ROLE r_grant, r_deny, r_combined;
DROP USER u1@localhost;
--echo #
--echo # DB-level DENY on a role blocks table-level GRANT
--echo # inherited through a different role in the DAG.
--echo #
CREATE USER u2@localhost;
CREATE ROLE r_db_deny, r_tbl_grant, r_top;
DENY SELECT ON testdb.* TO r_db_deny;
GRANT SELECT ON testdb.t1 TO r_tbl_grant;
GRANT r_db_deny TO r_top;
GRANT r_tbl_grant TO r_top;
GRANT r_top TO u2@localhost;
connect (con2, localhost, u2,,);
SET ROLE r_top;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
disconnect con2;
connection default;
--echo # Cleanup scenario 2
DROP ROLE r_db_deny, r_tbl_grant, r_top;
DROP USER u2@localhost;
--echo #
--echo # DENY on a leaf role, grant on a higher role:
--echo # deny should still win (deny always wins over grant).
--echo #
CREATE USER u3@localhost;
CREATE ROLE r_child, r_parent;
GRANT r_child TO r_parent;
GRANT r_parent TO u3@localhost;
GRANT SELECT ON testdb.t1 TO r_parent;
DENY SELECT ON testdb.t1 TO r_child;
connect (con3, localhost, u3,,);
SET ROLE r_parent;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
disconnect con3;
connection default;
--echo #
--echo # Reverse: deny on parent, grant on child — deny still wins.
--echo #
REVOKE DENY SELECT ON testdb.t1 FROM r_child;
REVOKE SELECT ON testdb.t1 FROM r_parent;
GRANT SELECT ON testdb.t1 TO r_child;
DENY SELECT ON testdb.t1 TO r_parent;
connect (con3, localhost, u3,,);
SET ROLE r_parent;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM testdb.t1;
disconnect con3;
connection default;
--echo # Cleanup scenario 3
DROP ROLE r_child, r_parent;
DROP USER u3@localhost;
--echo # Final cleanup
DROP TABLE testdb.t1;
DROP DATABASE testdb;

View File

@@ -0,0 +1,106 @@
#
# DENY with roles of roles and PUBLIC
#
CREATE TABLE test.t1 (id INT);
INSERT INTO test.t1 VALUES (1);
#
# DENY on base role should block through role inheritance
#
CREATE USER u1@localhost;
CREATE ROLE r_base, r_mid, r_top;
GRANT SELECT ON test.t1 TO r_base;
DENY SELECT ON test.t1 TO r_base;
GRANT r_base TO r_mid;
GRANT r_mid TO r_top;
GRANT r_top TO u1@localhost;
#
# SHOW GRANTS for roles and JSON storage
#
SHOW GRANTS FOR r_base;
Grants for r_base
GRANT USAGE ON *.* TO `r_base`
GRANT SELECT ON `test`.`t1` TO `r_base`
DENY SELECT ON `test`.`t1` TO `r_base`
# END SHOW GRANTS
SHOW GRANTS FOR r_mid;
Grants for r_mid
GRANT `r_base` TO `r_mid`
GRANT USAGE ON *.* TO `r_mid`
GRANT USAGE ON *.* TO `r_base`
GRANT SELECT ON `test`.`t1` TO `r_base`
DENY SELECT ON `test`.`t1` TO `r_base`
# END SHOW GRANTS
SHOW GRANTS FOR r_top;
Grants for r_top
GRANT `r_mid` TO `r_top`
GRANT USAGE ON *.* TO `r_top`
GRANT `r_base` TO `r_mid`
GRANT USAGE ON *.* TO `r_mid`
GRANT USAGE ON *.* TO `r_base`
GRANT SELECT ON `test`.`t1` TO `r_base`
DENY SELECT ON `test`.`t1` TO `r_base`
# END SHOW GRANTS
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='r_base';
User Host denies_pretty
r_base [
{
"type": "table",
"db": "test",
"table": "t1",
"bits": 1
}
]
connect con1, localhost, u1,,;
SET ROLE r_top;
SELECT * FROM test.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `test`.`t1`
disconnect con1;
connection default;
REVOKE DENY SELECT ON test.t1 FROM r_base;
connect con1, localhost, u1,,;
SET ROLE r_top;
SELECT * FROM test.t1;
id
1
disconnect con1;
connection default;
DROP ROLE r_top;
DROP ROLE r_mid;
DROP ROLE r_base;
DROP USER u1@localhost;
#
# PUBLIC DENY should affect users
#
CREATE USER u2@localhost;
GRANT SELECT ON test.t1 TO u2@localhost;
DENY SELECT ON test.t1 TO PUBLIC;
SHOW GRANTS FOR PUBLIC;
Grants for PUBLIC
DENY SELECT ON `test`.`t1` TO PUBLIC
# END SHOW GRANTS
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='PUBLIC';
User Host denies_pretty
PUBLIC [
{
"type": "table",
"db": "test",
"table": "t1",
"bits": 1
}
]
connect con2, localhost, u2,,;
SELECT * FROM test.t1;
ERROR 42000: SELECT command denied to user 'u2'@'localhost' for table `test`.`t1`
disconnect con2;
connection default;
REVOKE DENY SELECT ON test.t1 FROM PUBLIC;
DROP USER u2@localhost;
DROP TABLE test.t1;
DELETE FROM mysql.global_priv WHERE User='PUBLIC';
FLUSH PRIVILEGES;

View File

@@ -0,0 +1,79 @@
--source include/not_embedded.inc
--echo #
--echo # DENY with roles of roles and PUBLIC
--echo #
CREATE TABLE test.t1 (id INT);
INSERT INTO test.t1 VALUES (1);
--echo #
--echo # DENY on base role should block through role inheritance
--echo #
CREATE USER u1@localhost;
CREATE ROLE r_base, r_mid, r_top;
GRANT SELECT ON test.t1 TO r_base;
DENY SELECT ON test.t1 TO r_base;
GRANT r_base TO r_mid;
GRANT r_mid TO r_top;
GRANT r_top TO u1@localhost;
--echo #
--echo # SHOW GRANTS for roles and JSON storage
--echo #
SHOW GRANTS FOR r_base;
--echo # END SHOW GRANTS
SHOW GRANTS FOR r_mid;
--echo # END SHOW GRANTS
SHOW GRANTS FOR r_top;
--echo # END SHOW GRANTS
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='r_base';
connect (con1, localhost, u1,,);
SET ROLE r_top;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM test.t1;
disconnect con1;
connection default;
REVOKE DENY SELECT ON test.t1 FROM r_base;
connect (con1, localhost, u1,,);
SET ROLE r_top;
SELECT * FROM test.t1;
disconnect con1;
connection default;
DROP ROLE r_top;
DROP ROLE r_mid;
DROP ROLE r_base;
DROP USER u1@localhost;
--echo #
--echo # PUBLIC DENY should affect users
--echo #
CREATE USER u2@localhost;
GRANT SELECT ON test.t1 TO u2@localhost;
DENY SELECT ON test.t1 TO PUBLIC;
SHOW GRANTS FOR PUBLIC;
--echo # END SHOW GRANTS
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='PUBLIC';
connect (con2, localhost, u2,,);
--error ER_TABLEACCESS_DENIED_ERROR
SELECT * FROM test.t1;
disconnect con2;
connection default;
REVOKE DENY SELECT ON test.t1 FROM PUBLIC;
DROP USER u2@localhost;
DROP TABLE test.t1;
DELETE FROM mysql.global_priv WHERE User='PUBLIC';
FLUSH PRIVILEGES;

View File

@@ -0,0 +1,168 @@
#
# DENY routine privileges
#
CREATE PROCEDURE proc1() BEGIN SET @p=1; END;
$$
CREATE FUNCTION func1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
#
# Routine-level DENY for different privileges
#
CREATE USER u@localhost;
#
# Illegal routine DENY statements
#
# Missing routine type (treated as table-level grant target)
DENY EXECUTE ON test.proc1 TO u@localhost;
ERROR 42000: Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used
# SELECT is not a routine privilege (procedure target)
DENY SELECT ON PROCEDURE test.proc1 TO u@localhost;
ERROR 42000: Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used
GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE test.proc1 TO u@localhost;
GRANT EXECUTE, ALTER ROUTINE ON FUNCTION test.func1 TO u@localhost;
DENY ALTER ROUTINE ON PROCEDURE test.proc1 TO u@localhost;
DENY EXECUTE ON FUNCTION test.func1 TO u@localhost;
connect con1, localhost, u,,test;
CALL proc1();
SELECT @p;
@p
1
ALTER PROCEDURE proc1 COMMENT 'x';
ERROR 42000: alter routine command denied to user 'u'@'localhost' for routine 'test.proc1'
SELECT func1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.func1'
ALTER FUNCTION func1 COMMENT 'x';
connection default;
disconnect con1;
DROP USER u@localhost;
#
# Routine-level DENY ALL
#
CREATE USER u_all@localhost;
GRANT ALL ON PROCEDURE test.proc1 TO u_all@localhost;
GRANT ALL ON FUNCTION test.func1 TO u_all@localhost;
DENY ALL PRIVILEGES ON PROCEDURE test.proc1 TO u_all@localhost;
DENY ALL PRIVILEGES ON FUNCTION test.func1 TO u_all@localhost;
connect con1, localhost, u_all,,;
CALL test.proc1();
ERROR 42000: execute command denied to user 'u_all'@'localhost' for routine 'test.proc1'
SELECT test.func1();
ERROR 42000: execute command denied to user 'u_all'@'localhost' for routine 'test.func1'
ALTER PROCEDURE test.proc1 COMMENT 'x';
ERROR 42000: alter routine command denied to user 'u_all'@'localhost' for routine 'test.proc1'
ALTER FUNCTION test.func1 COMMENT 'x';
ERROR 42000: alter routine command denied to user 'u_all'@'localhost' for routine 'test.func1'
connection default;
disconnect con1;
DROP USER u_all@localhost;
#
# DB-level DENY overrides routine grants
#
CREATE USER u@localhost;
GRANT EXECUTE ON PROCEDURE test.proc1 TO u@localhost;
GRANT EXECUTE ON PACKAGE BODY test.pkg1 TO u@localhost;
DENY EXECUTE ON test.* TO u@localhost;
connect con1, localhost, u,,test;
CALL proc1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.proc1'
CALL pkg1.p1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.pkg1'
SELECT pkg1.f1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.pkg1'
connection default;
disconnect con1;
DROP USER u@localhost;
#
# Global DENY overrides routine grants
#
CREATE USER u@localhost;
GRANT EXECUTE ON FUNCTION test.func1 TO u@localhost;
DENY EXECUTE ON *.* TO u@localhost;
connect con1, localhost, u,,test;
SELECT func1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.func1'
connection default;
disconnect con1;
DROP USER u@localhost;
#
# Routine DENY is case-insensitive
#
CREATE USER u_case@localhost;
GRANT EXECUTE ON PROCEDURE test.proc1 TO u_case@localhost;
GRANT EXECUTE ON FUNCTION test.func1 TO u_case@localhost;
DENY EXECUTE ON PROCEDURE test.ProC1 TO u_case@localhost;
DENY EXECUTE ON FUNCTION test.FUNC1 TO u_case@localhost;
connect con_case, localhost, u_case,,;
CALL test.proc1();
ERROR 42000: execute command denied to user 'u_case'@'localhost' for routine 'test.proc1'
SELECT test.func1();
ERROR 42000: execute command denied to user 'u_case'@'localhost' for routine 'test.func1'
connection default;
disconnect con_case;
DROP USER u_case@localhost;
#
# Package / package body DENY
#
CREATE USER u@localhost;
GRANT EXECUTE,GRANT OPTION ON PACKAGE BODY test.pkg1 TO u@localhost;
DENY EXECUTE ON PACKAGE BODY test.pkg1 TO u@localhost;
connect con1, localhost, u,,test;
CALL pkg1.p1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.pkg1'
SELECT pkg1.f1();
ERROR 42000: execute command denied to user 'u'@'localhost' for routine 'test.pkg1'
connection default;
disconnect con1;
DROP USER u@localhost;
#
# DROP SP clears denies for user
#
CREATE USER u@localhost;
CREATE PROCEDURE proc2() BEGIN SET @p=1; END;
$$
DENY EXECUTE ON PROCEDURE proc2 TO u@localhost;
SHOW GRANTS for u@localhost;
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
DENY EXECUTE ON PROCEDURE `test`.`proc2` TO `u`@`localhost`
# END SHOW GRANTS
# show that deny entry exists in json
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='u';
User Host denies_pretty
u localhost [
{
"type": "procedure",
"db": "test",
"procedure": "proc2",
"bits": 262144
}
]
DROP PROCEDURE proc2;
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='u';
User Host denies_pretty
u localhost []
SHOW GRANTS for u@localhost;
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
# END SHOW GRANTS
DROP USER u@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP PROCEDURE proc1;
DROP FUNCTION func1;

View File

@@ -0,0 +1,199 @@
--source include/not_embedded.inc
--echo #
--echo # DENY routine privileges
--echo #
DELIMITER $$;
CREATE PROCEDURE proc1() BEGIN SET @p=1; END;
$$
CREATE FUNCTION func1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
DELIMITER ;$$
--echo #
--echo # Routine-level DENY for different privileges
--echo #
CREATE USER u@localhost;
--echo #
--echo # Illegal routine DENY statements
--echo #
--echo # Missing routine type (treated as table-level grant target)
--error ER_ILLEGAL_GRANT_FOR_TABLE
DENY EXECUTE ON test.proc1 TO u@localhost;
--echo # SELECT is not a routine privilege (procedure target)
--error ER_ILLEGAL_GRANT_FOR_TABLE
DENY SELECT ON PROCEDURE test.proc1 TO u@localhost;
GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE test.proc1 TO u@localhost;
GRANT EXECUTE, ALTER ROUTINE ON FUNCTION test.func1 TO u@localhost;
DENY ALTER ROUTINE ON PROCEDURE test.proc1 TO u@localhost;
DENY EXECUTE ON FUNCTION test.func1 TO u@localhost;
connect (con1, localhost, u,,test);
CALL proc1();
SELECT @p;
--error ER_PROCACCESS_DENIED_ERROR
ALTER PROCEDURE proc1 COMMENT 'x';
--error ER_PROCACCESS_DENIED_ERROR
SELECT func1();
ALTER FUNCTION func1 COMMENT 'x';
connection default;
disconnect con1;
DROP USER u@localhost;
--echo #
--echo # Routine-level DENY ALL
--echo #
CREATE USER u_all@localhost;
GRANT ALL ON PROCEDURE test.proc1 TO u_all@localhost;
GRANT ALL ON FUNCTION test.func1 TO u_all@localhost;
DENY ALL PRIVILEGES ON PROCEDURE test.proc1 TO u_all@localhost;
DENY ALL PRIVILEGES ON FUNCTION test.func1 TO u_all@localhost;
connect (con1, localhost, u_all,,);
--error ER_PROCACCESS_DENIED_ERROR
CALL test.proc1();
--error ER_PROCACCESS_DENIED_ERROR
SELECT test.func1();
--error ER_PROCACCESS_DENIED_ERROR
ALTER PROCEDURE test.proc1 COMMENT 'x';
--error ER_PROCACCESS_DENIED_ERROR
ALTER FUNCTION test.func1 COMMENT 'x';
connection default;
disconnect con1;
DROP USER u_all@localhost;
--echo #
--echo # DB-level DENY overrides routine grants
--echo #
CREATE USER u@localhost;
GRANT EXECUTE ON PROCEDURE test.proc1 TO u@localhost;
GRANT EXECUTE ON PACKAGE BODY test.pkg1 TO u@localhost;
DENY EXECUTE ON test.* TO u@localhost;
connect (con1, localhost, u,,test);
--error ER_PROCACCESS_DENIED_ERROR
CALL proc1();
--error ER_PROCACCESS_DENIED_ERROR
CALL pkg1.p1();
--error ER_PROCACCESS_DENIED_ERROR
SELECT pkg1.f1();
connection default;
disconnect con1;
DROP USER u@localhost;
--echo #
--echo # Global DENY overrides routine grants
--echo #
CREATE USER u@localhost;
GRANT EXECUTE ON FUNCTION test.func1 TO u@localhost;
DENY EXECUTE ON *.* TO u@localhost;
connect (con1, localhost, u,,test);
--error ER_PROCACCESS_DENIED_ERROR
SELECT func1();
connection default;
disconnect con1;
DROP USER u@localhost;
--echo #
--echo # Routine DENY is case-insensitive
--echo #
CREATE USER u_case@localhost;
GRANT EXECUTE ON PROCEDURE test.proc1 TO u_case@localhost;
GRANT EXECUTE ON FUNCTION test.func1 TO u_case@localhost;
DENY EXECUTE ON PROCEDURE test.ProC1 TO u_case@localhost;
DENY EXECUTE ON FUNCTION test.FUNC1 TO u_case@localhost;
connect (con_case, localhost, u_case,,);
--error ER_PROCACCESS_DENIED_ERROR
CALL test.proc1();
--error ER_PROCACCESS_DENIED_ERROR
SELECT test.func1();
connection default;
disconnect con_case;
DROP USER u_case@localhost;
--echo #
--echo # Package / package body DENY
--echo #
CREATE USER u@localhost;
GRANT EXECUTE,GRANT OPTION ON PACKAGE BODY test.pkg1 TO u@localhost;
DENY EXECUTE ON PACKAGE BODY test.pkg1 TO u@localhost;
connect (con1, localhost, u,,test);
--error ER_PROCACCESS_DENIED_ERROR
CALL pkg1.p1();
--error ER_PROCACCESS_DENIED_ERROR
SELECT pkg1.f1();
connection default;
disconnect con1;
DROP USER u@localhost;
--echo #
--echo # DROP SP clears denies for user
--echo #
CREATE USER u@localhost;
DELIMITER $$;
CREATE PROCEDURE proc2() BEGIN SET @p=1; END;
$$
DELIMITER ;$$
DENY EXECUTE ON PROCEDURE proc2 TO u@localhost;
SHOW GRANTS for u@localhost;
--echo # END SHOW GRANTS
--echo # show that deny entry exists in json
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='u';
DROP PROCEDURE proc2;
SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, '$.denies')) AS denies_pretty
FROM mysql.global_priv
WHERE User='u';
SHOW GRANTS for u@localhost;
--echo # END SHOW GRANTS
DROP USER u@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP PROCEDURE proc1;
DROP FUNCTION func1;

View File

@@ -0,0 +1,349 @@
#
# SHOW commands with DENY
#
# SHOW DATABASES visibility
CREATE USER u_show@localhost;
CREATE DATABASE db1;
CREATE TABLE db1.t1 (a int);
GRANT SELECT ON *.* TO u_show@localhost;
connect con1,localhost,u_show,,;
# db1 should be visible (global SELECT granted)
SHOW DATABASES LIKE 'db1';
Database (db1)
db1
disconnect con1;
connection default;
DENY SELECT ON *.* TO u_show@localhost;
connect con1,localhost,u_show,,;
# db1 should be hidden (global SELECT denied)
SHOW DATABASES LIKE 'db1';
Database (db1)
disconnect con1;
connection default;
GRANT INSERT ON db1.t1 TO u_show@localhost;
connect con1,localhost,u_show,,;
# db1 should be visible (table INSERT grant not denied)
SHOW DATABASES LIKE 'db1';
Database (db1)
db1
disconnect con1;
connection default;
DENY INSERT ON *.* TO u_show@localhost;
connect con1,localhost,u_show,,;
# db1 should be hidden (table INSERT masked by global DENY)
SHOW DATABASES LIKE 'db1';
Database (db1)
disconnect con1;
connection default;
DROP USER u_show@localhost;
DROP DATABASE db1;
# SHOW CREATE VIEW visibility
CREATE USER u_view@localhost;
CREATE DATABASE db2;
CREATE TABLE db2.t1 (a int);
CREATE VIEW db2.v1 AS SELECT * FROM db2.t1;
GRANT SELECT, SHOW VIEW ON db2.* TO u_view@localhost;
connect con2,localhost,u_view,,;
# SHOW CREATE VIEW should be allowed (SELECT + SHOW VIEW granted)
SHOW CREATE VIEW db2.v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `db2`.`v1` AS select `db2`.`t1`.`a` AS `a` from `db2`.`t1` <charset> <collation>
# information_schema.views always shows the name; definition is visible here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
table_name view_definition
v1 select `db2`.`t1`.`a` AS `a` from `db2`.`t1`
disconnect con2;
connection default;
DENY SELECT ON db2.* TO u_view@localhost;
connect con2,localhost,u_view,,;
# SHOW CREATE VIEW should be denied (SELECT denied)
SHOW CREATE VIEW db2.v1;
ERROR 42000: SELECT command denied to user 'u_view'@'localhost' for table `db2`.`v1`
# information_schema.views always shows the name; definition is hidden here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
table_name view_definition
v1
disconnect con2;
connection default;
REVOKE DENY SELECT ON db2.* FROM u_view@localhost;
DENY SHOW VIEW ON db2.* TO u_view@localhost;
connect con2,localhost,u_view,,;
# SHOW CREATE VIEW should be denied (SHOW VIEW denied)
SHOW CREATE VIEW db2.v1;
ERROR 42000: SHOW VIEW command denied to user 'u_view'@'localhost' for table `db2`.`v1`
# information_schema.views always shows the name; definition is hidden here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
table_name view_definition
v1
disconnect con2;
connection default;
DROP USER u_view@localhost;
DROP DATABASE db2;
# SHOW FIELDS / SHOW INDEX visibility
CREATE USER u_idx@localhost;
CREATE DATABASE db3;
CREATE TABLE db3.t1 (a int, b int, KEY k1(a));
CREATE TABLE db3.t2 (a int, b int, KEY k2(b));
GRANT SELECT ON db3.* TO u_idx@localhost;
connect con3,localhost,u_idx,,;
# SHOW FIELDS on db3.t1 should be allowed (SELECT granted)
SHOW FIELDS FROM db3.t1;
Field Type Null Key Default Extra
a int(11) YES MUL NULL
b int(11) YES NULL
# SHOW INDEX on db3.t1 should be allowed (SELECT granted)
SHOW INDEX FROM db3.t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t1 1 k1 1 a A <cardinality> NULL NULL YES BTREE NO
# SHOW FIELDS on db3.t2 should be allowed (SELECT granted)
SHOW FIELDS FROM db3.t2;
Field Type Null Key Default Extra
a int(11) YES NULL
b int(11) YES MUL NULL
# SHOW INDEX on db3.t2 should be allowed (SELECT granted)
SHOW INDEX FROM db3.t2;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t2 1 k2 1 b A <cardinality> NULL NULL YES BTREE NO
disconnect con3;
connection default;
DENY SELECT ON db3.t1 TO u_idx@localhost;
connect con3,localhost,u_idx,,;
# SHOW FIELDS/INDEX on t1 should be denied (table DENY)
SHOW FIELDS FROM db3.t1;
ERROR 42000: SELECT command denied to user 'u_idx'@'localhost' for table `db3`.`t1`
# SHOW INDEX on db3.t1 should be denied (table DENY)
SHOW INDEX FROM db3.t1;
ERROR 42000: SELECT command denied to user 'u_idx'@'localhost' for table `db3`.`t1`
# SHOW FIELDS on db3.t2 should be allowed
SHOW FIELDS FROM db3.t2;
Field Type Null Key Default Extra
a int(11) YES NULL
b int(11) YES MUL NULL
# SHOW INDEX on db3.t2 should be allowed
SHOW INDEX FROM db3.t2;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
disconnect con3;
connection default;
DROP USER u_idx@localhost;
DROP DATABASE db3;
# SHOW CREATE DATABASE visibility
CREATE USER u_scdb@localhost;
CREATE DATABASE db4;
GRANT REFERENCES ON db4.* TO u_scdb@localhost;
connect con4,localhost,u_scdb,,;
# SHOW CREATE DATABASE should be allowed (REFERENCES granted)
SHOW CREATE DATABASE db4;
Database Create Database
db4 <create_database>
disconnect con4;
connection default;
DENY REFERENCES ON db4.* TO u_scdb@localhost;
connect con4,localhost,u_scdb,,;
# SHOW CREATE DATABASE should be denied (REFERENCES denied)
SHOW CREATE DATABASE db4;
ERROR 42000: Access denied for user 'u_scdb'@'localhost' to database 'db4'
disconnect con4;
connection default;
DROP USER u_scdb@localhost;
DROP DATABASE db4;
# SHOW TABLES / SHOW TABLE STATUS / I_S visibility
CREATE USER u_tbl@localhost;
CREATE DATABASE db5;
CREATE TABLE db5.t1 (a int);
CREATE TABLE db5.t2 (a int, b int);
GRANT SELECT ON db5.* TO u_tbl@localhost;
connect con5,localhost,u_tbl,,;
# SHOW TABLES should list t1 and t2 (SELECT granted)
SHOW TABLES FROM db5;
Tables_in_db5
t1
t2
# SHOW TABLE STATUS should list t1 and t2 (SELECT granted)
SHOW TABLE STATUS FROM db5;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary
t1 <engine> 10 <row_format> <rows> <avg_row_length> <data_length> <max_data_length> <index_length> <data_free> NULL <auto_increment> <created> <updated> <checked> <collation> 17179868160 <max_index_length>
t2 <engine> 10 <row_format> <rows> <avg_row_length> <data_length> <max_data_length> <index_length> <data_free> NULL <auto_increment> <created> <updated> <checked> <collation> 17179868160 <max_index_length>
# I_S.TABLES should list t1 and t2 (SELECT granted)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
table_name
t1
t2
# I_S.COLUMNS should list columns for t1 and t2 (SELECT granted)
SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema='db5' ORDER BY table_name, column_name;
table_name column_name
t1 a
t2 a
t2 b
# SHOW CREATE TABLE should be allowed for t1 (SELECT granted)
SHOW CREATE TABLE db5.t1;
Table Create Table
t1 <create_table>
# SHOW CREATE TABLE should be allowed for t2 (SELECT granted)
SHOW CREATE TABLE db5.t2;
Table Create Table
t2 <create_table>
disconnect con5;
connection default;
DENY SELECT ON db5.t1 TO u_tbl@localhost;
connect con5,localhost,u_tbl,,;
# SHOW TABLES should hide t1 (table DENY)
SHOW TABLES FROM db5;
Tables_in_db5
t2
# SHOW TABLE STATUS should hide t1 (table DENY)
SHOW TABLE STATUS FROM db5;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary
t2 <engine> 10 <row_format> <rows> <avg_row_length> <data_length> <max_data_length> <index_length> <data_free> NULL <auto_increment> <created> <updated> <checked> <collation> 17179868160 <max_index_length>
# I_S.TABLES should hide t1 (table DENY)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
table_name
t2
# I_S.COLUMNS should hide t1 columns (table DENY)
SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema='db5' ORDER BY table_name, column_name;
table_name column_name
t2 a
t2 b
# SHOW CREATE TABLE on t1 should be denied (table DENY)
SHOW CREATE TABLE db5.t1;
ERROR 42000: SHOW command denied to user 'u_tbl'@'localhost' for table `db5`.`t1`
# SHOW CREATE TABLE on t2 should be allowed (SELECT allowed on table level)
SHOW CREATE TABLE db5.t2;
Table Create Table
t2 <create_table>
disconnect con5;
connection default;
REVOKE SELECT ON db5.* FROM u_tbl@localhost;
REVOKE DENY SELECT ON db5.t1 FROM u_tbl@localhost;
GRANT INSERT ON db5.t2 TO u_tbl@localhost;
DENY INSERT ON *.* TO u_tbl@localhost;
connect con5,localhost,u_tbl,,;
# SHOW TABLES should be denied (table INSERT masked by global DENY)
SHOW TABLES FROM db5;
ERROR 42000: Access denied for user 'u_tbl'@'localhost' to database 'db5'
# I_S.TABLES should be empty (table INSERT masked by global DENY)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
table_name
disconnect con5;
connection default;
GRANT SHOW VIEW ON db5.* TO u_tbl@localhost;
connect con5,localhost,u_tbl,,;
# SHOW CREATE TABLE on t2 should be allowed after SHOW VIEW grant
SHOW CREATE TABLE db5.t2;
Table Create Table
t2 <create_table>
disconnect con5;
connection default;
DROP USER u_tbl@localhost;
DROP DATABASE db5;
# I_S.STATISTICS + I_S.KEY_COLUMN_USAGE with column-level DENY
# 1) With SELECT on indexed columns, I_S.STATISTICS shows composite index.
# 2) DENY on one indexed column hides the index rows.
# 3) With SELECT on PK column, I_S.KEY_COLUMN_USAGE shows PK row.
# 4) DENY on PK column hides the PK row.
CREATE USER u_stat@localhost;
CREATE DATABASE db_stat;
CREATE TABLE db_stat.t (a int, b int, PRIMARY KEY (a), KEY k1(a,b));
GRANT SELECT (a,b) ON db_stat.t TO u_stat@localhost;
connect con_stat,localhost,u_stat,,;
# I_S.STATISTICS should show index (column SELECT granted)
SELECT table_schema, table_name, index_name, seq_in_index, column_name
FROM information_schema.statistics
WHERE table_schema='db_stat' AND table_name='t' AND index_name='k1'
ORDER BY seq_in_index;
table_schema table_name index_name seq_in_index column_name
db_stat t k1 1 a
db_stat t k1 2 b
disconnect con_stat;
connection default;
DENY SELECT (b) ON db_stat.t TO u_stat@localhost;
connect con_stat,localhost,u_stat,,;
# I_S.STATISTICS should be empty (column SELECT denied)
SELECT table_schema, table_name, index_name, seq_in_index, column_name
FROM information_schema.statistics
WHERE table_schema='db_stat' AND table_name='t' AND index_name='k1'
ORDER BY seq_in_index;
table_schema table_name index_name seq_in_index column_name
disconnect con_stat;
connection default;
connect con_stat,localhost,u_stat,,;
# I_S.KEY_COLUMN_USAGE should show PK (column SELECT granted)
SELECT table_schema, table_name, constraint_name, column_name
FROM information_schema.key_column_usage
WHERE table_schema='db_stat' AND table_name='t' AND constraint_name='PRIMARY'
ORDER BY constraint_name, column_name;
table_schema table_name constraint_name column_name
db_stat t PRIMARY a
disconnect con_stat;
connection default;
DENY SELECT (a) ON db_stat.t TO u_stat@localhost;
connect con_stat,localhost,u_stat,,;
# I_S.KEY_COLUMN_USAGE should be empty (column SELECT denied)
SELECT table_schema, table_name, constraint_name, column_name
FROM information_schema.key_column_usage
WHERE table_schema='db_stat' AND table_name='t' AND constraint_name='PRIMARY'
ORDER BY constraint_name, column_name;
table_schema table_name constraint_name column_name
disconnect con_stat;
connection default;
DROP USER u_stat@localhost;
DROP DATABASE db_stat;
# SHOW PROCEDURE/FUNCTION visibility
CREATE USER u_rtn@localhost;
CREATE DATABASE db6;
CREATE PROCEDURE db6.p1() BEGIN SELECT 1; END//
CREATE FUNCTION db6.f1() RETURNS INT RETURN 1//
GRANT EXECUTE, SHOW CREATE ROUTINE ON db6.* TO u_rtn@localhost;
connect con6,localhost,u_rtn,,;
# SHOW PROCEDURE STATUS should show p1 (EXECUTE + SHOW CREATE ROUTINE granted)
SHOW PROCEDURE STATUS WHERE Db='db6';
Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation
db6 p1 PROCEDURE root@localhost <modified> <created> DEFINER <charset> <collation> <db_collation>
# SHOW FUNCTION STATUS should show f1 (EXECUTE + SHOW CREATE ROUTINE granted)
SHOW FUNCTION STATUS WHERE Db='db6';
Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation
db6 f1 FUNCTION root@localhost <modified> <created> DEFINER <charset> <collation> <db_collation>
# SHOW CREATE PROCEDURE should be allowed
SHOW CREATE PROCEDURE db6.p1;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
p1 <sql_mode> CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
BEGIN SELECT 1; END <charset> <collation> <db_collation>
# SHOW CREATE FUNCTION should be allowed
SHOW CREATE FUNCTION db6.f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 <sql_mode> CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
RETURN 1 <charset> <collation> <db_collation>
disconnect con6;
connection default;
DENY SHOW CREATE ROUTINE ON db6.* TO u_rtn@localhost;
connect con6,localhost,u_rtn,,;
# SHOW PROCEDURE STATUS should still show p1 (SHOW CREATE ROUTINE denied)
SHOW PROCEDURE STATUS WHERE Db='db6';
Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation
db6 p1 PROCEDURE root@localhost <modified> <created> DEFINER <charset> <collation> <db_collation>
# SHOW FUNCTION STATUS should still show f1 (SHOW CREATE ROUTINE denied)
SHOW FUNCTION STATUS WHERE Db='db6';
Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation
db6 f1 FUNCTION root@localhost <modified> <created> DEFINER <charset> <collation> <db_collation>
# SHOW CREATE PROCEDURE should be allowed but definition hidden
SHOW CREATE PROCEDURE db6.p1;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
p1 <sql_mode> NULL <charset> <collation> <db_collation>
# SHOW CREATE FUNCTION should be allowed but definition hidden
SHOW CREATE FUNCTION db6.f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 <sql_mode> NULL <charset> <collation> <db_collation>
disconnect con6;
connection default;
DROP USER u_rtn@localhost;
DROP DATABASE db6;

View File

@@ -0,0 +1,347 @@
--source include/not_embedded.inc
--echo #
--echo # SHOW commands with DENY
--echo #
--echo # SHOW DATABASES visibility
CREATE USER u_show@localhost;
CREATE DATABASE db1;
CREATE TABLE db1.t1 (a int);
GRANT SELECT ON *.* TO u_show@localhost;
--connect (con1,localhost,u_show,,)
--echo # db1 should be visible (global SELECT granted)
SHOW DATABASES LIKE 'db1';
disconnect con1;
connection default;
DENY SELECT ON *.* TO u_show@localhost;
--connect (con1,localhost,u_show,,)
--echo # db1 should be hidden (global SELECT denied)
SHOW DATABASES LIKE 'db1';
disconnect con1;
connection default;
GRANT INSERT ON db1.t1 TO u_show@localhost;
--connect (con1,localhost,u_show,,)
--echo # db1 should be visible (table INSERT grant not denied)
SHOW DATABASES LIKE 'db1';
disconnect con1;
connection default;
DENY INSERT ON *.* TO u_show@localhost;
--connect (con1,localhost,u_show,,)
--echo # db1 should be hidden (table INSERT masked by global DENY)
SHOW DATABASES LIKE 'db1';
disconnect con1;
connection default;
DROP USER u_show@localhost;
DROP DATABASE db1;
--echo # SHOW CREATE VIEW visibility
CREATE USER u_view@localhost;
CREATE DATABASE db2;
CREATE TABLE db2.t1 (a int);
CREATE VIEW db2.v1 AS SELECT * FROM db2.t1;
GRANT SELECT, SHOW VIEW ON db2.* TO u_view@localhost;
--connect (con2,localhost,u_view,,)
--echo # SHOW CREATE VIEW should be allowed (SELECT + SHOW VIEW granted)
--replace_column 3 <charset> 4 <collation>
SHOW CREATE VIEW db2.v1;
--echo # information_schema.views always shows the name; definition is visible here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
disconnect con2;
connection default;
DENY SELECT ON db2.* TO u_view@localhost;
--connect (con2,localhost,u_view,,)
--echo # SHOW CREATE VIEW should be denied (SELECT denied)
--replace_column 3 <charset> 4 <collation>
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE VIEW db2.v1;
--echo # information_schema.views always shows the name; definition is hidden here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
disconnect con2;
connection default;
REVOKE DENY SELECT ON db2.* FROM u_view@localhost;
DENY SHOW VIEW ON db2.* TO u_view@localhost;
--connect (con2,localhost,u_view,,)
--echo # SHOW CREATE VIEW should be denied (SHOW VIEW denied)
--replace_column 3 <charset> 4 <collation>
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE VIEW db2.v1;
--echo # information_schema.views always shows the name; definition is hidden here
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_schema='db2' ORDER BY table_name;
disconnect con2;
connection default;
DROP USER u_view@localhost;
DROP DATABASE db2;
--echo # SHOW FIELDS / SHOW INDEX visibility
CREATE USER u_idx@localhost;
CREATE DATABASE db3;
CREATE TABLE db3.t1 (a int, b int, KEY k1(a));
CREATE TABLE db3.t2 (a int, b int, KEY k2(b));
GRANT SELECT ON db3.* TO u_idx@localhost;
--connect (con3,localhost,u_idx,,)
--echo # SHOW FIELDS on db3.t1 should be allowed (SELECT granted)
SHOW FIELDS FROM db3.t1;
--echo # SHOW INDEX on db3.t1 should be allowed (SELECT granted)
--replace_column 7 <cardinality>
SHOW INDEX FROM db3.t1;
--echo # SHOW FIELDS on db3.t2 should be allowed (SELECT granted)
SHOW FIELDS FROM db3.t2;
--echo # SHOW INDEX on db3.t2 should be allowed (SELECT granted)
--replace_column 7 <cardinality>
SHOW INDEX FROM db3.t2;
disconnect con3;
connection default;
DENY SELECT ON db3.t1 TO u_idx@localhost;
--connect (con3,localhost,u_idx,,)
--echo # SHOW FIELDS/INDEX on t1 should be denied (table DENY)
--error ER_TABLEACCESS_DENIED_ERROR
SHOW FIELDS FROM db3.t1;
--echo # SHOW INDEX on db3.t1 should be denied (table DENY)
--replace_column 7 <cardinality>
--error ER_TABLEACCESS_DENIED_ERROR
SHOW INDEX FROM db3.t1;
--echo # SHOW FIELDS on db3.t2 should be allowed
SHOW FIELDS FROM db3.t2;
--echo # SHOW INDEX on db3.t2 should be allowed
--replace_column 7 <cardinality>
SHOW INDEX FROM db3.t2;
disconnect con3;
connection default;
DROP USER u_idx@localhost;
DROP DATABASE db3;
--echo # SHOW CREATE DATABASE visibility
CREATE USER u_scdb@localhost;
CREATE DATABASE db4;
GRANT REFERENCES ON db4.* TO u_scdb@localhost;
--connect (con4,localhost,u_scdb,,)
--echo # SHOW CREATE DATABASE should be allowed (REFERENCES granted)
--replace_column 2 <create_database>
SHOW CREATE DATABASE db4;
disconnect con4;
connection default;
DENY REFERENCES ON db4.* TO u_scdb@localhost;
--connect (con4,localhost,u_scdb,,)
--echo # SHOW CREATE DATABASE should be denied (REFERENCES denied)
--replace_column 2 <create_database>
--error ER_DBACCESS_DENIED_ERROR
SHOW CREATE DATABASE db4;
disconnect con4;
connection default;
DROP USER u_scdb@localhost;
DROP DATABASE db4;
--echo # SHOW TABLES / SHOW TABLE STATUS / I_S visibility
CREATE USER u_tbl@localhost;
CREATE DATABASE db5;
CREATE TABLE db5.t1 (a int);
CREATE TABLE db5.t2 (a int, b int);
GRANT SELECT ON db5.* TO u_tbl@localhost;
--connect (con5,localhost,u_tbl,,)
--echo # SHOW TABLES should list t1 and t2 (SELECT granted)
SHOW TABLES FROM db5;
--echo # SHOW TABLE STATUS should list t1 and t2 (SELECT granted)
--replace_column 2 <engine> 4 <row_format> 5 <rows> 6 <avg_row_length> 7 <data_length> 8 <max_data_length> 9 <index_length> 10 <data_free> 12 <auto_increment> 13 <created> 14 <updated> 15 <checked> 16 <collation> 20 <max_index_length>
SHOW TABLE STATUS FROM db5;
--echo # I_S.TABLES should list t1 and t2 (SELECT granted)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
--echo # I_S.COLUMNS should list columns for t1 and t2 (SELECT granted)
SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema='db5' ORDER BY table_name, column_name;
--echo # SHOW CREATE TABLE should be allowed for t1 (SELECT granted)
--replace_column 2 <create_table>
SHOW CREATE TABLE db5.t1;
--echo # SHOW CREATE TABLE should be allowed for t2 (SELECT granted)
--replace_column 2 <create_table>
SHOW CREATE TABLE db5.t2;
disconnect con5;
connection default;
DENY SELECT ON db5.t1 TO u_tbl@localhost;
--connect (con5,localhost,u_tbl,,)
--echo # SHOW TABLES should hide t1 (table DENY)
SHOW TABLES FROM db5;
--echo # SHOW TABLE STATUS should hide t1 (table DENY)
--replace_column 2 <engine> 4 <row_format> 5 <rows> 6 <avg_row_length> 7 <data_length> 8 <max_data_length> 9 <index_length> 10 <data_free> 12 <auto_increment> 13 <created> 14 <updated> 15 <checked> 16 <collation> 20 <max_index_length>
SHOW TABLE STATUS FROM db5;
--echo # I_S.TABLES should hide t1 (table DENY)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
--echo # I_S.COLUMNS should hide t1 columns (table DENY)
SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema='db5' ORDER BY table_name, column_name;
--echo # SHOW CREATE TABLE on t1 should be denied (table DENY)
--replace_column 2 <create_table>
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE TABLE db5.t1;
--echo # SHOW CREATE TABLE on t2 should be allowed (SELECT allowed on table level)
--replace_column 2 <create_table>
SHOW CREATE TABLE db5.t2;
disconnect con5;
connection default;
REVOKE SELECT ON db5.* FROM u_tbl@localhost;
REVOKE DENY SELECT ON db5.t1 FROM u_tbl@localhost;
GRANT INSERT ON db5.t2 TO u_tbl@localhost;
DENY INSERT ON *.* TO u_tbl@localhost;
--connect (con5,localhost,u_tbl,,)
--echo # SHOW TABLES should be denied (table INSERT masked by global DENY)
--error ER_DBACCESS_DENIED_ERROR
SHOW TABLES FROM db5;
--echo # I_S.TABLES should be empty (table INSERT masked by global DENY)
SELECT table_name FROM information_schema.tables
WHERE table_schema='db5' ORDER BY table_name;
disconnect con5;
connection default;
GRANT SHOW VIEW ON db5.* TO u_tbl@localhost;
--connect (con5,localhost,u_tbl,,)
--echo # SHOW CREATE TABLE on t2 should be allowed after SHOW VIEW grant
--replace_column 2 <create_table>
SHOW CREATE TABLE db5.t2;
disconnect con5;
connection default;
DROP USER u_tbl@localhost;
DROP DATABASE db5;
--echo # I_S.STATISTICS + I_S.KEY_COLUMN_USAGE with column-level DENY
--echo # 1) With SELECT on indexed columns, I_S.STATISTICS shows composite index.
--echo # 2) DENY on one indexed column hides the index rows.
--echo # 3) With SELECT on PK column, I_S.KEY_COLUMN_USAGE shows PK row.
--echo # 4) DENY on PK column hides the PK row.
CREATE USER u_stat@localhost;
CREATE DATABASE db_stat;
CREATE TABLE db_stat.t (a int, b int, PRIMARY KEY (a), KEY k1(a,b));
GRANT SELECT (a,b) ON db_stat.t TO u_stat@localhost;
--connect (con_stat,localhost,u_stat,,)
--echo # I_S.STATISTICS should show index (column SELECT granted)
SELECT table_schema, table_name, index_name, seq_in_index, column_name
FROM information_schema.statistics
WHERE table_schema='db_stat' AND table_name='t' AND index_name='k1'
ORDER BY seq_in_index;
disconnect con_stat;
connection default;
DENY SELECT (b) ON db_stat.t TO u_stat@localhost;
--connect (con_stat,localhost,u_stat,,)
--echo # I_S.STATISTICS should be empty (column SELECT denied)
SELECT table_schema, table_name, index_name, seq_in_index, column_name
FROM information_schema.statistics
WHERE table_schema='db_stat' AND table_name='t' AND index_name='k1'
ORDER BY seq_in_index;
disconnect con_stat;
connection default;
--connect (con_stat,localhost,u_stat,,)
--echo # I_S.KEY_COLUMN_USAGE should show PK (column SELECT granted)
SELECT table_schema, table_name, constraint_name, column_name
FROM information_schema.key_column_usage
WHERE table_schema='db_stat' AND table_name='t' AND constraint_name='PRIMARY'
ORDER BY constraint_name, column_name;
disconnect con_stat;
connection default;
DENY SELECT (a) ON db_stat.t TO u_stat@localhost;
--connect (con_stat,localhost,u_stat,,)
--echo # I_S.KEY_COLUMN_USAGE should be empty (column SELECT denied)
SELECT table_schema, table_name, constraint_name, column_name
FROM information_schema.key_column_usage
WHERE table_schema='db_stat' AND table_name='t' AND constraint_name='PRIMARY'
ORDER BY constraint_name, column_name;
disconnect con_stat;
connection default;
DROP USER u_stat@localhost;
DROP DATABASE db_stat;
--echo # SHOW PROCEDURE/FUNCTION visibility
CREATE USER u_rtn@localhost;
CREATE DATABASE db6;
DELIMITER //;
CREATE PROCEDURE db6.p1() BEGIN SELECT 1; END//
CREATE FUNCTION db6.f1() RETURNS INT RETURN 1//
DELIMITER ;//
GRANT EXECUTE, SHOW CREATE ROUTINE ON db6.* TO u_rtn@localhost;
--connect (con6,localhost,u_rtn,,)
--echo # SHOW PROCEDURE STATUS should show p1 (EXECUTE + SHOW CREATE ROUTINE granted)
--replace_column 5 <modified> 6 <created> 9 <charset> 10 <collation> 11 <db_collation>
SHOW PROCEDURE STATUS WHERE Db='db6';
--echo # SHOW FUNCTION STATUS should show f1 (EXECUTE + SHOW CREATE ROUTINE granted)
--replace_column 5 <modified> 6 <created> 9 <charset> 10 <collation> 11 <db_collation>
SHOW FUNCTION STATUS WHERE Db='db6';
--echo # SHOW CREATE PROCEDURE should be allowed
--replace_column 2 <sql_mode> 4 <charset> 5 <collation> 6 <db_collation>
SHOW CREATE PROCEDURE db6.p1;
--echo # SHOW CREATE FUNCTION should be allowed
--replace_column 2 <sql_mode> 4 <charset> 5 <collation> 6 <db_collation>
SHOW CREATE FUNCTION db6.f1;
disconnect con6;
connection default;
DENY SHOW CREATE ROUTINE ON db6.* TO u_rtn@localhost;
--connect (con6,localhost,u_rtn,,)
--echo # SHOW PROCEDURE STATUS should still show p1 (SHOW CREATE ROUTINE denied)
--replace_column 5 <modified> 6 <created> 9 <charset> 10 <collation> 11 <db_collation>
SHOW PROCEDURE STATUS WHERE Db='db6';
--echo # SHOW FUNCTION STATUS should still show f1 (SHOW CREATE ROUTINE denied)
--replace_column 5 <modified> 6 <created> 9 <charset> 10 <collation> 11 <db_collation>
SHOW FUNCTION STATUS WHERE Db='db6';
--echo # SHOW CREATE PROCEDURE should be allowed but definition hidden
--replace_column 2 <sql_mode> 4 <charset> 5 <collation> 6 <db_collation>
SHOW CREATE PROCEDURE db6.p1;
--echo # SHOW CREATE FUNCTION should be allowed but definition hidden
--replace_column 2 <sql_mode> 4 <charset> 5 <collation> 6 <db_collation>
SHOW CREATE FUNCTION db6.f1;
disconnect con6;
connection default;
DROP USER u_rtn@localhost;
DROP DATABASE db6;

View File

@@ -0,0 +1,213 @@
#
# SHOW GRANTS with DENY output
#
CREATE DATABASE sg_db;
USE sg_db;
CREATE TABLE t1 (c1 INT, c2 INT);
CREATE PROCEDURE p1() BEGIN SET @x=1; END;
$$
CREATE FUNCTION f1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
CREATE USER u1@localhost;
#
# Miscellaneous GRANT/DENY mix, different levels
#
GRANT SELECT ON sg_db.t1 TO u1@localhost;
GRANT UPDATE (c1) ON sg_db.t1 TO u1@localhost;
GRANT EXECUTE ON PROCEDURE sg_db.p1 TO u1@localhost;
GRANT EXECUTE ON PACKAGE sg_db.pkg1 TO u1@localhost;
DENY SELECT ON *.* TO u1@localhost;
DENY INSERT ON sg_db.* TO u1@localhost;
DENY UPDATE (c2) ON sg_db.t1 TO u1@localhost;
DENY EXECUTE ON FUNCTION sg_db.f1 TO u1@localhost;
DENY EXECUTE ON PACKAGE BODY sg_db.pkg1 TO u1@localhost;
SHOW GRANTS FOR u1@localhost;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
DENY SELECT ON *.* TO `u1`@`localhost`
DENY INSERT ON `sg_db`.* TO `u1`@`localhost`
GRANT SELECT, UPDATE (`c1`) ON `sg_db`.`t1` TO `u1`@`localhost`
DENY UPDATE (`c2`) ON `sg_db`.`t1` TO `u1`@`localhost`
GRANT EXECUTE ON PROCEDURE `sg_db`.`p1` TO `u1`@`localhost`
DENY EXECUTE ON FUNCTION `sg_db`.`f1` TO `u1`@`localhost`
GRANT EXECUTE ON PACKAGE `sg_db`.`pkg1` TO `u1`@`localhost`
DENY EXECUTE ON PACKAGE BODY `sg_db`.`pkg1` TO `u1`@`localhost`
# END SHOW GRANTS
DROP USER u1@localhost;
#
# ALL PRIVILEGES, GRANT OPTION
#
CREATE USER u1@localhost;
DENY ALL PRIVILEGES ON sg_db.* TO u1@localhost;
SHOW GRANTS FOR u1@localhost;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
DENY ALL PRIVILEGES ON `sg_db`.* TO `u1`@`localhost`
# END SHOW GRANTS
DENY ALL PRIVILEGES ON sg_db.* TO u1@localhost WITH GRANT OPTION;
SHOW GRANTS FOR u1@localhost;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
DENY ALL PRIVILEGES ON `sg_db`.* TO `u1`@`localhost`
# END SHOW GRANTS
REVOKE DENY ALL ON sg_db.* FROM u1@localhost;
SHOW GRANTS FOR u1@localhost;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
DENY USAGE ON `sg_db`.* TO `u1`@`localhost`
# END SHOW GRANTS
DENY GRANT OPTION ON sg_db.* to u1@localhost;
SHOW GRANTS FOR u1@localhost;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
DENY USAGE ON `sg_db`.* TO `u1`@`localhost`
# END SHOW GRANTS
#
# Do not show (own) DENY for non-privileged users
#
connect con1, localhost, u1,,;
SHOW GRANTS;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
# END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect con1, localhost, u1,,;
SHOW GRANTS;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
GRANT SELECT ON `mysql`.* TO `u1`@`localhost`
DENY USAGE ON `sg_db`.* TO `u1`@`localhost`
# END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP FUNCTION f1;
DROP PROCEDURE p1;
DROP TABLE t1;
DROP DATABASE sg_db;
#
# SHOW GRANTS FOR role: non-privileged vs privileged user
# Without SELECT on mysql.*, a user must NOT see their own active role's DENY.
# With SELECT on mysql.* (privileged), the DENY must be visible.
# SHOW GRANTS FOR CURRENT_ROLE and SHOW GRANTS FOR <role_name> must be consistent.
#
CREATE ROLE r1;
DENY SELECT ON test.* TO r1;
CREATE USER u1@localhost;
GRANT r1 TO u1@localhost;
connect con1, localhost, u1,,;
SET ROLE r1;
# non-privileged: SHOW GRANTS FOR CURRENT_ROLE
SHOW GRANTS FOR CURRENT_ROLE;
Grants for r1
GRANT USAGE ON *.* TO `r1`
# END SHOW GRANTS
# non-privileged: SHOW GRANTS FOR r1 (same role, by name)
SHOW GRANTS FOR r1;
Grants for r1
GRANT USAGE ON *.* TO `r1`
# END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect con1, localhost, u1,,;
SET ROLE r1;
# privileged: SHOW GRANTS FOR CURRENT_ROLE
SHOW GRANTS FOR CURRENT_ROLE;
Grants for r1
GRANT USAGE ON *.* TO `r1`
DENY SELECT ON `test`.* TO `r1`
# END SHOW GRANTS
# privileged: SHOW GRANTS FOR r1 (same role, by name)
SHOW GRANTS FOR r1;
Grants for r1
GRANT USAGE ON *.* TO `r1`
DENY SELECT ON `test`.* TO `r1`
# END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP ROLE r1;
#
# SHOW GRANTS FOR PUBLIC: non-privileged vs privileged user
# Without SELECT on mysql.*, a user must NOT see DENYs on PUBLIC.
# With SELECT on mysql.* (privileged), the DENY must be visible.
#
CREATE USER u1@localhost;
DENY SELECT ON test.* TO PUBLIC;
connect con1, localhost, u1,,;
# non-privileged: SHOW GRANTS FOR PUBLIC
SHOW GRANTS FOR PUBLIC;
Grants for PUBLIC
# END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect con1, localhost, u1,,;
# privileged: SHOW GRANTS FOR PUBLIC
SHOW GRANTS FOR PUBLIC;
Grants for PUBLIC
DENY SELECT ON `test`.* TO PUBLIC
# END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
REVOKE DENY SELECT ON test.* FROM PUBLIC;
DELETE FROM mysql.global_priv WHERE User='PUBLIC';
FLUSH PRIVILEGES;
#
# SHOW GRANTS: user with SELECT on mysql.* but DENY on mysql.global_priv
# That user must neither see own DENIES, not should he be able to
# SHOW GRANTS for a another user
#
CREATE USER u1@localhost;
GRANT SELECT ON mysql.* TO u1@localhost;
DENY SELECT ON mysql.global_priv TO u1@localhost;
DENY SELECT ON test.* to u1@localhost;
CREATE USER u2@localhost;
GRANT SELECT ON test.* TO u2@localhost;
connect con1, localhost, u1,,;
# SHOW GRANTS should succeed and must not contain own DENY
SHOW GRANTS;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
GRANT SELECT ON `mysql`.* TO `u1`@`localhost`
# END SHOW GRANTS;
SHOW GRANTS FOR u2@localhost;
ERROR 42000: Access denied for user 'u1'@'localhost' to database 'mysql'
disconnect con1;
connection default;
# Now allow access to mysql.global_priv and verify own DENY are visible
REVOKE DENY SELECT ON mysql.global_priv FROM u1@localhost;
connect con1, localhost, u1,,;
# SHOW GRANTS should succeed and must contain own DENY
SHOW GRANTS;
Grants for u1@localhost
GRANT USAGE ON *.* TO `u1`@`localhost`
GRANT SELECT ON `mysql`.* TO `u1`@`localhost`
DENY SELECT ON `test`.* TO `u1`@`localhost`
# END SHOW GRANTS;
# SHOW GRANTS for another user
SHOW GRANTS FOR u2@localhost;
Grants for u2@localhost
GRANT USAGE ON *.* TO `u2`@`localhost`
GRANT SELECT ON `test`.* TO `u2`@`localhost`
# END SHOW GRANTS;
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP USER u2@localhost;

View File

@@ -0,0 +1,197 @@
--source include/not_embedded.inc
--source include/no_protocol.inc
--echo #
--echo # SHOW GRANTS with DENY output
--echo #
CREATE DATABASE sg_db;
USE sg_db;
CREATE TABLE t1 (c1 INT, c2 INT);
DELIMITER $$;
CREATE PROCEDURE p1() BEGIN SET @x=1; END;
$$
CREATE FUNCTION f1() RETURNS INT RETURN 1;
$$
CREATE PACKAGE pkg1
PROCEDURE p1();
FUNCTION f1() RETURNS INT;
END;
$$
CREATE PACKAGE BODY pkg1
PROCEDURE p1() BEGIN SET @pkg=2; END;
FUNCTION f1() RETURNS INT BEGIN RETURN 2; END;
END;
$$
DELIMITER ;$$
CREATE USER u1@localhost;
--echo #
--echo # Miscellaneous GRANT/DENY mix, different levels
--echo #
GRANT SELECT ON sg_db.t1 TO u1@localhost;
GRANT UPDATE (c1) ON sg_db.t1 TO u1@localhost;
GRANT EXECUTE ON PROCEDURE sg_db.p1 TO u1@localhost;
GRANT EXECUTE ON PACKAGE sg_db.pkg1 TO u1@localhost;
DENY SELECT ON *.* TO u1@localhost;
DENY INSERT ON sg_db.* TO u1@localhost;
DENY UPDATE (c2) ON sg_db.t1 TO u1@localhost;
DENY EXECUTE ON FUNCTION sg_db.f1 TO u1@localhost;
DENY EXECUTE ON PACKAGE BODY sg_db.pkg1 TO u1@localhost;
SHOW GRANTS FOR u1@localhost;
--echo # END SHOW GRANTS
DROP USER u1@localhost;
--echo #
--echo # ALL PRIVILEGES, GRANT OPTION
--echo #
CREATE USER u1@localhost;
DENY ALL PRIVILEGES ON sg_db.* TO u1@localhost;
SHOW GRANTS FOR u1@localhost;
--echo # END SHOW GRANTS
DENY ALL PRIVILEGES ON sg_db.* TO u1@localhost WITH GRANT OPTION;
SHOW GRANTS FOR u1@localhost;
--echo # END SHOW GRANTS
REVOKE DENY ALL ON sg_db.* FROM u1@localhost;
SHOW GRANTS FOR u1@localhost;
--echo # END SHOW GRANTS
DENY GRANT OPTION ON sg_db.* to u1@localhost;
# slightly ugly DENY USAGE .. WITH GRANT OPTION
SHOW GRANTS FOR u1@localhost;
--echo # END SHOW GRANTS
--echo #
--echo # Do not show (own) DENY for non-privileged users
--echo #
connect(con1, localhost, u1,,);
SHOW GRANTS; # should only show USAGE
--echo # END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect(con1, localhost, u1,,);
SHOW GRANTS; # should show more
--echo # END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
DROP FUNCTION f1;
DROP PROCEDURE p1;
DROP TABLE t1;
DROP DATABASE sg_db;
--echo #
--echo # SHOW GRANTS FOR role: non-privileged vs privileged user
--echo # Without SELECT on mysql.*, a user must NOT see their own active role's DENY.
--echo # With SELECT on mysql.* (privileged), the DENY must be visible.
--echo # SHOW GRANTS FOR CURRENT_ROLE and SHOW GRANTS FOR <role_name> must be consistent.
--echo #
CREATE ROLE r1;
DENY SELECT ON test.* TO r1;
CREATE USER u1@localhost;
GRANT r1 TO u1@localhost;
connect(con1, localhost, u1,,);
SET ROLE r1;
--echo # non-privileged: SHOW GRANTS FOR CURRENT_ROLE
SHOW GRANTS FOR CURRENT_ROLE;
--echo # END SHOW GRANTS
--echo # non-privileged: SHOW GRANTS FOR r1 (same role, by name)
SHOW GRANTS FOR r1;
--echo # END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect(con1, localhost, u1,,);
SET ROLE r1;
--echo # privileged: SHOW GRANTS FOR CURRENT_ROLE
SHOW GRANTS FOR CURRENT_ROLE;
--echo # END SHOW GRANTS
--echo # privileged: SHOW GRANTS FOR r1 (same role, by name)
SHOW GRANTS FOR r1;
--echo # END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP ROLE r1;
--echo #
--echo # SHOW GRANTS FOR PUBLIC: non-privileged vs privileged user
--echo # Without SELECT on mysql.*, a user must NOT see DENYs on PUBLIC.
--echo # With SELECT on mysql.* (privileged), the DENY must be visible.
--echo #
CREATE USER u1@localhost;
DENY SELECT ON test.* TO PUBLIC;
connect(con1, localhost, u1,,);
--echo # non-privileged: SHOW GRANTS FOR PUBLIC
SHOW GRANTS FOR PUBLIC;
--echo # END SHOW GRANTS
disconnect con1;
connection default;
GRANT SELECT ON mysql.* TO u1@localhost;
connect(con1, localhost, u1,,);
--echo # privileged: SHOW GRANTS FOR PUBLIC
SHOW GRANTS FOR PUBLIC;
--echo # END SHOW GRANTS
disconnect con1;
connection default;
DROP USER u1@localhost;
REVOKE DENY SELECT ON test.* FROM PUBLIC;
DELETE FROM mysql.global_priv WHERE User='PUBLIC';
FLUSH PRIVILEGES;
--echo #
--echo # SHOW GRANTS: user with SELECT on mysql.* but DENY on mysql.global_priv
--echo # That user must neither see own DENIES, not should he be able to
--echo # SHOW GRANTS for a another user
--echo #
CREATE USER u1@localhost;
GRANT SELECT ON mysql.* TO u1@localhost;
DENY SELECT ON mysql.global_priv TO u1@localhost;
DENY SELECT ON test.* to u1@localhost;
CREATE USER u2@localhost;
GRANT SELECT ON test.* TO u2@localhost;
connect(con1, localhost, u1,,);
--echo # SHOW GRANTS should succeed and must not contain own DENY
SHOW GRANTS;
--echo # END SHOW GRANTS;
--error ER_DBACCESS_DENIED_ERROR
SHOW GRANTS FOR u2@localhost;
disconnect con1;
connection default;
--echo # Now allow access to mysql.global_priv and verify own DENY are visible
REVOKE DENY SELECT ON mysql.global_priv FROM u1@localhost;
connect(con1, localhost, u1,,);
--echo # SHOW GRANTS should succeed and must contain own DENY
SHOW GRANTS;
--echo # END SHOW GRANTS;
--echo # SHOW GRANTS for another user
SHOW GRANTS FOR u2@localhost;
--echo # END SHOW GRANTS;
disconnect con1;
connection default;
DROP USER u1@localhost;
DROP USER u2@localhost;

View File

@@ -0,0 +1,247 @@
#
# Test DENY representation as JSON in mysql.global_priv table
#
SET @q_show_denies =
'SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, ''$.denies'')) AS denies_pretty
FROM mysql.global_priv
WHERE JSON_LENGTH(JSON_EXTRACT(Priv, ''$.denies'')) > 0
ORDER BY User, Host';
PREPARE show_denies FROM @q_show_denies;
CREATE USER u;
CREATE DATABASE d;
DENY SELECT,INSERT ON mysql.* TO u;
DENY UPDATE ON d.* TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "mysql",
"bits": 3
},
{
"type": "db",
"db": "d",
"bits": 4
}
]
REVOKE DENY SELECT ON mysql.* FROM u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "mysql",
"bits": 2
},
{
"type": "db",
"db": "d",
"bits": 4
}
]
REVOKE DENY INSERT ON mysql.* FROM u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "d",
"bits": 4
}
]
REVOKE DENY UPDATE ON d.* FROM u;
EXECUTE show_denies;
User Host denies_pretty
# Table level
DENY SELECT,INSERT ON mysql.global_priv TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "table",
"db": "mysql",
"table": "global_priv",
"bits": 3
}
]
REVOKE DENY INSERT,SELECT ON mysql.global_priv FROM u;
EXECUTE show_denies;
User Host denies_pretty
DENY SELECT(USER) ON mysql.global_priv TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "column",
"db": "mysql",
"table": "global_priv",
"column": "USER",
"bits": 1
}
]
DENY UPDATE(USER) ON mysql.global_priv TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "column",
"db": "mysql",
"table": "global_priv",
"column": "USER",
"bits": 5
}
]
DENY SELECT ON *.* TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "column",
"db": "mysql",
"table": "global_priv",
"column": "USER",
"bits": 5
},
{
"type": "global",
"bits": 1
}
]
CREATE TABLE sensitive_data (
id INT,
public_info VARCHAR(50),
salary DECIMAL(10,2),
ssn VARCHAR(11),
address VARCHAR(100)
);
DENY SELECT(salary,ssn) ON sensitive_data TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "column",
"db": "mysql",
"table": "global_priv",
"column": "USER",
"bits": 5
},
{
"type": "global",
"bits": 1
},
{
"type": "column",
"db": "test",
"table": "sensitive_data",
"column": "salary",
"bits": 1
},
{
"type": "column",
"db": "test",
"table": "sensitive_data",
"column": "ssn",
"bits": 1
}
]
GRANT ALL PRIVILEGES on *.* TO u WITH GRANT OPTION;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u;
# REVOKE ALL PRIVILEGES clears DENY entries
SHOW GRANTS FOR u;
Grants for u@%
GRANT USAGE ON *.* TO `u`@`%`
# END SHOW GRANTS
EXECUTE show_denies;
User Host denies_pretty
#
# Identifiers that require JSON escaping (double-quotes, backslash, etc.)
#
CREATE DATABASE `a"b\c`;
CREATE TABLE `a"b\c`.`t"1` (
`col"x` INT,
normal_col INT
);
DENY SELECT ON `a"b\c`.* TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "a\"b\\c",
"bits": 1
}
]
DENY INSERT ON `a"b\c`.`t"1` TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "a\"b\\c",
"bits": 1
},
{
"type": "table",
"db": "a\"b\\c",
"table": "t\"1",
"bits": 2
}
]
DENY UPDATE(`col"x`) ON `a"b\c`.`t"1` TO u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "a\"b\\c",
"bits": 1
},
{
"type": "table",
"db": "a\"b\\c",
"table": "t\"1",
"bits": 2
},
{
"type": "column",
"db": "a\"b\\c",
"table": "t\"1",
"column": "col\"x",
"bits": 4
}
]
# Verify SHOW GRANTS round-trips the special names
SHOW GRANTS FOR u;
Grants for u@%
GRANT USAGE ON *.* TO `u`@`%`
DENY SELECT ON `a"b\c`.* TO `u`@`%`
DENY INSERT, UPDATE (`col"x`) ON `a"b\c`.`t"1` TO `u`@`%`
# Verify REVOKE works with the special names
REVOKE DENY UPDATE(`col"x`) ON `a"b\c`.`t"1` FROM u;
EXECUTE show_denies;
User Host denies_pretty
u % [
{
"type": "db",
"db": "a\"b\\c",
"bits": 1
},
{
"type": "table",
"db": "a\"b\\c",
"table": "t\"1",
"bits": 2
}
]
REVOKE DENY INSERT ON `a"b\c`.`t"1` FROM u;
REVOKE DENY SELECT ON `a"b\c`.* FROM u;
EXECUTE show_denies;
User Host denies_pretty
DROP TABLE `a"b\c`.`t"1`;
DROP DATABASE `a"b\c`;
DEALLOCATE PREPARE show_denies;
DROP USER u;
DROP DATABASE d;
DROP TABLE sensitive_data;

View File

@@ -0,0 +1,106 @@
--source include/not_embedded.inc
--echo #
--echo # Test DENY representation as JSON in mysql.global_priv table
--echo #
SET @q_show_denies =
'SELECT User, Host,
JSON_PRETTY(JSON_EXTRACT(Priv, ''$.denies'')) AS denies_pretty
FROM mysql.global_priv
WHERE JSON_LENGTH(JSON_EXTRACT(Priv, ''$.denies'')) > 0
ORDER BY User, Host';
PREPARE show_denies FROM @q_show_denies;
CREATE USER u;
CREATE DATABASE d;
DENY SELECT,INSERT ON mysql.* TO u;
DENY UPDATE ON d.* TO u;
EXECUTE show_denies;
REVOKE DENY SELECT ON mysql.* FROM u;
EXECUTE show_denies;
REVOKE DENY INSERT ON mysql.* FROM u;
EXECUTE show_denies;
REVOKE DENY UPDATE ON d.* FROM u;
EXECUTE show_denies;
--echo # Table level
DENY SELECT,INSERT ON mysql.global_priv TO u;
EXECUTE show_denies;
REVOKE DENY INSERT,SELECT ON mysql.global_priv FROM u;
EXECUTE show_denies;
DENY SELECT(USER) ON mysql.global_priv TO u;
EXECUTE show_denies;
DENY UPDATE(USER) ON mysql.global_priv TO u;
EXECUTE show_denies;
DENY SELECT ON *.* TO u;
EXECUTE show_denies;
CREATE TABLE sensitive_data (
id INT,
public_info VARCHAR(50),
salary DECIMAL(10,2),
ssn VARCHAR(11),
address VARCHAR(100)
);
DENY SELECT(salary,ssn) ON sensitive_data TO u;
EXECUTE show_denies;
GRANT ALL PRIVILEGES on *.* TO u WITH GRANT OPTION;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u;
--echo # REVOKE ALL PRIVILEGES clears DENY entries
SHOW GRANTS FOR u;
--echo # END SHOW GRANTS
EXECUTE show_denies;
--echo #
--echo # Identifiers that require JSON escaping (double-quotes, backslash, etc.)
--echo #
CREATE DATABASE `a"b\c`;
CREATE TABLE `a"b\c`.`t"1` (
`col"x` INT,
normal_col INT
);
DENY SELECT ON `a"b\c`.* TO u;
EXECUTE show_denies;
DENY INSERT ON `a"b\c`.`t"1` TO u;
EXECUTE show_denies;
DENY UPDATE(`col"x`) ON `a"b\c`.`t"1` TO u;
EXECUTE show_denies;
--echo # Verify SHOW GRANTS round-trips the special names
SHOW GRANTS FOR u;
--echo # Verify REVOKE works with the special names
REVOKE DENY UPDATE(`col"x`) ON `a"b\c`.`t"1` FROM u;
EXECUTE show_denies;
REVOKE DENY INSERT ON `a"b\c`.`t"1` FROM u;
REVOKE DENY SELECT ON `a"b\c`.* FROM u;
EXECUTE show_denies;
DROP TABLE `a"b\c`.`t"1`;
DROP DATABASE `a"b\c`;
DEALLOCATE PREPARE show_denies;
DROP USER u;
DROP DATABASE d;
DROP TABLE sensitive_data;

View File

@@ -0,0 +1,43 @@
#
# check_grant_db with DENY-only entries
#
CREATE DATABASE db1;
CREATE TABLE db1.t1 (c1 INT, c2 INT);
CREATE USER u1@localhost;
DENY INSERT ON db1.t1 TO u1@localhost;
connect con1, localhost, u1,,;
USE db1;
ERROR 42000: Access denied for user 'u1'@'localhost' to database 'db1'
connection default;
GRANT SELECT (c1) ON db1.t1 TO u1@localhost;
connection con1;
USE db1;
connection default;
disconnect con1;
DROP USER u1@localhost;
#
# global DENY ALL prevents USE, even with table grants
#
CREATE USER u2@localhost;
GRANT SELECT ON db1.t1 TO u2@localhost;
DENY ALL PRIVILEGES ON *.* TO u2@localhost;
connect con1, localhost, u2,,;
USE db1;
ERROR 42000: Access denied for user 'u2'@'localhost' to database 'db1'
connection default;
disconnect con1;
DROP USER u2@localhost;
#
# db-level DENY ALL prevents USE, even with table grants
#
CREATE USER u3@localhost;
GRANT SELECT ON db1.t1 TO u3@localhost;
DENY ALL PRIVILEGES ON db1.* TO u3@localhost;
connect con1, localhost, u3,,;
USE db1;
ERROR 42000: Access denied for user 'u3'@'localhost' to database 'db1'
connection default;
disconnect con1;
DROP USER u3@localhost;
DROP TABLE db1.t1;
DROP DATABASE db1;

View File

@@ -0,0 +1,63 @@
--source include/not_embedded.inc
--echo #
--echo # check_grant_db with DENY-only entries
--echo #
CREATE DATABASE db1;
CREATE TABLE db1.t1 (c1 INT, c2 INT);
CREATE USER u1@localhost;
DENY INSERT ON db1.t1 TO u1@localhost;
connect (con1, localhost, u1,,);
# Deny-only entries should not satisfy check_grant_db().
--error ER_DBACCESS_DENIED_ERROR
USE db1;
connection default;
GRANT SELECT (c1) ON db1.t1 TO u1@localhost;
connection con1;
# Column-level grant allows USE.
USE db1;
connection default;
disconnect con1;
DROP USER u1@localhost;
--echo #
--echo # global DENY ALL prevents USE, even with table grants
--echo #
CREATE USER u2@localhost;
GRANT SELECT ON db1.t1 TO u2@localhost;
DENY ALL PRIVILEGES ON *.* TO u2@localhost;
connect (con1, localhost, u2,,);
--error ER_DBACCESS_DENIED_ERROR
USE db1;
connection default;
disconnect con1;
DROP USER u2@localhost;
--echo #
--echo # db-level DENY ALL prevents USE, even with table grants
--echo #
CREATE USER u3@localhost;
GRANT SELECT ON db1.t1 TO u3@localhost;
DENY ALL PRIVILEGES ON db1.* TO u3@localhost;
connect (con1, localhost, u3,,);
--error ER_DBACCESS_DENIED_ERROR
USE db1;
connection default;
disconnect con1;
DROP USER u3@localhost;
DROP TABLE db1.t1;
DROP DATABASE db1;

View File

@@ -191,17 +191,17 @@ SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR
FROM performance_schema.events_statements_summary_by_digest
ORDER BY DIGEST_TEXT;
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR
test 7f807b8cf301913b61deb32c6cab1e1b EXPLAIN SELECT * FROM `test` . `v1` 1
test e17dbf6fd40651ae8bd34f6a3e8ef24e EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 1
test 602c8a82b325449790d331768979c6b9 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 1
test e261dc01764c5f2708df66fa22884459 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 1
test d63272115b72b81871327666eaeea38b EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 1
test 2885c2680299873f350feba04eeafaf8 SELECT * FROM `test` . `v1` 1
test 311ba3c50a8a7360e6667ad134607fab SELECT * FROM `test` . `v1` WHERE `a` = ? 1
test 5587d23d7fa7acabd8e15b0b9526a3c9 SELECT * FROM `test` . `v1` WHERE `b` > ? 1
test dd24f2a9de5abbd2f9e8db1d5eaf615c SELECT `a` , `b` FROM `test` . `v1` 1
test f15dd457d598d7befe8cf8047bed0927 SELECT `b` , `a` FROM `test` . `v1` 1
test 750d4101ef2bcc9061debad4690a80d9 TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
test 6f321f4e40122b391c5f21aa427ed78f EXPLAIN SELECT * FROM `test` . `v1` 1
test 28dad445d0e04be3fd88734d6b5092d1 EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 1
test 23f674bf310451decac56222b65e8970 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 1
test 0f1961b7d219d57f146da8fde77d2a81 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 1
test b1515277e9f6c1edbed0289707048f13 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 1
test 9260e4a28f4b5613948ed2c3adb3ed3d SELECT * FROM `test` . `v1` 1
test 8a2f81fd93e3244896a9b1e48f4bbd7e SELECT * FROM `test` . `v1` WHERE `a` = ? 1
test c49d3c36f8f9c96678a9076399b55a9f SELECT * FROM `test` . `v1` WHERE `b` > ? 1
test 425ddae2c8b389172ba32cdbc3caa8fa SELECT `a` , `b` FROM `test` . `v1` 1
test 8b843c81f1f063fe9560fd61e921418e SELECT `b` , `a` FROM `test` . `v1` 1
test ee83a5ec5cb9651bffe18ae1e3a5315d TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
DROP TABLE test.v1;
CREATE VIEW test.v1 AS SELECT * FROM test.t1;
EXPLAIN SELECT * from test.v1;
@@ -248,19 +248,19 @@ SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR
FROM performance_schema.events_statements_summary_by_digest
ORDER BY DIGEST_TEXT;
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR
test 2977c8beffabf7bebac017c7b95aefe3 CREATE VIEW `test` . `v1` AS SELECT * FROM `test` . `t1` 1
test fd8390bb0d24a48e871d95b5b0758269 DROP TABLE `test` . `v1` 1
test 7f807b8cf301913b61deb32c6cab1e1b EXPLAIN SELECT * FROM `test` . `v1` 2
test e17dbf6fd40651ae8bd34f6a3e8ef24e EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 2
test 602c8a82b325449790d331768979c6b9 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 2
test e261dc01764c5f2708df66fa22884459 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 2
test d63272115b72b81871327666eaeea38b EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 2
test 2885c2680299873f350feba04eeafaf8 SELECT * FROM `test` . `v1` 2
test 311ba3c50a8a7360e6667ad134607fab SELECT * FROM `test` . `v1` WHERE `a` = ? 2
test 5587d23d7fa7acabd8e15b0b9526a3c9 SELECT * FROM `test` . `v1` WHERE `b` > ? 2
test d885cf933b5d3317333a02003dee7b48 SELECT SCHEMA_NAME , `DIGEST` , `DIGEST_TEXT` , `COUNT_STAR` FROM `performance_schema` . `events_statements_summary_by_digest` ORDER BY `DIGEST_TEXT` 1
test dd24f2a9de5abbd2f9e8db1d5eaf615c SELECT `a` , `b` FROM `test` . `v1` 2
test f15dd457d598d7befe8cf8047bed0927 SELECT `b` , `a` FROM `test` . `v1` 2
test 750d4101ef2bcc9061debad4690a80d9 TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
test 784ad71c451db220c8b53cd84d696252 CREATE VIEW `test` . `v1` AS SELECT * FROM `test` . `t1` 1
test 3a6acea315a53efa1c8f8786622ea867 DROP TABLE `test` . `v1` 1
test 6f321f4e40122b391c5f21aa427ed78f EXPLAIN SELECT * FROM `test` . `v1` 2
test 28dad445d0e04be3fd88734d6b5092d1 EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 2
test 23f674bf310451decac56222b65e8970 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 2
test 0f1961b7d219d57f146da8fde77d2a81 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 2
test b1515277e9f6c1edbed0289707048f13 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 2
test 9260e4a28f4b5613948ed2c3adb3ed3d SELECT * FROM `test` . `v1` 2
test 8a2f81fd93e3244896a9b1e48f4bbd7e SELECT * FROM `test` . `v1` WHERE `a` = ? 2
test c49d3c36f8f9c96678a9076399b55a9f SELECT * FROM `test` . `v1` WHERE `b` > ? 2
test 12e528310fc145b4a866d8fbaac5e519 SELECT SCHEMA_NAME , `DIGEST` , `DIGEST_TEXT` , `COUNT_STAR` FROM `performance_schema` . `events_statements_summary_by_digest` ORDER BY `DIGEST_TEXT` 1
test 425ddae2c8b389172ba32cdbc3caa8fa SELECT `a` , `b` FROM `test` . `v1` 2
test 8b843c81f1f063fe9560fd61e921418e SELECT `b` , `a` FROM `test` . `v1` 2
test ee83a5ec5cb9651bffe18ae1e3a5315d TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
DROP VIEW test.v1;
DROP TABLE test.t1;

View File

@@ -8,5 +8,5 @@ SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
####################################
SELECT event_name, digest, digest_text, sql_text FROM events_statements_history_long;
event_name digest digest_text sql_text
statement/sql/select bc85fb4e8188bed1c842f764ec9fb06a SELECT ? + ? + SELECT ...
statement/sql/truncate 208418d9203df0b1b2cadb4d7196ea77 TRUNCATE TABLE truncat...
statement/sql/select 2490fb0602b2ad29d2adc5923c6be917 SELECT ? + ? + SELECT ...
statement/sql/truncate 8460b8b5a83d4357fcb94a1ccbb54817 TRUNCATE TABLE truncat...

View File

@@ -0,0 +1,61 @@
include/master-slave.inc
[connection master]
connection master;
CREATE USER 'u'@'localhost';
GRANT SELECT, INSERT, UPDATE ON test.* TO 'u'@'localhost';
connection slave;
# Verify baseline GRANT replicated
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
# DENY SELECT and verify replication
connection master;
DENY SELECT ON test.* TO 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY SELECT ON `test`.* TO `u`@`localhost`
connection slave;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY SELECT ON `test`.* TO `u`@`localhost`
# DENY INSERT and verify replication
connection master;
DENY INSERT ON test.* TO 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY SELECT, INSERT ON `test`.* TO `u`@`localhost`
connection slave;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY SELECT, INSERT ON `test`.* TO `u`@`localhost`
# REVOKE DENY SELECT and verify replication
connection master;
REVOKE DENY SELECT ON test.* FROM 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY INSERT ON `test`.* TO `u`@`localhost`
connection slave;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
Grants for u@localhost
GRANT USAGE ON *.* TO `u`@`localhost`
GRANT SELECT, INSERT, UPDATE ON `test`.* TO `u`@`localhost`
DENY INSERT ON `test`.* TO `u`@`localhost`
connection master;
DROP USER 'u'@'localhost';
connection slave;
include/rpl_end.inc

View File

@@ -0,0 +1,49 @@
#
# Verify that DENY statements are replicated correctly to the slave.
#
--source include/not_embedded.inc
--source include/master-slave.inc
connection master;
CREATE USER 'u'@'localhost';
GRANT SELECT, INSERT, UPDATE ON test.* TO 'u'@'localhost';
sync_slave_with_master;
--echo # Verify baseline GRANT replicated
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
--echo # DENY SELECT and verify replication
connection master;
DENY SELECT ON test.* TO 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
sync_slave_with_master;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
--echo # DENY INSERT and verify replication
connection master;
DENY INSERT ON test.* TO 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
sync_slave_with_master;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
--echo # REVOKE DENY SELECT and verify replication
connection master;
REVOKE DENY SELECT ON test.* FROM 'u'@'localhost';
SHOW GRANTS FOR 'u'@'localhost';
sync_slave_with_master;
connection slave;
SHOW GRANTS FOR 'u'@'localhost';
# Cleanup
connection master;
DROP USER 'u'@'localhost';
sync_slave_with_master;
--source include/rpl_end.inc

View File

@@ -104,8 +104,8 @@ static int prepare_for_fill(TABLE_LIST *tables)
thd->real_id= pthread_self();
thd->db= null_clex_str;
thd->security_ctx->host_or_ip= "";
thd->security_ctx->db_access= DB_ACLS;
thd->security_ctx->master_access= ALL_KNOWN_ACL;
thd->security_ctx->db_access= access_t(DB_ACLS);
thd->security_ctx->master_access= access_t(ALL_KNOWN_ACL);
bzero((char*) &thd->net, sizeof(thd->net));
lex_start(thd);
thd->lex->init_select();

View File

@@ -30,7 +30,7 @@ static int index_stats_fill(THD *thd, TABLE_LIST *tables, COND *cond)
tmp_table.db.length= strlen(index_stats->index);
tmp_table.table_name.str= index_stats->index + tmp_table.db.length + 1;
tmp_table.table_name.length= strlen(tmp_table.table_name.str);
tmp_table.grant.privilege= NO_ACL;
tmp_table.grant.privilege= access_t(NO_ACL);
if (check_access(thd, SELECT_ACL, tmp_table.db.str,
&tmp_table.grant.privilege, NULL, 0, 1) ||
check_grant(thd, SELECT_ACL, &tmp_table, 1, 1, 1))

View File

@@ -43,7 +43,7 @@ static int table_stats_fill(THD *thd, TABLE_LIST *tables, COND *cond)
tmp_table.db.length= schema_length;
tmp_table.table_name.str= end_of_schema+1;
tmp_table.table_name.length= table_name_length;
tmp_table.grant.privilege= NO_ACL;
tmp_table.grant.privilege= access_t(NO_ACL);
if (check_access(thd, SELECT_ACL, tmp_table.db.str,
&tmp_table.grant.privilege, NULL, 0, 1) ||
check_grant(thd, SELECT_ACL, &tmp_table, 1, 1, 1))

View File

@@ -1496,8 +1496,8 @@ end:
Temporarily reset it to read-write.
*/
privilege_t saved_master_access(thd->security_ctx->master_access);
thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
access_t saved_master_access(thd->security_ctx->master_access);
thd->security_ctx->master_access.force_allow(PRIV_IGNORE_READ_ONLY, true);
bool save_tx_read_only= thd->tx_read_only;
thd->tx_read_only= false;

View File

@@ -179,8 +179,8 @@ pre_init_event_thread(THD* thd)
set_current_thd(thd);
thd->client_capabilities= 0;
thd->security_ctx->master_access= NO_ACL;
thd->security_ctx->db_access= NO_ACL;
thd->security_ctx->master_access= access_t(NO_ACL);
thd->security_ctx->db_access= access_t(NO_ACL);
thd->security_ctx->host_or_ip= (char*)my_localhost;
my_net_init(&thd->net, NULL, thd, MYF(MY_THREAD_SPECIFIC));
thd->security_ctx->set_user((char*)"event_scheduler");
@@ -415,7 +415,7 @@ Event_scheduler::start(int *err_no)
Same goes for transaction access mode. Set it to read-write for this thd.
*/
new_thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
new_thd->security_ctx->master_access.force_allow(PRIV_IGNORE_READ_ONLY);
new_thd->variables.tx_read_only= false;
new_thd->tx_read_only= false;

View File

@@ -1173,8 +1173,8 @@ Events::load_events_from_db(THD *thd)
Temporarily reset it to read-write.
*/
privilege_t saved_master_access(thd->security_ctx->master_access);
thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
access_t saved_master_access(thd->security_ctx->master_access);
thd->security_ctx->master_access.force_allow(PRIV_IGNORE_READ_ONLY, true);
bool save_tx_read_only= thd->tx_read_only;
thd->tx_read_only= false;

View File

@@ -923,7 +923,7 @@ TABLE *create_table_for_function(THD *thd, TABLE_LIST *sql_table)
table->next= thd->derived_tables;
thd->derived_tables= table;
table->s->tmp_table= INTERNAL_TMP_TABLE;
table->grant.privilege= SELECT_ACL;
table->grant.privilege= access_t(SELECT_ACL);
sql_table->table= table;

View File

@@ -188,6 +188,7 @@ SYMBOL symbols[] = {
{ "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM)},
{ "DELETE", SYM(DELETE_SYM)},
{ "DELETE_DOMAIN_ID", SYM(DELETE_DOMAIN_ID_SYM)},
{ "DENY", SYM(DENY)},
{ "DESC", SYM(DESC)},
{ "DESCRIBE", SYM(DESCRIBE)},
{ "DETERMINISTIC", SYM(DETERMINISTIC_SYM)},

View File

@@ -17,7 +17,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#include "my_global.h" // ulonglong
#include "my_bit.h" //my_bit_log2_uint64
/*
A strict enum to store privilege bits.
@@ -670,6 +670,190 @@ constexpr privilege_t PRIV_STMT_SHOW_ENGINE_MUTEX= PROCESS_ACL;
constexpr privilege_t PRIV_STMT_SHOW_PROCESSLIST= PROCESS_ACL;
/*
access_t represents effective privileges at a given scope.
It bundles:
- allow bits (positive grants at this scope)
- deny bits (explicit DENY at this scope)
- deny_subtree (summary of DENY from lower scopes, used for fast checks)
access_t is what privilege checks cache and combine (roles, parents, etc.)
to decide whether a requested privilege is allowed, denied, or unknown.
*/
class access_t
{
private:
privilege_t m_allow_bits;
privilege_t m_deny_bits;
privilege_t m_deny_subtree;
public:
explicit access_t(privilege_t allow, privilege_t deny= NO_ACL,
privilege_t deny_subtree= NO_ACL)
: m_allow_bits(allow), m_deny_bits(deny), m_deny_subtree(deny_subtree)
{
}
/* True if no allow/deny information is present. */
bool is_empty() const
{
return m_allow_bits == NO_ACL && m_deny_bits == NO_ACL &&
m_deny_subtree == NO_ACL;
}
void set_deny_bits(privilege_t deny) { m_deny_bits= deny; }
/* Set explicit denies and subtree denies in one shot. */
void set_deny(privilege_t deny, privilege_t deny_subtree)
{
m_deny_bits= deny;
m_deny_subtree= deny_subtree;
}
/* Set subtree deny summary (denies from lower scopes). */
void set_deny_subtree(privilege_t deny_subtree)
{
m_deny_subtree= deny_subtree;
}
/* Replace allow bits at this scope. */
void set_allow_bits(privilege_t allow)
{
m_allow_bits= allow;
}
/* Force allow bits; optionally clear matching denies at this scope. */
void force_allow(privilege_t bits, bool overwrite_deny=false)
{
m_allow_bits|= bits;
if (overwrite_deny)
m_deny_bits&= ~bits ;
}
/*
Disallow implicit conversions from privilege
(potential source of bugs)
*/
access_t &operator=(const privilege_t &)= delete;
/*
Merge another access_t into this one (bitwise OR of all components).
Used for: accumulating privileges from multiple roles, or from PUBLIC +
user, etc.
*/
access_t &merge_same_level(const access_t &other)
{
m_allow_bits= privilege_t(m_allow_bits | other.m_allow_bits);
m_deny_bits= privilege_t(m_deny_bits | other.m_deny_bits);
m_deny_subtree= privilege_t(m_deny_subtree | other.m_deny_subtree);
return *this;
}
access_t &operator|=(const access_t &)= delete;
/*
Intersect allow bits; keep union of denies and subtree denies.
Used for: intersecting access from db and host.
*/
access_t intersect(const access_t &other) const
{
return access_t((privilege_t) (m_allow_bits & other.m_allow_bits),
(privilege_t) (m_deny_bits | other.m_deny_bits),
(privilege_t) (m_deny_subtree | other.m_deny_subtree));
}
/*
Combine child-level access with parent-level access (vertical hierarchy).
Used for: global -> database -> table -> column traversal
Rules:
- Allow bits accumulate (union), but an explicit deny always wins over an allow.
- Deny bits accumulate (once denied at any scope, always denied).
- deny_subtree is taken from *this* (the child), not from the parent.
The difference between this and merge_same_level() is the handling of
deny_subtree. merge_same_level() is for same-level access for user+role+PUBLIC,
while this one is hierarchical, used when going from global to db etc until
column level. If merged correctly, on the "leaf level" of hierarchy,
the deny_subtree should always be zero.
*/
access_t &merge_with_parent(const access_t &parent)
{
m_allow_bits= privilege_t(m_allow_bits | parent.m_allow_bits);
m_deny_bits= privilege_t(m_deny_bits | parent.m_deny_bits);
/* Keep current deny_subtree (child scope). */
return *this;
}
friend bool operator==(const access_t &x, const access_t &y)
{
return x.m_allow_bits == y.m_allow_bits &&
x.m_deny_bits == y.m_deny_bits &&
x.m_deny_subtree == y.m_deny_subtree;
}
friend bool operator!=(const access_t &x, const access_t &y)
{
return !(x == y);
}
/* Bitwise OR merge is intentionally avoided for access_t. */
/* Return bits that are granted and not denied here or below. */
privilege_t certainly_allowed(privilege_t priv) const
{
return (privilege_t) (priv & m_allow_bits & ~m_deny_bits &
~m_deny_subtree);
}
/* Used for quick checks in USE db / SHOW */
privilege_t maybe_allowed(privilege_t priv) const
{
return (privilege_t) (priv & m_allow_bits & ~m_deny_bits);
}
/*
Intersect with a privilege mask.
deny bits are "sticky", i.e. won't be cleared by
frequently used "priv &=~ access" patterns
*/
friend privilege_t operator&(const access_t &x, const privilege_t &y)
{
return x.certainly_allowed(y);
}
/*
Complement of certainly_allowed across all known privilege bits.
Used in popular "want_privilege &=~ access" pattern to clear known
allowed bits.
*/
ulonglong operator~() const { return ~certainly_allowed(ALL_KNOWN_ACL); };
/* Return bits that are requested and denied. */
privilege_t denied_privs(privilege_t priv) const
{
return (privilege_t) (m_deny_bits & priv);
}
/* Raw accessors. */
privilege_t allow_bits() const { return m_allow_bits; }
privilege_t deny_bits() const { return m_deny_bits; }
privilege_t deny_subtree() const { return m_deny_subtree; }
};
inline access_t merge_same_level(const access_t& first, const access_t& second)
{
access_t res(first);
return res.merge_same_level(second);
}
inline access_t merge_same_level(const access_t &first, const access_t &second,
const access_t& third)
{
return merge_same_level(first, merge_same_level(second,third));
}
inline access_t merge_with_parent(const access_t &child, const access_t &parent)
{
access_t res(child);
res.merge_with_parent(parent);
return res;
}
/*
Defines to change the above bits to how things are stored in tables
This is needed as the 'host' and 'db' table is missing a few privileges

View File

@@ -387,7 +387,7 @@ public:
class set_var_role: public set_var_base
{
LEX_CSTRING role;
privilege_t access;
access_t access;
public:
set_var_role(LEX_CSTRING role_arg) : role(role_arg), access(NO_ACL) {}
int check(THD *thd) override;

View File

@@ -6928,12 +6928,12 @@ ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR
spa "No puedo cargar valor desde fichero/archivo con filas de tamaño fijo en variable"
sw "Haiwezi kupakia thamani kutoka kwa faili iliyo na safu za saizi maalum hadi kutofautisha"
ER_CANT_CREATE_USER_WITH_GRANT 42000
chi "您不允许使用创建用户时给予GRANT"
eng "You are not allowed to create a user with GRANT"
ger "Sie dürfen keinen Benutzer mit GRANT anlegen"
geo "GRANT-ით მომხმარებლის შექმნის უფლება არ გაქვთ"
spa "No está autorizado a crear un usuario con GRANT"
sw "Hauruhusiwi kuunda mtumiaji na GRANT"
chi "您不允许使用创建用户时给予%s"
eng "You are not allowed to create a user with %s"
ger "Sie dürfen keinen Benutzer mit %s anlegen"
geo "%s-ით მომხმარებლის შექმნის უფლება არ გაქვთ"
spa "No está autorizado a crear un usuario con %s"
sw "Hauruhusiwi kuunda mtumiaji na %s"
ER_WRONG_VALUE_FOR_TYPE
chi "不正确的%-.32s值:'%-.128sT' 函数:%-.32s"
eng "Incorrect %-.32s value: '%-.128sT' for function %-.32s"

View File

@@ -2983,15 +2983,12 @@ bool check_db_routine_access(THD *thd, privilege_t privilege,
const Sp_handler *sph,
bool no_errors)
{
privilege_t db_priv;
access_t db_priv(NO_ACL);
if (check_access(thd, privilege, db,
&db_priv, NULL, 0, no_errors))
return 1;
if ((db_priv & privilege) == privilege)
return 0;
return check_routine_level_acl(thd, (privilege & ~db_priv),
db, name, sph);
return check_routine_level_acl(thd, privilege,
db, name, sph, db_priv);
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,19 @@ enum mysql_db_table_field
MYSQL_DB_FIELD_COUNT
};
enum ACL_PRIV_TYPE
{
PRIV_TYPE_GLOBAL,
PRIV_TYPE_DB,
PRIV_TYPE_TABLE,
PRIV_TYPE_COLUMN,
PRIV_TYPE_FUNCTION,
PRIV_TYPE_PROCEDURE,
PRIV_TYPE_PACKAGE,
PRIV_TYPE_PACKAGE_BODY,
PRIV_TYPE_MAX
};
extern const TABLE_FIELD_DEF mysql_db_table_def;
extern LEX_CSTRING host_not_specified;
@@ -77,7 +90,7 @@ bool hostname_requires_resolving(const char *hostname);
bool acl_init(bool dont_read_acl_tables);
bool acl_reload(THD *thd);
void acl_free(bool end=0);
privilege_t acl_get_all3(Security_context *sctx, const char *db,
access_t acl_get_all3(Security_context *sctx, const char *db,
bool db_is_patern);
bool acl_authenticate(THD *thd, uint com_change_user_pkt_len);
bool acl_getroot(Security_context *sctx,
@@ -90,10 +103,10 @@ bool change_password(THD *thd, LEX_USER *user);
bool mysql_grant_role(THD *thd, List<LEX_USER> &user_list, bool revoke);
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, privilege_t rights,
bool revoke);
bool revoke, bool is_deny);
bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph,
List <LEX_USER> &user_list, privilege_t rights,
bool revoke, bool write_to_binlog);
bool revoke, bool write_to_binlog, bool deny);
bool grant_init();
void grant_free(void);
bool grant_reload(THD *thd);
@@ -110,18 +123,18 @@ bool check_grant_all_columns(THD *thd, privilege_t want_access,
bool check_grant_routine(THD *thd, privilege_t want_access,
TABLE_LIST *procs, const Sp_handler *sph,
bool no_error);
bool check_grant_db(THD *thd,const char *db);
bool check_grant_db(THD *thd,const access_t& priv, const char *db);
bool check_global_access(THD *thd, const privilege_t want_access, bool no_errors= false);
bool check_access(THD *thd, privilege_t want_access,
const char *db, privilege_t *save_priv,
const char *db, access_t *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors);
privilege_t get_table_grant(THD *thd, TABLE_LIST *table);
privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
access_t get_table_grant(THD *thd, TABLE_LIST *table);
access_t get_column_grant(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const Lex_ident_column &field_name);
bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
const char **hostname, const char **rolename);
const char **hostname, const char **rolename, bool *show_denies);
void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
const char *name, size_t length);
bool mysql_show_grants(THD *thd, LEX_USER *user);
@@ -147,7 +160,8 @@ bool sp_grant_privileges(THD *thd,
const Sp_handler *sph);
bool check_routine_level_acl(THD *thd, privilege_t acl,
const char *db, const char *name,
const Sp_handler *sph);
const Sp_handler *sph,
const access_t &db_access);
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);
@@ -284,10 +298,10 @@ bool acl_check_proxy_grant_access (THD *thd,
const LEX_CSTRING &user,
bool with_grant);
int acl_setauthorization(THD *thd, const LEX_USER *user);
int acl_setrole(THD *thd, const LEX_CSTRING &rolename, privilege_t access);
int acl_setrole(THD *thd, const LEX_CSTRING &rolename, const access_t& access);
int acl_check_setrole(THD *thd,
const LEX_CSTRING &rolename,
privilege_t *access);
access_t *access);
int acl_check_set_default_role(THD *thd,
const LEX_CSTRING &host,
const LEX_CSTRING &user,
@@ -317,6 +331,7 @@ class Sql_cmd_grant: public Sql_cmd
{
protected:
enum_sql_command m_command;
bool m_deny;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
void warn_hostname_requires_resolving(THD *thd, List<LEX_USER> &list);
bool user_list_reset_mqh(THD *thd, List<LEX_USER> &list);
@@ -324,10 +339,11 @@ protected:
#endif
public:
Sql_cmd_grant(enum_sql_command command)
:m_command(command)
:m_command(command),m_deny(false)
{ }
bool is_revoke() const { return m_command == SQLCOM_REVOKE; }
enum_sql_command sql_command_code() const override { return m_command; }
void set_deny() { m_deny= true; }
};

View File

@@ -540,7 +540,7 @@ bool Sql_cmd_alter_table::execute(THD *thd)
Table_specification_st create_info(lex->create_info);
Alter_info alter_info(lex->alter_info, thd->mem_root);
create_info.alter_info= &alter_info;
privilege_t priv(NO_ACL);
access_t priv(NO_ACL);
privilege_t priv_needed(ALTER_ACL);
bool result;

View File

@@ -275,7 +275,7 @@ static my_bool list_open_tables_callback(void *el, void *a)
/* Check if user has SELECT privilege for any column in the table */
arg->table_list.db= db;
arg->table_list.table_name= Lex_cstring_strlen(table_name);
arg->table_list.grant.privilege= NO_ACL;
arg->table_list.grant.privilege= access_t(NO_ACL);
if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
return FALSE;

View File

@@ -4935,10 +4935,10 @@ void Security_context::init()
host= user= ip= external_user= 0;
host_or_ip= "connecting host";
priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0';
master_access= NO_ACL;
master_access= access_t(NO_ACL);
password_expired= false;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACL;
db_access= access_t(NO_ACL);
#endif
}
@@ -4973,7 +4973,7 @@ void Security_context::skip_grants()
{
/* privileges for the user are unknown everything is allowed */
host_or_ip= (char *)"";
master_access= ALL_KNOWN_ACL;
master_access= access_t(ALL_KNOWN_ACL);
*priv_user= *priv_host= '\0';
password_expired= false;
}

View File

@@ -2096,8 +2096,8 @@ public:
char *external_user;
/* points to host if host is available, otherwise points to ip */
const char *host_or_ip;
privilege_t master_access; /* Global privileges from mysql.user */
privilege_t db_access; /* Privileges for current db */
access_t master_access; /* Global privileges from mysql.user */
access_t db_access; /* Privileges for current db */
bool password_expired;
@@ -4320,7 +4320,7 @@ public:
update auto-updatable fields (like auto_increment and timestamp).
*/
query_id_t query_id;
privilege_t col_access;
access_t col_access{NO_ACL};
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;

View File

@@ -60,7 +60,7 @@ static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *,
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
static void mysql_change_db_impl(THD *, LEX_CSTRING *, privilege_t,
static void mysql_change_db_impl(THD *, LEX_CSTRING *, const access_t&,
CHARSET_INFO *);
static bool mysql_rm_db_internal(THD *thd, const Lex_ident_db &db,
bool if_exists, bool silent);
@@ -1308,7 +1308,7 @@ exit:
if (unlikely(thd->db.str &&
cmp_db_names(Lex_ident_db(thd->db), db) && !error))
{
mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
mysql_change_db_impl(thd, NULL, access_t(NO_ACL), thd->variables.collation_server);
thd->session_tracker.current_schema.mark_as_changed(thd);
}
end:
@@ -1563,8 +1563,9 @@ err:
@param new_db_charset Character set of the new database.
*/
static void mysql_change_db_impl(THD *thd, LEX_CSTRING *new_db_name,
privilege_t new_db_access,
static void mysql_change_db_impl(THD *thd,
LEX_CSTRING *new_db_name,
const access_t& new_db_access,
CHARSET_INFO *new_db_charset)
{
/* 1. Change current database in THD. */
@@ -1713,7 +1714,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
LEX_CSTRING new_db_file_name;
Security_context *sctx= thd->security_ctx;
privilege_t db_access(sctx->db_access);
access_t db_access(sctx->db_access);
CHARSET_INFO *db_default_cl;
DBUG_ENTER("mysql_change_db");
@@ -1730,7 +1731,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
new_db_name->length == 0.
*/
mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
mysql_change_db_impl(thd, NULL, access_t(NO_ACL), thd->variables.collation_server);
goto done;
}
@@ -1747,7 +1748,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
{
/* Switch the current database to INFORMATION_SCHEMA. */
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL,
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, access_t(SELECT_ACL),
system_charset_info);
goto done;
}
@@ -1771,7 +1772,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
if (Lex_ident_db::check_name_with_error(new_db_file_name))
{
if (force)
mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
mysql_change_db_impl(thd, NULL, access_t(NO_ACL), thd->variables.collation_server);
DBUG_RETURN(ER_WRONG_DB_NAME);
}
@@ -1781,16 +1782,16 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (test_all_bits(sctx->master_access, DB_ACLS))
{
db_access= DB_ACLS;
db_access= access_t(DB_ACLS);
}
else
{
db_access= acl_get_all3(sctx, new_db_file_name.str, FALSE);
db_access|= sctx->master_access;
db_access.merge_with_parent(sctx->master_access);
}
if (!force&& !(db_access & DB_ACLS) &&
check_grant_db(thd, new_db_file_name.str))
check_grant_db(thd, db_access, new_db_file_name.str))
{
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user,
@@ -1816,7 +1817,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING &new_db_name, bool force)
/* Change db to NULL. */
mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
mysql_change_db_impl(thd, NULL, access_t(NO_ACL), thd->variables.collation_server);
/* The operation succeed. */
goto done;

View File

@@ -980,8 +980,8 @@ exit:
{
DBUG_ASSERT(derived->is_derived());
DBUG_ASSERT(derived->is_anonymous_derived_table());
table->grant.privilege= SELECT_ACL;
derived->grant.privilege= SELECT_ACL;
table->grant.privilege= access_t(SELECT_ACL);
derived->grant.privilege= access_t(SELECT_ACL);
}
#endif
/* Add new temporary table to list of open derived tables */

View File

@@ -999,7 +999,7 @@ int bootstrap(MYSQL_FILE *file)
thd->bootstrap=1;
my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
thd->max_client_packet_length= thd->net.max_packet;
thd->security_ctx->master_access= ALL_KNOWN_ACL;
thd->security_ctx->master_access= access_t(ALL_KNOWN_ACL);
#ifndef EMBEDDED_LIBRARY
mysql_thread_set_psi_id(thd->thread_id);
@@ -6625,17 +6625,17 @@ wsrep_error_label:
bool
check_access(THD *thd, privilege_t want_access,
const char *db, privilege_t *save_priv,
const char *db, access_t *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors)
{
#ifdef NO_EMBEDDED_ACCESS_CHECKS
if (save_priv)
*save_priv= GLOBAL_ACLS;
*save_priv= access_t(GLOBAL_ACLS);
return false;
#else
Security_context *sctx= thd->security_ctx;
privilege_t db_access(NO_ACL);
access_t db_access(NO_ACL);
/*
GRANT command:
@@ -6647,19 +6647,19 @@ check_access(THD *thd, privilege_t want_access,
set db_is_pattern according to 'dont_check_global_grants' value.
*/
bool db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
privilege_t dummy(NO_ACL);
access_t dummy(NO_ACL);
DBUG_ENTER("check_access");
DBUG_PRINT("enter",("db: %s want_access: %llx master_access: %llx",
db ? db : "",
(longlong) want_access,
(longlong) sctx->master_access));
db ? db : "",
(longlong) want_access,
(longlong) sctx->master_access.allow_bits()));
if (save_priv)
*save_priv= NO_ACL;
*save_priv= access_t(NO_ACL);
else
{
save_priv= &dummy;
dummy= NO_ACL;
dummy= access_t(NO_ACL);
}
/* check access may be called twice in a row. Don't change to same stage */
@@ -6680,7 +6680,10 @@ check_access(THD *thd, privilege_t want_access,
access= get_cached_schema_access(grant_internal_info, db);
if (access)
{
switch (access->check(want_access, save_priv))
privilege_t p(save_priv->allow_bits());
auto res= access->check(want_access, &p);
save_priv->set_allow_bits(p);
switch (res)
{
case ACL_INTERNAL_ACCESS_GRANTED:
/*
@@ -6731,10 +6734,11 @@ check_access(THD *thd, privilege_t want_access,
and the intersection of db- and host-privileges,
plus the internal privileges.
*/
*save_priv|= sctx->master_access | db_access;
db_access.merge_with_parent(sctx->master_access);
save_priv->merge_same_level(db_access);
}
else
*save_priv|= sctx->master_access;
save_priv->merge_same_level(sctx->master_access);
DBUG_RETURN(FALSE);
}
if (unlikely(((want_access & ~sctx->master_access) & ~DB_ACLS) ||
@@ -6768,21 +6772,30 @@ check_access(THD *thd, privilege_t want_access,
else
db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %llx want_access: %llx",
(longlong) db_access, (longlong) want_access));
(longlong) db_access.allow_bits(), (longlong) want_access));
/*
Save the union of User-table and the intersection between Db-table and
Host-table privileges, with the already saved internal privileges.
*/
db_access= (db_access | sctx->master_access);
*save_priv|= db_access;
db_access.merge_with_parent(sctx->master_access);
save_priv->merge_same_level(db_access);
/*
We could check for DENYs here, and do early return, if we find any.
But we do not, for the sake of better/more natural error reporting
ER_TABLEACCESS_DENIED_ERROR and ER_COLUMNACCESS_DENIED_ERROR contain
better info.
*/
/*
We need to investigate column- and table access if all requested privileges
belongs to the bit set of .
*/
bool need_table_or_column_check=
(want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;
(want_access & (TABLE_ACLS | PROC_ACLS |
db_access.maybe_allowed(want_access))) == want_access;
/*
Grant access if the requested access is in the intersection of
@@ -6918,7 +6931,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
not always automatically grant SELECT but use the grant tables.
See Bug#38837 need a way to disable information_schema for security
*/
table->grant.privilege= SELECT_ACL;
table->grant.privilege=access_t(SELECT_ACL);
switch (get_schema_table_idx(table->schema_table)) {
case SCH_SCHEMATA:
@@ -6939,7 +6952,8 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
&thd->col_access, NULL, FALSE, FALSE))
return TRUE;
if (!thd->col_access && check_grant_db(thd, dst_db_name))
if (!thd->col_access.maybe_allowed(DB_ACLS | SHOW_DB_ACL) &&
check_grant_db(thd, thd->col_access, dst_db_name))
{
status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
@@ -7108,7 +7122,7 @@ check_routine_access(THD *thd, privilege_t want_access, const LEX_CSTRING *db,
*/
DBUG_ASSERT((want_access & CREATE_PROC_ACL) == NO_ACL);
if ((thd->security_ctx->master_access & want_access) == want_access)
tables->grant.privilege= want_access;
tables->grant.privilege= access_t(want_access);
else if (check_access(thd, want_access, db->str,
&tables->grant.privilege,
&tables->grant.m_internal,
@@ -7135,7 +7149,7 @@ check_routine_access(THD *thd, privilege_t want_access, const LEX_CSTRING *db,
bool check_some_routine_access(THD *thd, const char *db, const char *name,
const Sp_handler *sph)
{
privilege_t save_priv(NO_ACL);
access_t save_priv(NO_ACL);
/*
The following test is just a shortcut for check_access() (to avoid
calculating db_access)
@@ -7148,11 +7162,10 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name,
if (thd->security_ctx->master_access & SHOW_PROC_WITHOUT_DEFINITION_ACLS)
return FALSE;
if (!check_access(thd, SHOW_PROC_WITHOUT_DEFINITION_ACLS,
db, &save_priv, NULL, 0, 1) ||
(save_priv & SHOW_PROC_WITHOUT_DEFINITION_ACLS))
db, &save_priv, NULL, 0, 1))
return FALSE;
return check_routine_level_acl(thd, SHOW_PROC_WITHOUT_DEFINITION_ACLS,
db, name, sph);
db, name, sph, save_priv);
}
@@ -9519,7 +9532,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
if (table->is_jtbm())
continue;
if (table->derived)
table->grant.privilege= SELECT_ACL;
table->grant.privilege= access_t(SELECT_ACL);
else if ((check_access(thd, UPDATE_ACL, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,

View File

@@ -174,7 +174,7 @@ inline bool check_routine_access(THD *thd, privilege_t want_access,
{ return false; }
inline bool check_some_access(THD *thd, privilege_t want_access, TABLE_LIST *table)
{
table->grant.privilege= want_access;
table->grant.privilege= access_t(want_access);
return false;
}
inline bool check_some_routine_access(THD *thd, const char *db,

View File

@@ -1969,7 +1969,7 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
char buff[1024];
const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
if (get_show_user(thd, thd->lex->grant_user, &username, &hostname, &rolename))
if (get_show_user(thd, thd->lex->grant_user, &username, &hostname, &rolename, NULL))
DBUG_RETURN(1);
if (username)

View File

@@ -1244,9 +1244,9 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
contains any table-specific privilege.
*/
DBUG_PRINT("debug", ("table_list->grant.privilege: %llx",
(longlong) (table_list->grant.privilege)));
(longlong) (table_list->grant.privilege.allow_bits())));
if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, table_list) ||
(table_list->grant.privilege & SHOW_CREATE_TABLE_ACLS) == NO_ACL)
table_list->grant.privilege.maybe_allowed(SHOW_CREATE_TABLE_ACLS) == NO_ACL)
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"SHOW", thd->security_ctx->priv_user,
@@ -1449,7 +1449,7 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
String buffer(buff, sizeof(buff), system_charset_info);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
privilege_t db_access(NO_ACL);
access_t db_access(NO_ACL);
#endif
Schema_specification_st create;
Protocol *protocol=thd->protocol;
@@ -1458,12 +1458,14 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (test_all_bits(sctx->master_access, DB_ACLS))
db_access=DB_ACLS;
db_access= access_t(DB_ACLS);
else
db_access= acl_get_all3(sctx, dbname->str, FALSE) |
sctx->master_access;
{
db_access= acl_get_all3(sctx, dbname->str, FALSE);
db_access.merge_with_parent(sctx->master_access);
}
if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname->str))
if (!(db_access & DB_ACLS) && check_grant_db(thd, db_access, dbname->str))
{
status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
@@ -5433,9 +5435,11 @@ static privilege_t get_schema_privileges_for_show(THD *thd, TABLE_LIST *tables,
if (thd->col_access & need)
return thd->col_access & need;
privilege_t all3= acl_get_all3(thd->security_ctx, tables->db.str, 0);
if (all3 & need)
return all3 & need;
access_t dbacc=
merge_with_parent(acl_get_all3(thd->security_ctx, tables->db.str, 0),
thd->security_ctx->master_access);
if (dbacc & need)
return dbacc & need;
check_grant(thd, need, tables, 0, 1, true);
return (on_any_column ? tables->grant.all_privilege()
@@ -5635,9 +5639,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
LEX_CSTRING *db_name= db_names.at(i);
DBUG_ASSERT(db_name->length <= NAME_LEN);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
access_t acc= acl_get_all3(sctx, db_name->str, 0).
merge_with_parent(sctx->master_access);
if (!check_access(thd, SELECT_ACL, db_name->str, &thd->col_access, 0,0,1) ||
sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
acl_get_all3(sctx, db_name->str, 0))
(acc.maybe_allowed(DB_ACLS | SHOW_DB_ACL)))
#endif
{
Dynamic_array<LEX_CSTRING*> table_names(PSI_INSTRUMENT_MEM);
@@ -5835,9 +5840,10 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
continue;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
acl_get_all3(sctx, db_name->str, false) ||
!check_grant_db(thd, db_name->str))
access_t acc= acl_get_all3(sctx, db_name->str, false)
.merge_with_parent(sctx->master_access);
if (acc.maybe_allowed(DB_ACLS | SHOW_DB_ACL) ||
!check_grant_db(thd, acc ,db_name->str))
#endif
{
load_db_opt_by_name(thd, db_name->str, &create);
@@ -6565,7 +6571,7 @@ int get_schema_column_record(THD *thd, TABLE_LIST *tables,
&tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table));
if (is_temporary_table(tables))
{
tables->grant.privilege|= TMP_TABLE_ACLS;
tables->grant.privilege.force_allow(TMP_TABLE_ACLS, true);
}
#endif
@@ -7461,11 +7467,11 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, TABLE *table,
uint j;
for (j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
auto access= get_column_grant(thd, &tables->grant, db_name->str,
access_t access= get_column_grant(thd, &tables->grant, db_name->str,
table_name->str,
key_part->field->field_name);
if (!access)
if (!(access & COL_ACLS))
break;
}
if (j != key_info->user_defined_key_parts)
@@ -7605,7 +7611,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
table_list.db= tables->db;
table_list.table_name= tables->table_name;
table_list.grant.privilege= thd->col_access;
privilege_t view_access(get_table_grant(thd, &table_list));
access_t view_access(get_table_grant(thd, &table_list));
if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) ==
(SHOW_VIEW_ACL|SELECT_ACL))
tables->allowed_show= TRUE;
@@ -8110,7 +8116,7 @@ get_schema_key_column_usage_record(THD *thd, TABLE_LIST *tables,
table_name->str,
key_part->field->field_name);
if (!access)
if (!(access & COL_ACLS))
break;
}
if (j != key_info->user_defined_key_parts)
@@ -8148,11 +8154,11 @@ get_schema_key_column_usage_record(THD *thd, TABLE_LIST *tables,
{
while ((r_info= it1++))
{
auto access= get_column_grant(thd, &tables->grant, db_name->str,
access_t access= get_column_grant(thd, &tables->grant, db_name->str,
table_name->str,
Lex_ident_column(*r_info));
if (!access)
if (!(access & COL_ACLS))
break;
}
if (!it1.at_end())
@@ -9518,7 +9524,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
if (!(table= create_schema_table(thd, table_list)))
DBUG_RETURN(1);
table->s->tmp_table= SYSTEM_TMP_TABLE;
table->grant.privilege= SELECT_ACL;
table->grant.privilege= access_t(SELECT_ACL);
/*
This test is necessary to make
case insensitive file systems +

View File

@@ -541,6 +541,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> DELETE_DOMAIN_ID_SYM
%token <kwd> DELETE_SYM /* SQL-2003-R */
%token <kwd> DENSE_RANK_SYM
%token <kwd> DENY
%token <kwd> DESCRIBE /* SQL-2003-R */
%token <kwd> DESC /* SQL-2003-N */
%token <kwd> DETERMINISTIC_SYM /* SQL-2003-R */
@@ -1879,7 +1880,7 @@ rule:
procedure_list procedure_list2 procedure_item
handler opt_generated_always
opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list
deny grant revoke set lock unlock string_list
table_lock_list table_lock
ref_list opt_match_clause opt_on_update_delete use
opt_delete_options opt_delete_option varchar nchar nvarchar
@@ -2191,6 +2192,7 @@ verb_clause:
| create
| deallocate
| delete
| deny
| describe
| do
| drop
@@ -18359,11 +18361,41 @@ handler_rkey_mode:
/* GRANT / REVOKE */
revoke:
REVOKE clear_privileges revoke_command
REVOKE clear_privileges revoke_privileges_or_proxy
{
}
|
REVOKE DENY clear_privileges revoke_privileges
{
static_cast<Sql_cmd_grant*>(Lex->m_sql_cmd)->set_deny();
}
| REVOKE clear_privileges ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
| REVOKE clear_privileges revoke_role
{}
;
revoke_command:
revoke_role:
admin_option_for_role FROM user_and_role_list
{
Lex->sql_command= SQLCOM_REVOKE_ROLE;
if (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
MYSQL_YYABORT;
}
;
revoke_privileges_or_proxy:
revoke_privileges
| PROXY_SYM ON user FROM user_list
{
if (Lex->stmt_revoke_proxy(thd, $3))
MYSQL_YYABORT;
}
;
revoke_privileges:
grant_privileges ON opt_table grant_ident FROM user_and_role_list
{
if (Lex->stmt_revoke_table(thd, $1, *$4))
@@ -18374,21 +18406,6 @@ revoke_command:
if (Lex->stmt_revoke_sp(thd, $1, *$4, *$3))
MYSQL_YYABORT;
}
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
| PROXY_SYM ON user FROM user_list
{
if (Lex->stmt_revoke_proxy(thd, $3))
MYSQL_YYABORT;
}
| admin_option_for_role FROM user_and_role_list
{
Lex->sql_command= SQLCOM_REVOKE_ROLE;
if (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
MYSQL_YYABORT;
}
;
admin_option_for_role:
@@ -18398,12 +18415,37 @@ admin_option_for_role:
{ Lex->with_admin_option= false; $$= $1; }
;
deny:
DENY clear_privileges grant_privileges_command
{
static_cast<Sql_cmd_grant*>(Lex->m_sql_cmd)->set_deny();
}
;
grant:
GRANT clear_privileges grant_command
{}
{
}
| GRANT clear_privileges grant_role_command
{
/*
TODO(cvicentiu) grant_role_command should use the Sql_cmd_grant
interface. This will allow moving Lex->stmt_grant_xxx calls
at this level instead of having them within grant_command
rule.
*/
}
;
grant_command:
grant_privileges_command
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
if (Lex->stmt_grant_proxy(thd, $3, $6))
MYSQL_YYABORT;
}
;
grant_privileges_command:
grant_privileges ON opt_table grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
@@ -18416,12 +18458,10 @@ grant_command:
if (Lex->stmt_grant_sp(thd, $1, *$4, *$3, $8))
MYSQL_YYABORT;
}
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
if (Lex->stmt_grant_proxy(thd, $3, $6))
MYSQL_YYABORT;
}
| grant_role TO_SYM grant_list opt_with_admin_option
;
grant_role_command:
grant_role TO_SYM grant_list opt_with_admin_option
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_GRANT_ROLE;
@@ -18429,7 +18469,6 @@ grant_command:
if (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
MYSQL_YYABORT;
}
;
opt_with_admin:

View File

@@ -7123,7 +7123,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
/* REPAIR needs SELECT_ACL */
while ((tbl= tb++))
{
tbl->grant.privilege= SELECT_ACL;
tbl->grant.privilege= access_t(SELECT_ACL);
tbl->security_ctx= save_security_ctx;
}
DBUG_RETURN(FALSE);
@@ -7154,7 +7154,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
thd->security_ctx= save_security_ctx;
#else
while ((tbl= tb++))
tbl->grant.privilege= ALL_KNOWN_ACL;
tbl->grant.privilege= access_t(ALL_KNOWN_ACL);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
DBUG_RETURN(FALSE);
}
@@ -8952,6 +8952,11 @@ void TABLE_LIST::reinit_before_use(THD *thd)
table= 0;
/* Reset is_schema_table_processed value(needed for I_S tables */
schema_table_state= NOT_PROCESSED;
/*
Reset want_privilege: a previous prepare/execute may have left
something here.
*/
grant.want_privilege= NO_ACL;
TABLE_LIST *embedded; /* The table at the current level of nesting. */
TABLE_LIST *parent_embedding= this; /* The parent nested table reference. */

View File

@@ -355,10 +355,8 @@ typedef struct st_grant_info
process. In each step the "wanted privilege" is checked against the
fulfilled privileges. When/if the intersection of these sets is empty,
access is granted.
The set is implemented as a bitmap, with the bits defined in sql_acl.h.
*/
privilege_t privilege;
access_t privilege;
/**
@brief the set of privileges that the current user needs to fulfil in
order to carry out the requested operation.
@@ -383,11 +381,11 @@ typedef struct st_grant_info
inline void refresh(const Security_context *sctx, const char *db,
const char *table);
inline privilege_t aggregate_privs();
inline access_t aggregate_privs();
inline privilege_t aggregate_cols();
/* OR table and all column privileges */
privilege_t all_privilege();
access_t all_privilege();
} GRANT_INFO;
enum tmp_table_type

View File

@@ -1238,7 +1238,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
}
table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
table->grant.privilege= TMP_TABLE_ACLS;
table->grant.privilege= access_t(TMP_TABLE_ACLS);
table->query_id= query_id;
share->tmp_table= (table->file->has_transaction_manager() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);

View File

@@ -4092,7 +4092,7 @@ void* start_wsrep_THD(void *arg)
/* from bootstrap()... */
thd->bootstrap=1;
thd->max_client_packet_length= thd->net.max_packet;
thd->security_ctx->master_access= ALL_KNOWN_ACL;
thd->security_ctx->master_access= access_t(ALL_KNOWN_ACL);
/* from handle_one_connection... */
pthread_detach_this_thread();

View File

@@ -522,7 +522,7 @@ thd::thd (my_bool ini, bool system_thread)
{
ptr->system_thread= SYSTEM_THREAD_GENERIC;
}
ptr->security_ctx->master_access= ALL_KNOWN_ACL;
ptr->security_ctx->master_access= access_t(ALL_KNOWN_ACL);
lex_start(ptr);
}
}

View File

@@ -174,7 +174,7 @@ int FiberScanState::init(TABLE *tbl,
}
/* Grant full privileges so the fiber can open any table */
fiber_thd->security_ctx->master_access= ALL_KNOWN_ACL;
fiber_thd->security_ctx->master_access= access_t(ALL_KNOWN_ACL);
/* Disable query cache — fiber queries are internal, not cacheable */
fiber_thd->query_cache_is_applicable= 0;