diff --git a/mysql-test/suite/roles/drop_routines.result b/mysql-test/suite/roles/drop_routines.result new file mode 100644 index 00000000000..bbb6cc6ca39 --- /dev/null +++ b/mysql-test/suite/roles/drop_routines.result @@ -0,0 +1,104 @@ +create role r1; +create role r2; +create role r3; +create role r4; +create user u1; +grant r2 to r1; +grant r3 to r2; +grant r4 to r3; +grant r1 to u1; +grant r4 to r1; +show grants for u1; +Grants for u1@% +GRANT USAGE ON *.* TO 'u1'@'%' +GRANT r1 TO 'u1'@'%' +show grants for r1; +Grants for r1 +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +grant SELECT on *.* to u1; +grant INSERT on mysql.* to r1; +grant DELETE on mysql.roles_mapping to r2; +grant UPDATE on mysql.user to r3; +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); +create procedure mysql.test_proc (OUT param1 INT) +begin +select COUNT(*) into param1 from mysql.roles_mapping; +end| +grant execute on function mysql.test_func to r2; +grant execute on procedure mysql.test_proc to r3; +grant execute on mysql.* to r4; +show grants for r1; +Grants for r1 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +show grants for r2; +Grants for r2 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r3 TO 'r2' +GRANT r4 TO 'r3' +show grants for r3; +Grants for r3 +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r4 TO 'r3' +show grants for r4; +Grants for r4 +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT USAGE ON *.* TO 'r4' +drop function mysql.test_func; +Warnings: +Warning 1403 There is no such grant defined for user 'r1' on host '' on routine 'test_func' +Warning 1403 There is no such grant defined for user 'r1' on host '' on routine 'test_func' +drop procedure mysql.test_proc; +Warnings: +Warning 1403 There is no such grant defined for user 'r2' on host '' on routine 'test_proc' +Warning 1403 There is no such grant defined for user 'r1' on host '' on routine 'test_proc' +Warning 1403 There is no such grant defined for user 'r2' on host '' on routine 'test_proc' +Warning 1403 There is no such grant defined for user 'r1' on host '' on routine 'test_proc' +show grants for r1; +Grants for r1 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +drop role r1, r2, r3, r4; +drop user u1; diff --git a/mysql-test/suite/roles/drop_routines.test b/mysql-test/suite/roles/drop_routines.test new file mode 100644 index 00000000000..78f2039f55a --- /dev/null +++ b/mysql-test/suite/roles/drop_routines.test @@ -0,0 +1,61 @@ +source include/not_embedded.inc; + +create role r1; +create role r2; +create role r3; +create role r4; +create user u1; + +#CREATE A CHAIN OF ROLES +grant r2 to r1; +grant r3 to r2; +grant r4 to r3; +grant r1 to u1; +grant r4 to r1; + +--sorted_result +show grants for u1; +--sorted_result +show grants for r1; + +grant SELECT on *.* to u1; +grant INSERT on mysql.* to r1; +grant DELETE on mysql.roles_mapping to r2; +grant UPDATE on mysql.user to r3; + +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); + + +delimiter |; +create procedure mysql.test_proc (OUT param1 INT) +begin + select COUNT(*) into param1 from mysql.roles_mapping; +end| +delimiter ;| + +grant execute on function mysql.test_func to r2; +grant execute on procedure mysql.test_proc to r3; +grant execute on mysql.* to r4; + +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +drop function mysql.test_func; +drop procedure mysql.test_proc; + +# HERE there are func_priv_hash and proc_priv hash entries inherited from other roles that are not +# deleted. These remain and could affect privilege checks + +--sorted_result +show grants for r1; + +drop role r1, r2, r3, r4; +drop user u1; diff --git a/mysql-test/suite/roles/revoke_all.result b/mysql-test/suite/roles/revoke_all.result new file mode 100644 index 00000000000..7e72b5bc766 --- /dev/null +++ b/mysql-test/suite/roles/revoke_all.result @@ -0,0 +1,183 @@ +create role r1; +create role r2; +create role r3; +create role r4; +create user u1; +grant r2 to r1; +grant r3 to r2; +grant r4 to r3; +grant r1 to u1; +grant r4 to r1; +show grants for u1; +Grants for u1@% +GRANT USAGE ON *.* TO 'u1'@'%' +GRANT r1 TO 'u1'@'%' +show grants for r1; +Grants for r1 +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +grant SELECT on *.* to u1; +grant INSERT on mysql.* to r1; +grant DELETE on mysql.roles_mapping to r2; +grant UPDATE on mysql.user to r3; +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); +create procedure mysql.test_proc (OUT param1 INT) +begin +select COUNT(*) into param1 from mysql.roles_mapping; +end| +grant execute on function mysql.test_func to r2; +grant execute on procedure mysql.test_proc to r3; +grant execute on mysql.* to r4; +show grants for r1; +Grants for r1 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +show grants for r2; +Grants for r2 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r3 TO 'r2' +GRANT r4 TO 'r3' +show grants for r3; +Grants for r3 +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r4 TO 'r3' +show grants for r4; +Grants for r4 +GRANT EXECUTE ON `mysql`.* TO 'r4' +GRANT USAGE ON *.* TO 'r4' +revoke all privileges, grant option from r4; +show grants for r1; +Grants for r1 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +GRANT r4 TO 'r3' +show grants for r2; +Grants for r2 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r3 TO 'r2' +GRANT r4 TO 'r3' +show grants for r3; +Grants for r3 +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'r3' +GRANT UPDATE ON `mysql`.`user` TO 'r3' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r4 TO 'r3' +show grants for r4; +Grants for r4 +GRANT USAGE ON *.* TO 'r4' +revoke all privileges, grant option from r3; +show grants for r1; +Grants for r1 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r3 TO 'r2' +GRANT r4 TO 'r1' +show grants for r2; +Grants for r2 +GRANT DELETE ON `mysql`.`roles_mapping` TO 'r2' +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'r2' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r3' +GRANT r3 TO 'r2' +show grants for r3; +Grants for r3 +GRANT USAGE ON *.* TO 'r3' +show grants for r4; +Grants for r4 +GRANT USAGE ON *.* TO 'r4' +revoke all privileges, grant option from r2; +show grants for r1; +Grants for r1 +GRANT INSERT ON `mysql`.* TO 'r1' +GRANT USAGE ON *.* TO 'r1' +GRANT USAGE ON *.* TO 'r2' +GRANT USAGE ON *.* TO 'r4' +GRANT r2 TO 'r1' +GRANT r4 TO 'r1' +show grants for r2; +Grants for r2 +GRANT USAGE ON *.* TO 'r2' +show grants for r3; +Grants for r3 +GRANT USAGE ON *.* TO 'r3' +show grants for r4; +Grants for r4 +GRANT USAGE ON *.* TO 'r4' +revoke all privileges, grant option from r1; +show grants for r1; +Grants for r1 +GRANT USAGE ON *.* TO 'r1' +show grants for r2; +Grants for r2 +GRANT USAGE ON *.* TO 'r2' +show grants for r3; +Grants for r3 +GRANT USAGE ON *.* TO 'r3' +show grants for r4; +Grants for r4 +GRANT USAGE ON *.* TO 'r4' +revoke all privileges, grant option from u1; +show grants for u1; +Grants for u1@% +GRANT USAGE ON *.* TO 'u1'@'%' +drop function mysql.test_func; +drop procedure mysql.test_proc; +show grants for r1; +Grants for r1 +GRANT USAGE ON *.* TO 'r1' +drop role r1, r2, r3, r4; +drop user u1; diff --git a/mysql-test/suite/roles/revoke_all.test b/mysql-test/suite/roles/revoke_all.test new file mode 100644 index 00000000000..a3dee981c40 --- /dev/null +++ b/mysql-test/suite/roles/revoke_all.test @@ -0,0 +1,103 @@ +source include/not_embedded.inc; + +create role r1; +create role r2; +create role r3; +create role r4; +create user u1; + +#CREATE A CHAIN OF ROLES +grant r2 to r1; +grant r3 to r2; +grant r4 to r3; +grant r1 to u1; +grant r4 to r1; + +--sorted_result +show grants for u1; +--sorted_result +show grants for r1; + +grant SELECT on *.* to u1; +grant INSERT on mysql.* to r1; +grant DELETE on mysql.roles_mapping to r2; +grant UPDATE on mysql.user to r3; + +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); + + +delimiter |; +create procedure mysql.test_proc (OUT param1 INT) +begin + select COUNT(*) into param1 from mysql.roles_mapping; +end| +delimiter ;| + +grant execute on function mysql.test_func to r2; +grant execute on procedure mysql.test_proc to r3; +grant execute on mysql.* to r4; + +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +revoke all privileges, grant option from r4; + +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +revoke all privileges, grant option from r3; +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +revoke all privileges, grant option from r2; +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +revoke all privileges, grant option from r1; +--sorted_result +show grants for r1; +--sorted_result +show grants for r2; +--sorted_result +show grants for r3; +--sorted_result +show grants for r4; + +revoke all privileges, grant option from u1; + +show grants for u1; + +drop function mysql.test_func; +drop procedure mysql.test_proc; + +--sorted_result +show grants for r1; + +drop role r1, r2, r3, r4; +drop user u1; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f1344ebaf96..94c880a6848 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2385,7 +2385,7 @@ static bool add_role_user_mapping(const char *uname, const char *hname, /* This helper function is used to removes roles and grantees from the corresponding cross-reference arrays. see remove_role_user_mapping(). - as such, it asserts that a element to delete is present in the array, + as such, it asserts that an element to delete is present in the array, and is present only once. */ static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr) @@ -4371,6 +4371,9 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, The following should never happen as we first check the in memory grant tables for the user. There is however always a small change that the user has modified the grant tables directly. + + Also, there is also a second posibility that this routine entry + is created for a role by being inherited from a granted role. */ if (revoke_grant) { // no row, no revoke @@ -9323,7 +9326,10 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) result= -1; continue; } - if (!find_user_exact(lex_user->host.str, lex_user->user.str)) + + /* This is not a role and the user could not be found */ + if (!lex_user->is_role() && + !find_user_exact(lex_user->host.str, lex_user->user.str)) { result= -1; continue; @@ -9429,6 +9435,10 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { + /* only inherited rights, nothing to do here */ + if (!grant_proc->init_privs) + continue; + if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user, grant_proc->db, grant_proc->tname, @@ -9443,6 +9453,59 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) counter++; } } while (revoked); + + ACL_USER_BASE *user_or_role; + /* remove role grants */ + if (lex_user->is_role()) + { + /* this can not fail due to get_current_user already having searched for it */ + user_or_role= find_acl_role(lex_user->user.str); + } + else + { + user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str); + } + /* + Find every role grant pair matching the role_grants array and remove it, + both from the acl_roles_mappings and the roles_mapping table + */ + for (counter= 0; counter < user_or_role->role_grants.elements; counter++) + { + ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants, + counter, ACL_ROLE**); + ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user, + &lex_user->host, + &role_grant->user); + if (replace_roles_mapping_table(tables[6].table, + &lex_user->user, + &lex_user->host, + &role_grant->user, false, pair, true)) + { + result= -1; //Something went wrong + } + /* + Delete from the parent_grantee array of the roles granted, + the entry pointing to this user_or_role + */ + remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role); + } + /* TODO + How to handle an error in the replace_roles_mapping_table, in + regards to the privileges held in memory + */ + + /* Finally, clear the role_grants array */ + if (counter == user_or_role->role_grants.elements) + { + reset_dynamic(&user_or_role->role_grants); + } + /* + If we are revoking from a role, we need to update all the parent grantees + */ + if (lex_user->is_role()) + { + propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL, 0, 0); + } } mysql_mutex_unlock(&acl_cache->lock);