From 95ef78e432e66f9b851a81a6878b11f9cc55532f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:49:38 -0700 Subject: [PATCH] SET ROLE now works recursively for routines. The warnings present in the set_role_routine-simple testcase will be removed when reworking the grant privilege to call. --- .../acl_roles_set_role-routine-simple.result | 100 ++++++++++++++++++ .../t/acl_roles_set_role-routine-simple.test | 84 +++++++++++++++ sql/sql_acl.cc | 60 ++++++++++- 3 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/acl_roles_set_role-routine-simple.result create mode 100644 mysql-test/t/acl_roles_set_role-routine-simple.test diff --git a/mysql-test/r/acl_roles_set_role-routine-simple.result b/mysql-test/r/acl_roles_set_role-routine-simple.result new file mode 100644 index 00000000000..5933a3e174d --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-routine-simple.result @@ -0,0 +1,100 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +create role test_role3; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role3'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_role3 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role3 +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 test_role2@''; +grant execute on procedure mysql.test_proc to test_role2@''; +grant execute on mysql.* to test_role3@''; +flush privileges; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +use mysql; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +set role test_role1; +use mysql; +call test_proc(@a); +SELECT @a; +@a +3 +SELECT test_func('AABBCCDD'); +test_func('AABBCCDD') +Test string: AABBCCDD +show grants; +Grants for test_user@localhost +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'test_role2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +GRANT test_role3 TO 'test_user'@'localhost' +set role none; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +call test_proc(@a); +ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_proc' +SELECT test_func('AABBCCDD'); +ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_func' +set role test_role3; +show grants; +Grants for test_user@localhost +GRANT EXECUTE ON `mysql`.* TO 'test_role3' +GRANT USAGE ON *.* TO 'test_role3' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +call test_proc(@a); +SELECT @a; +@a +3 +SELECT test_func('AABBCCDD'); +test_func('AABBCCDD') +Test string: AABBCCDD +drop user 'test_user'@'localhost'; +revoke execute on function mysql.test_func from test_role2@''; +revoke execute on procedure mysql.test_proc from test_role2@''; +revoke execute on mysql.* from test_role3@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +drop function mysql.test_func; +Warnings: +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_func' +drop procedure mysql.test_proc; +Warnings: +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-routine-simple.test b/mysql-test/t/acl_roles_set_role-routine-simple.test new file mode 100644 index 00000000000..e731c4dee10 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-routine-simple.test @@ -0,0 +1,84 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +create role test_role3; + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role3'); + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; + +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 test_role2@''; +grant execute on procedure mysql.test_proc to test_role2@''; + +grant execute on mysql.* to test_role3@''; + +flush privileges; + +change_user 'test_user'; +--sorted_result +show grants; + +--error ER_DBACCESS_DENIED_ERROR +use mysql; +set role test_role1; +use mysql; + +call test_proc(@a); +SELECT @a; + +SELECT test_func('AABBCCDD'); + +--sorted_result +show grants; +set role none; +--sorted_result +show grants; + +--error ER_PROCACCESS_DENIED_ERROR +call test_proc(@a); + +--error ER_PROCACCESS_DENIED_ERROR +SELECT test_func('AABBCCDD'); + +set role test_role3; +--sorted_result +show grants; +call test_proc(@a); +SELECT @a; + +SELECT test_func('AABBCCDD'); + +change_user 'root'; +drop user 'test_user'@'localhost'; +revoke execute on function mysql.test_func from test_role2@''; +revoke execute on procedure mysql.test_proc from test_role2@''; +revoke execute on mysql.* from test_role3@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +drop function mysql.test_func; +drop procedure mysql.test_proc; +flush privileges; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 69e619c01b5..700e2d38783 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3895,6 +3895,8 @@ public: GRANT_NAME(const char *h, const char *d,const char *u, const char *t, ulong p, bool is_routine); GRANT_NAME (TABLE *form, bool is_routine); + /* copy from an inherited GRANT_NAME */ + GRANT_NAME(GRANT_NAME *source, char *u, bool is_routine); virtual ~GRANT_NAME() {}; virtual bool ok() { return privs != 0; } void set_user_details(const char *h, const char *d, @@ -3951,6 +3953,12 @@ GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, set_user_details(h, d, u, t, is_routine); } +GRANT_NAME::GRANT_NAME(GRANT_NAME *source, char *u, bool is_routine) + :db(0), tname(0), privs(source->privs), init_privs(0) +{ + set_user_details("", source->db, u, source->tname, is_routine); +} + GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) :GRANT_NAME(h,d,u,t,p, FALSE), cols(c) @@ -7414,6 +7422,7 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) /* elements of the arrays */ ACL_DB *target_db, *source_db; GRANT_TABLE *target_table, *source_table; + GRANT_NAME *target_name, *source_name; MEM_ROOT *memex_ptr = &memex; (void) my_init_dynamic_array(&target_objs,sizeof(void *), 50, 100, MYF(0)); @@ -7488,7 +7497,7 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) void **); if (!strcmp(source_table->db, grant_table->db) && !strcmp(source_table->tname, grant_table->tname)) - target_table= grant_table;; + target_table= grant_table; } if (target_table) @@ -7506,8 +7515,55 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) } + reset_dynamic(&source_objs); + reset_dynamic(&target_objs); + /* Merge function and procedure privileges */ - /* TODO */ + for (uint is_proc= 0; is_proc < 2; is_proc++) { + HASH *hash; + if (is_proc) + hash= &proc_priv_hash; + else + hash= &func_priv_hash; + + for (uint i=0 ; i < hash->records ; i++) + { + GRANT_NAME *grant_name= (GRANT_NAME *) my_hash_element(hash, i); + if (grant_name->user && (!grant_name->host.hostname || + !grant_name->host.hostname[0])) + { + if (!strcmp(target->user.str, grant_name->user)) + push_dynamic(&target_objs, (uchar*)&grant_name); + if (!strcmp(source->user.str, grant_name->user)) + push_dynamic(&source_objs, (uchar*)&grant_name); + } + } + + for (uint i=0 ; i < source_objs.elements; i++) + { + source_name= (GRANT_NAME *)*dynamic_element(&source_objs, i, void **); + target_name= NULL; + for (uint j=0; j < target_objs.elements; j++) + { + GRANT_NAME *grant_name= (GRANT_NAME *)*dynamic_element(&target_objs, i, + void **); + if (!strcmp(source_name->db, grant_name->db) && + !strcmp(source_name->tname, grant_name->tname)) + target_name= grant_name; + } + + if (target_name) + { + target_name->privs|= source_name->privs; + } + else + { + target_name= new (memex_ptr) GRANT_NAME(source_name, target->user.str, + is_proc); + my_hash_insert(hash, (uchar *) target_name); + } + } + } /* cleanup */ delete_dynamic(&source_objs);