diff --git a/mysql-test/suite/roles/recursive_dbug.result b/mysql-test/suite/roles/recursive_dbug.result index 417602c5c60..6a86df655e7 100644 --- a/mysql-test/suite/roles/recursive_dbug.result +++ b/mysql-test/suite/roles/recursive_dbug.result @@ -56,7 +56,7 @@ connection default; grant select on *.* to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 19 +Debug_role_merges_global 20 Debug_role_merges_db 0 Debug_role_merges_table 0 Debug_role_merges_column 0 @@ -106,7 +106,7 @@ connection default; revoke select on *.* from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 +Debug_role_merges_global 29 Debug_role_merges_db 0 Debug_role_merges_table 0 Debug_role_merges_column 0 @@ -124,8 +124,8 @@ connection default; grant select on mysql.* to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 8 +Debug_role_merges_global 29 +Debug_role_merges_db 9 Debug_role_merges_table 0 Debug_role_merges_column 0 Debug_role_merges_routine 0 @@ -164,8 +164,8 @@ connection default; revoke select on mysql.* from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 +Debug_role_merges_global 29 +Debug_role_merges_db 17 Debug_role_merges_table 0 Debug_role_merges_column 0 Debug_role_merges_routine 0 @@ -177,9 +177,9 @@ connection default; grant select on mysql.roles_mapping to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 8 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 9 Debug_role_merges_column 0 Debug_role_merges_routine 0 connection foo; @@ -217,9 +217,9 @@ connection default; revoke select on mysql.roles_mapping from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 16 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 17 Debug_role_merges_column 0 Debug_role_merges_routine 0 connection foo; @@ -230,10 +230,10 @@ connection default; grant select(User) on mysql.roles_mapping to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 24 -Debug_role_merges_column 8 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 26 +Debug_role_merges_column 9 Debug_role_merges_routine 0 connection foo; select count(*) from mysql.roles_mapping; @@ -272,10 +272,10 @@ connection default; grant select(Host) on mysql.roles_mapping to role3; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 30 -Debug_role_merges_column 14 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 33 +Debug_role_merges_column 16 Debug_role_merges_routine 0 connection foo; select count(concat(User,Host,Role)) from mysql.roles_mapping; @@ -312,10 +312,10 @@ connection default; revoke select(User) on mysql.roles_mapping from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 38 -Debug_role_merges_column 22 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 41 +Debug_role_merges_column 24 Debug_role_merges_routine 0 connection foo; select count(concat(User,Host)) from mysql.roles_mapping; @@ -327,10 +327,10 @@ connection default; revoke select(Host) on mysql.roles_mapping from role3; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 44 -Debug_role_merges_column 28 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 47 +Debug_role_merges_column 30 Debug_role_merges_routine 0 connection foo; select count(concat(Host)) from mysql.roles_mapping; @@ -342,11 +342,11 @@ create function fn1() returns char(10) return "fn1"; grant execute on procedure test.pr1 to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 44 -Debug_role_merges_column 28 -Debug_role_merges_routine 8 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 47 +Debug_role_merges_column 30 +Debug_role_merges_routine 9 connection foo; call pr1(); ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1' @@ -360,11 +360,11 @@ connection default; grant execute on function test.fn1 to role5; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 44 -Debug_role_merges_column 28 -Debug_role_merges_routine 13 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 47 +Debug_role_merges_column 30 +Debug_role_merges_routine 15 connection foo; select fn1(); fn1() @@ -373,11 +373,11 @@ connection default; revoke execute on procedure test.pr1 from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 44 -Debug_role_merges_column 28 -Debug_role_merges_routine 21 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 47 +Debug_role_merges_column 30 +Debug_role_merges_routine 23 connection foo; call pr1(); ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1' @@ -388,11 +388,11 @@ connection default; revoke execute on function test.fn1 from role5; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 44 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 47 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 connection foo; select fn1(); ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1' @@ -403,67 +403,67 @@ drop function fn1; grant select on mysql.roles_mapping to role3; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 50 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 54 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 grant select on mysql.roles_mapping to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 53 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 58 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 revoke select on mysql.roles_mapping from role3; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 54 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 59 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 revoke select on mysql.roles_mapping from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 16 -Debug_role_merges_table 62 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 17 +Debug_role_merges_table 67 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 grant select on mysql.* to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 24 -Debug_role_merges_table 62 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 26 +Debug_role_merges_table 67 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 grant select on test.* to role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 32 -Debug_role_merges_table 62 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 35 +Debug_role_merges_table 67 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 revoke select on mysql.* from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 40 -Debug_role_merges_table 62 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 43 +Debug_role_merges_table 67 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 revoke select on test.* from role1; show status like 'debug%'; Variable_name Value -Debug_role_merges_global 27 -Debug_role_merges_db 48 -Debug_role_merges_table 62 -Debug_role_merges_column 28 -Debug_role_merges_routine 26 +Debug_role_merges_global 29 +Debug_role_merges_db 51 +Debug_role_merges_table 67 +Debug_role_merges_column 30 +Debug_role_merges_routine 28 connection default; drop user foo@localhost; drop role role1; diff --git a/mysql-test/suite/roles/role_grant_propagate-29458.result b/mysql-test/suite/roles/role_grant_propagate-29458.result new file mode 100644 index 00000000000..a8ee6e7d987 --- /dev/null +++ b/mysql-test/suite/roles/role_grant_propagate-29458.result @@ -0,0 +1,137 @@ +create user foo; +create database some_db; +create table some_db.t1 (a int, b int, secret int); +CREATE PROCEDURE some_db.p1 (OUT param1 INT) +BEGIN +SELECT COUNT(*) INTO param1 FROM some_db.t1; +END; +// +CREATE FUNCTION some_db.f1 (param1 INT) RETURNS INT +BEGIN +DECLARE c INT; +SET c = 100; +RETURN param1 + c; +END; +// +# +# These roles will form a two level hierarchy. +# The "select" role will have the select privilege, while +# the active role will inherit the select role. +# +# The active role will be granted a different privilege but on the same +# level (global, database, table, proc respectively) *after* the 'select' +# role has been granted its select privilege. +# +create role r_select_global; +create role r_active_global; +create role r_select_database; +create role r_active_database; +create role r_select_table; +create role r_active_table; +create role r_select_column; +create role r_active_column; +create role r_execute_proc; +create role r_active_proc; +create role r_execute_func; +create role r_active_func; +grant r_select_global to r_active_global; +grant r_select_database to r_active_database; +grant r_select_table to r_active_table; +grant r_select_column to r_active_column; +grant r_execute_proc to r_active_proc; +grant r_execute_func to r_active_func; +# +# These 3 roles form a chain, where only the upper level has select +# privileges and the middle level will receive a grant for the same level +# privilege, but different kind (for example select on upper and insert +# on middle). +# +# The lower level should inherit both rights. +# +create role upper_level; +create role middle_level; +create role lower_level; +grant upper_level to middle_level; +grant middle_level to lower_level; +grant r_active_global to foo; +grant r_active_database to foo; +grant r_active_table to foo; +grant r_active_column to foo; +grant r_active_proc to foo; +grant r_active_func to foo; +grant lower_level to foo; +grant select on *.* to r_select_global; +grant select on some_db.* to r_select_database; +grant select on some_db.t1 to r_select_table; +grant select(a) on some_db.t1 to r_select_column; +grant select on *.* to upper_level; +grant execute on procedure some_db.p1 to r_execute_proc; +grant execute on function some_db.f1 to r_execute_func; +# +# Granting a privilege different than select on the corresponding level. +# This tests that the base role correctly inherits its granted roles +# privileges. +# +grant insert on *.* to r_active_global; +grant insert on some_db.* to r_active_database; +grant insert on some_db.t1 to r_active_table; +grant insert(a) on some_db.t1 to r_active_column; +grant insert on *.* to middle_level; +grant alter routine on procedure some_db.p1 to r_active_proc; +grant alter routine on function some_db.f1 to r_active_func; +flush privileges; +connect con1, localhost, foo,,; +select * from some_db.t1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' +# +# Before MDEV-29458 fix, all these commands would return +# ER_TABLEACCESS_DENIED_ERROR +# +set role r_active_global; +select * from some_db.t1; +a b secret +set role r_active_database; +select * from some_db.t1; +a b secret +set role r_active_table; +select * from some_db.t1; +a b secret +set role r_active_column; +select a from some_db.t1; +a +set role lower_level; +select * from some_db.t1; +a b secret +set role r_active_proc; +set @var=100; +call some_db.p1(@var); +set role r_active_func; +select some_db.f1(10); +some_db.f1(10) +110 +disconnect con1; +# +# Cleanup. +# +connection default; +drop database some_db; +drop role r_select_global, r_select_database, r_select_table, r_select_column; +drop role r_active_global, r_active_database, r_active_table, r_active_column; +drop role r_execute_proc, r_execute_func; +drop role r_active_proc, r_active_func; +drop role upper_level, middle_level, lower_level; +drop user foo; +# +# Test that dropping of roles clears the intermediate generated +# (such as an `acl_dbs` element with 0 init_access, but with access != 0) +# datastructures. +# +create role test_role1; +create role test_role2; +grant test_role2 to test_role1; +grant select on mysql.* to test_role2; +grant select on mysql.user to test_role2; +grant select(user) on mysql.user to test_role2; +drop role test_role1, test_role2; +create role test_role1; +drop role test_role1; diff --git a/mysql-test/suite/roles/role_grant_propagate-29458.test b/mysql-test/suite/roles/role_grant_propagate-29458.test new file mode 100644 index 00000000000..07c29a3800c --- /dev/null +++ b/mysql-test/suite/roles/role_grant_propagate-29458.test @@ -0,0 +1,164 @@ +--source include/not_embedded.inc + +create user foo; +create database some_db; +create table some_db.t1 (a int, b int, secret int); + +delimiter //; +CREATE PROCEDURE some_db.p1 (OUT param1 INT) + BEGIN + SELECT COUNT(*) INTO param1 FROM some_db.t1; + END; +// +delimiter ;// + +delimiter //; +CREATE FUNCTION some_db.f1 (param1 INT) RETURNS INT + BEGIN + DECLARE c INT; + SET c = 100; + RETURN param1 + c; + END; +// +delimiter ;// + +--echo # +--echo # These roles will form a two level hierarchy. +--echo # The "select" role will have the select privilege, while +--echo # the active role will inherit the select role. +--echo # +--echo # The active role will be granted a different privilege but on the same +--echo # level (global, database, table, proc respectively) *after* the 'select' +--echo # role has been granted its select privilege. +--echo # + +create role r_select_global; +create role r_active_global; + +create role r_select_database; +create role r_active_database; + +create role r_select_table; +create role r_active_table; + +create role r_select_column; +create role r_active_column; + +create role r_execute_proc; +create role r_active_proc; + +create role r_execute_func; +create role r_active_func; + +grant r_select_global to r_active_global; +grant r_select_database to r_active_database; +grant r_select_table to r_active_table; +grant r_select_column to r_active_column; +grant r_execute_proc to r_active_proc; +grant r_execute_func to r_active_func; + +--echo # +--echo # These 3 roles form a chain, where only the upper level has select +--echo # privileges and the middle level will receive a grant for the same level +--echo # privilege, but different kind (for example select on upper and insert +--echo # on middle). +--echo # +--echo # The lower level should inherit both rights. +--echo # +create role upper_level; +create role middle_level; +create role lower_level; + +grant upper_level to middle_level; +grant middle_level to lower_level; + +grant r_active_global to foo; +grant r_active_database to foo; +grant r_active_table to foo; +grant r_active_column to foo; +grant r_active_proc to foo; +grant r_active_func to foo; +grant lower_level to foo; + +grant select on *.* to r_select_global; +grant select on some_db.* to r_select_database; +grant select on some_db.t1 to r_select_table; +grant select(a) on some_db.t1 to r_select_column; +grant select on *.* to upper_level; + +grant execute on procedure some_db.p1 to r_execute_proc; +grant execute on function some_db.f1 to r_execute_func; + + +--echo # +--echo # Granting a privilege different than select on the corresponding level. +--echo # This tests that the base role correctly inherits its granted roles +--echo # privileges. +--echo # +grant insert on *.* to r_active_global; +grant insert on some_db.* to r_active_database; +grant insert on some_db.t1 to r_active_table; +grant insert(a) on some_db.t1 to r_active_column; +grant insert on *.* to middle_level; + +grant alter routine on procedure some_db.p1 to r_active_proc; +grant alter routine on function some_db.f1 to r_active_func; +flush privileges; + +--connect (con1, localhost, foo,,) +--error ER_TABLEACCESS_DENIED_ERROR +select * from some_db.t1; + +--echo # +--echo # Before MDEV-29458 fix, all these commands would return +--echo # ER_TABLEACCESS_DENIED_ERROR +--echo # +set role r_active_global; +select * from some_db.t1; +set role r_active_database; +select * from some_db.t1; +set role r_active_table; +select * from some_db.t1; +set role r_active_column; +select a from some_db.t1; +set role lower_level; +select * from some_db.t1; + +set role r_active_proc; +set @var=100; +call some_db.p1(@var); + +set role r_active_func; +select some_db.f1(10); + +disconnect con1; + +--echo # +--echo # Cleanup. +--echo # +connection default; + +drop database some_db; +drop role r_select_global, r_select_database, r_select_table, r_select_column; +drop role r_active_global, r_active_database, r_active_table, r_active_column; +drop role r_execute_proc, r_execute_func; +drop role r_active_proc, r_active_func; +drop role upper_level, middle_level, lower_level; +drop user foo; + +--echo # +--echo # Test that dropping of roles clears the intermediate generated +--echo # (such as an `acl_dbs` element with 0 init_access, but with access != 0) +--echo # datastructures. +--echo # +create role test_role1; +create role test_role2; + +grant test_role2 to test_role1; +grant select on mysql.* to test_role2; +grant select on mysql.user to test_role2; +grant select(user) on mysql.user to test_role2; +drop role test_role1, test_role2; + +create role test_role1; +drop role test_role1; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ba457083b75..6947dc13b5b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -5586,6 +5586,7 @@ static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context } static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *); +static bool merge_one_role_privileges(ACL_ROLE *grantee, PRIVS_TO_MERGE what); /** rebuild privileges of all affected roles @@ -5604,6 +5605,11 @@ static void propagate_role_grants(ACL_ROLE *role, mysql_mutex_assert_owner(&acl_cache->lock); PRIVS_TO_MERGE data= { what, db, name }; + /* + Before updating grants to roles that inherit from this role, ensure that + the effective grants on this role are up-to-date from *its* granted roles. + */ + merge_one_role_privileges(role, data); /* Changing privileges of a role causes all other roles that had this role granted to them to have their rights invalidated. @@ -6393,11 +6399,12 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)), return !changed; // don't recurse into the subgraph if privs didn't change } -static bool merge_one_role_privileges(ACL_ROLE *grantee) +static +bool merge_one_role_privileges(ACL_ROLE *grantee, + PRIVS_TO_MERGE what) { - PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 }; grantee->counter= 1; - return merge_role_privileges(0, grantee, &data); + return merge_role_privileges(0, grantee, &what); } /***************************************************************** @@ -7144,7 +7151,7 @@ bool mysql_grant_role(THD *thd, List &list, bool revoke) Only need to propagate grants when granting/revoking a role to/from a role */ - if (role_as_user && merge_one_role_privileges(role_as_user) == 0) + if (role_as_user) propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL); } @@ -9702,9 +9709,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, size_t old_key_length= acl_role->user.length; if (drop) { - /* all grants must be revoked from this role by now. propagate this */ - propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL); - // delete the role from cross-reference arrays for (uint i=0; i < acl_role->role_grants.elements; i++) { @@ -9720,6 +9724,12 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, remove_ptr_from_dynarray(&grantee->role_grants, acl_role); } + /* Remove all of the role_grants from this role. */ + delete_dynamic(&acl_role->role_grants); + + /* all grants must be revoked from this role by now. propagate this */ + propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL); + my_hash_delete(&acl_roles, (uchar*) acl_role); DBUG_RETURN(1); }