diff --git a/mysql-test/suite/roles/MDEV-35743.result b/mysql-test/suite/roles/MDEV-35743.result new file mode 100644 index 0000000000000..9f97e71d1c6cb --- /dev/null +++ b/mysql-test/suite/roles/MDEV-35743.result @@ -0,0 +1,29 @@ +create user supervisor; +create role r_admin; +create role r_limit_mod; +create role r_limit_view; +grant r_limit_mod to r_admin with admin option; +grant r_limit_view to r_admin with admin option; +grant r_limit_view to r_limit_mod; +create or replace function g1(p int) returns int return p+1; +grant execute on function g1 to r_limit_mod; +grant r_admin to supervisor with admin option; +set default role r_admin for supervisor; +connect con, localhost, supervisor; +select g1(1); +g1(1) +2 +disconnect con; +connection default; +flush privileges; +connect con, localhost, supervisor; +select g1(2); +g1(2) +3 +disconnect con; +connection default; +drop function g1; +drop role r_limit_view; +drop role r_limit_mod; +drop role r_admin; +drop user supervisor; diff --git a/mysql-test/suite/roles/MDEV-35743.test b/mysql-test/suite/roles/MDEV-35743.test new file mode 100644 index 0000000000000..0416d0f11a919 --- /dev/null +++ b/mysql-test/suite/roles/MDEV-35743.test @@ -0,0 +1,34 @@ +# MDEV-35743: FLUSH PRIVILEGES breaks role-based function execution grants +create user supervisor; + +create role r_admin; +create role r_limit_mod; +create role r_limit_view; + +grant r_limit_mod to r_admin with admin option; +grant r_limit_view to r_admin with admin option; +grant r_limit_view to r_limit_mod; + +create or replace function g1(p int) returns int return p+1; + +grant execute on function g1 to r_limit_mod; +grant r_admin to supervisor with admin option; +set default role r_admin for supervisor; + +--connect con, localhost, supervisor +select g1(1); +--disconnect con +--connection default +flush privileges; + +--connect con, localhost, supervisor +select g1(2); +--disconnect con +--connection default + +# Cleanup +drop function g1; +drop role r_limit_view; +drop role r_limit_mod; +drop role r_admin; +drop user supervisor; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 788442523db29..8d055c50e422b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6359,6 +6359,7 @@ struct PRIVS_TO_MERGE ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY } what; const char *db, *name; + bool rebuild_all; // full rebuild (acl_load/FLUSH): merge all roles, no shortcut }; @@ -6413,7 +6414,7 @@ static void propagate_role_grants(ACL_ROLE *role, return; mysql_mutex_assert_owner(&acl_cache->lock); - PRIVS_TO_MERGE data= { what, db, name }; + PRIVS_TO_MERGE data= { what, db, name, false }; /* Before updating grants to roles that inherit from this role, ensure that @@ -7222,6 +7223,8 @@ static int merge_role_privileges(ACL_USER_BASE *, changed|= merge_role_routine_grant_privileges(grantee, data->db, data->name, &role_hash, &package_body_priv_hash); + if (data->rebuild_all) + return 0; // full rebuild: always descend so every counter reaches zero return !changed; // don't recurse into the subgraph if privs didn't change } @@ -8295,7 +8298,7 @@ static my_bool propagate_role_grants_action(void *role_ptr, return 0; mysql_mutex_assert_owner(&acl_cache->lock); - PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 }; + PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0, true }; traverse_role_graph_up(role, &data, NULL, merge_role_privileges); return 0; }