From b2f16628cf4500111d59a8e52cc44851742ddfcc Mon Sep 17 00:00:00 2001 From: Sergei Golubchik <sergii@pisem.net> Date: Fri, 18 Oct 2013 12:17:49 -0700 Subject: [PATCH] support DEFINER=role and DEFINER=current_role --- include/my_user.h | 6 +- mysql-test/r/acl_roles_definer.result | 625 ++++++++++++++++++ mysql-test/r/acl_roles_rename_user.result | 28 +- mysql-test/r/acl_roles_rpl_definer.result | 49 ++ .../r/acl_roles_set_role-recursive.result | 3 +- mysql-test/r/trigger_notembedded.result | 4 +- mysql-test/t/acl_roles_definer.test | 330 +++++++++ mysql-test/t/acl_roles_rename_user.test | 36 +- mysql-test/t/acl_roles_rpl_definer.test | 40 ++ sql-common/my_user.c | 32 +- sql/log_event.cc | 15 +- sql/share/errmsg-utf8.txt | 5 +- sql/sp.cc | 19 +- sql/sp_head.cc | 9 +- sql/sql_acl.cc | 503 +++++++------- sql/sql_class.cc | 10 +- sql/sql_class.h | 21 +- sql/sql_parse.cc | 74 +-- sql/sql_parse.h | 6 +- sql/sql_show.cc | 7 +- sql/sql_trigger.cc | 4 +- sql/sql_view.cc | 12 +- sql/sql_yacc.yy | 8 +- sql/structs.h | 8 + 24 files changed, 1467 insertions(+), 387 deletions(-) create mode 100644 mysql-test/r/acl_roles_definer.result create mode 100644 mysql-test/r/acl_roles_rpl_definer.result create mode 100644 mysql-test/t/acl_roles_definer.test create mode 100644 mysql-test/t/acl_roles_rpl_definer.test diff --git a/include/my_user.h b/include/my_user.h index 46eb11a500d..404996e996c 100644 --- a/include/my_user.h +++ b/include/my_user.h @@ -26,9 +26,9 @@ C_MODE_START -void parse_user(const char *user_id_str, size_t user_id_len, - char *user_name_str, size_t *user_name_len, - char *host_name_str, size_t *host_name_len); +int parse_user(const char *user_id_str, size_t user_id_len, + char *user_name_str, size_t *user_name_len, + char *host_name_str, size_t *host_name_len); C_MODE_END diff --git a/mysql-test/r/acl_roles_definer.result b/mysql-test/r/acl_roles_definer.result new file mode 100644 index 00000000000..0010853be78 --- /dev/null +++ b/mysql-test/r/acl_roles_definer.result @@ -0,0 +1,625 @@ +create database mysqltest1; +use mysqltest1; +create table t1 (a int, b int, c int); +insert t1 values (1,10,100),(2,20,200); +create role role1; +grant select (a) on mysqltest1.t1 to role1; +grant event,execute,trigger on mysqltest1.* to role1; +grant role1 to current_user; +create role role2; +grant insert,select on mysqltest1.t1 to role2; +grant event,execute,trigger on mysqltest1.* to role2; +grant create view on mysqltest1.* to foo@localhost; +create role role4; +grant select on mysqltest1.t1 to role4; +grant role4 to foo@localhost; +create definer=current_role view test.v1 as select a+b,c from t1; +ERROR 0L000: Invalid definer +set role role1; +create definer=current_role view test.v1 as select a+b,c from t1; +show create view test.v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `test`.`v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +set role none; +create definer=role2 view test.v2 as select a+b,c from t1; +show create view test.v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `test`.`v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +create definer=role3 view test.v3 as select a+b,c from t1; +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show create view test.v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`role3`@`%` SQL SECURITY DEFINER VIEW `test`.`v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show grants; +Grants for foo@localhost +GRANT role4 TO 'foo'@'localhost' +GRANT USAGE ON *.* TO 'foo'@'localhost' +GRANT CREATE VIEW ON `mysqltest1`.* TO 'foo'@'localhost' +select * from test.v1; +ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +select * from test.v2; +a+b c +11 100 +22 200 +select * from test.v3; +ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO) +create definer=role4 view test.v4 as select a+b,c from t1; +ERROR 42000: ANY command denied to user 'foo'@'localhost' for table 't1' +select * from t1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' +set role role4; +select * from t1; +a b c +1 10 100 +2 20 200 +create view test.v4 as select a+b,c from t1; +create definer=role4 view test.v5 as select a+b,c from t1; +select * from test.v4; +ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +select * from test.v5; +a+b c +11 100 +22 200 +set role none; +select * from test.v4; +ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +select * from test.v5; +a+b c +11 100 +22 200 +drop role role4; +show create view test.v5; +View Create View character_set_client collation_connection +v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +Warnings: +Note 1449 The user specified as a definer ('role4'@'') does not exist +select * from test.v5; +ERROR HY000: The user specified as a definer ('role4'@'') does not exist +grant select on mysqltest1.t1 to role4; +show create view test.v5; +View Create View character_set_client collation_connection +v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +Warnings: +Note 1449 The user specified as a definer ('role4'@'') does not exist +select * from test.v5; +ERROR HY000: The user specified as a definer ('role4'@'') does not exist +show create view test.v5; +View Create View character_set_client collation_connection +v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4`@`%` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +select * from test.v5; +a+b c +11 100 +22 200 +drop user role4; +create table t2 select * from t1; +create definer=current_role trigger tr1 before insert on t2 for each row +insert t1 values (111, 222, 333); +ERROR 0L000: Invalid definer +set role role1; +create definer=current_role trigger tr1 before insert on t2 for each row +insert t1 values (111, 222, 333); +show create trigger tr1; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr1 CREATE DEFINER=`role1` trigger tr1 before insert on t2 for each row +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +set role none; +insert t2 values (11,22,33); +ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1' +select * from t1; +a b c +1 10 100 +2 20 200 +select * from t2; +a b c +1 10 100 +2 20 200 +create definer=role2 trigger tr2 before delete on t2 for each row +insert t1 values (111, 222, 333); +show create trigger tr2; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr2 CREATE DEFINER=`role2` trigger tr2 before delete on t2 for each row +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +delete from t2 where a=1; +select * from t1; +a b c +1 10 100 +2 20 200 +111 222 333 +select * from t2; +a b c +2 20 200 +delete from t1 where a=111; +create definer=role3 trigger tr3 before update on t2 for each row +insert t1 values (111, 222, 333); +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show create trigger tr3; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr3 CREATE DEFINER=`role3`@`%` trigger tr3 before update on t2 for each row +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +update t2 set b=2 where a=2; +ERROR HY000: The user specified as a definer ('role3'@'%') does not exist +select * from t1; +a b c +1 10 100 +2 20 200 +select * from t2; +a b c +2 20 200 +flush tables; +show create trigger tr2; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr2 CREATE DEFINER=`role2`@`` trigger tr2 before delete on t2 for each row +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +delete from t2 where a=2; +ERROR HY000: The user specified as a definer ('role2'@'%') does not exist +select * from t1; +a b c +1 10 100 +2 20 200 +select * from t2; +a b c +2 20 200 +create definer=current_role procedure pr1() insert t1 values (111, 222, 333); +ERROR 0L000: Invalid definer +set role role1; +create definer=current_role procedure pr1() insert t1 values (111, 222, 333); +show create procedure pr1; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pr1 CREATE DEFINER=`role1` PROCEDURE `pr1`() +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +set role none; +call pr1(); +ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1' +select * from t1; +a b c +1 10 100 +2 20 200 +create definer=role2 procedure pr2() insert t1 values (111, 222, 333); +show create procedure pr2; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pr2 CREATE DEFINER=`role2` PROCEDURE `pr2`() +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +call pr2(); +select * from t1; +a b c +1 10 100 +2 20 200 +111 222 333 +delete from t1 where a=111; +create definer=role3 procedure pr3() insert t1 values (111, 222, 333); +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show create procedure pr3; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pr3 CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`() +insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci +call pr3(); +ERROR HY000: The user specified as a definer ('role3'@'%') does not exist +select * from t1; +a b c +1 10 100 +2 20 200 +update mysql.proc set definer='role2@' where definer='role2'; +call pr2(); +ERROR HY000: The user specified as a definer ('role2'@'%') does not exist +create definer=current_role function fn1() returns int return (select sum(a+b) from t1); +ERROR 0L000: Invalid definer +set role role1; +create definer=current_role function fn1() returns int return (select sum(a+b) from t1); +show create function fn1; +Function sql_mode Create Function character_set_client collation_connection Database Collation +fn1 CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11) +return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci +set role none; +select fn1(); +ERROR 42000: SELECT command denied to user 'role1'@'' for column 'b' in table 't1' +select * from t1; +a b c +1 10 100 +2 20 200 +create definer=role2 function fn2() returns int return (select sum(a+b) from t1); +show create function fn2; +Function sql_mode Create Function character_set_client collation_connection Database Collation +fn2 CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11) +return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci +select fn2(); +fn2() +33 +create definer=role3 function fn3() returns int return (select sum(a+b) from t1); +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show create function fn3; +Function sql_mode Create Function character_set_client collation_connection Database Collation +fn3 CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11) +return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci +select fn3(); +ERROR HY000: The user specified as a definer ('role3'@'%') does not exist +set global event_scheduler=on; +create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do +insert t1 values (111, 1, 0); +ERROR 0L000: Invalid definer +set role role1; +create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do +insert t1 values (111, 2, 0); +show create event e1; +Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation +e1 SYSTEM CREATE DEFINER=`role1` EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0) latin1 latin1_swedish_ci latin1_swedish_ci +set role none; +create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do +insert t1 values (111, 3, 0); +Warnings: +Note 1449 The user specified as a definer ('role3'@'%') does not exist +show create event e3; +Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation +e3 SYSTEM CREATE DEFINER=`role3`@`%` EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0) latin1 latin1_swedish_ci latin1_swedish_ci +create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do +insert t1 values (111, 4, 0); +show create event e2; +Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation +e2 SYSTEM CREATE DEFINER=`role2` EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0) latin1 latin1_swedish_ci latin1_swedish_ci +set global event_scheduler=off; +select distinct * from t1; +a b c +1 10 100 +111 4 0 +2 20 200 +delete from t1 where a=111; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `test`; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v1` ( + `a+b` tinyint NOT NULL, + `c` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v2` ( + `a+b` tinyint NOT NULL, + `c` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3` ( + `a+b` tinyint NOT NULL, + `c` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v4` ( + `a+b` tinyint NOT NULL, + `c` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v5` ( + `a+b` tinyint NOT NULL, + `c` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `mysqltest1`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; +INSERT INTO `t1` VALUES (1,10,100),(2,20,200); +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; +INSERT INTO `t2` VALUES (2,20,200); +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +/*!50003 CREATE*/ /*!50017 DEFINER=`role1`*/ /*!50003 trigger tr1 before insert on t2 for each row +insert t1 values (111, 222, 333) */;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +/*!50003 CREATE*/ /*!50017 DEFINER=`role3`@`%`*/ /*!50003 trigger tr3 before update on t2 for each row +insert t1 values (111, 222, 333) */;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +/*!50003 CREATE*/ /*!50017 DEFINER=`role2`@``*/ /*!50003 trigger tr2 before delete on t2 for each row +insert t1 values (111, 222, 333) */;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50106 SET @save_time_zone= @@TIME_ZONE */ ; +DELIMITER ;; +/*!50003 SET @saved_cs_client = @@character_set_client */ ;; +/*!50003 SET @saved_cs_results = @@character_set_results */ ;; +/*!50003 SET @saved_col_connection = @@collation_connection */ ;; +/*!50003 SET character_set_client = latin1 */ ;; +/*!50003 SET character_set_results = latin1 */ ;; +/*!50003 SET collation_connection = latin1_swedish_ci */ ;; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ;; +/*!50003 SET sql_mode = '' */ ;; +/*!50003 SET @saved_time_zone = @@time_zone */ ;; +/*!50003 SET time_zone = 'SYSTEM' */ ;; +/*!50106 CREATE*/ /*!50117 DEFINER=`role1`*/ /*!50106 EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0) */ ;; +/*!50003 SET time_zone = @saved_time_zone */ ;; +/*!50003 SET sql_mode = @saved_sql_mode */ ;; +/*!50003 SET character_set_client = @saved_cs_client */ ;; +/*!50003 SET character_set_results = @saved_cs_results */ ;; +/*!50003 SET collation_connection = @saved_col_connection */ ;; +DELIMITER ;; +/*!50003 SET @saved_cs_client = @@character_set_client */ ;; +/*!50003 SET @saved_cs_results = @@character_set_results */ ;; +/*!50003 SET @saved_col_connection = @@collation_connection */ ;; +/*!50003 SET character_set_client = latin1 */ ;; +/*!50003 SET character_set_results = latin1 */ ;; +/*!50003 SET collation_connection = latin1_swedish_ci */ ;; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ;; +/*!50003 SET sql_mode = '' */ ;; +/*!50003 SET @saved_time_zone = @@time_zone */ ;; +/*!50003 SET time_zone = 'SYSTEM' */ ;; +/*!50106 CREATE*/ /*!50117 DEFINER=`role2`*/ /*!50106 EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0) */ ;; +/*!50003 SET time_zone = @saved_time_zone */ ;; +/*!50003 SET sql_mode = @saved_sql_mode */ ;; +/*!50003 SET character_set_client = @saved_cs_client */ ;; +/*!50003 SET character_set_results = @saved_cs_results */ ;; +/*!50003 SET collation_connection = @saved_col_connection */ ;; +DELIMITER ;; +/*!50003 SET @saved_cs_client = @@character_set_client */ ;; +/*!50003 SET @saved_cs_results = @@character_set_results */ ;; +/*!50003 SET @saved_col_connection = @@collation_connection */ ;; +/*!50003 SET character_set_client = latin1 */ ;; +/*!50003 SET character_set_results = latin1 */ ;; +/*!50003 SET collation_connection = latin1_swedish_ci */ ;; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ;; +/*!50003 SET sql_mode = '' */ ;; +/*!50003 SET @saved_time_zone = @@time_zone */ ;; +/*!50003 SET time_zone = 'SYSTEM' */ ;; +/*!50106 CREATE*/ /*!50117 DEFINER=`role3`@`%`*/ /*!50106 EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0) */ ;; +/*!50003 SET time_zone = @saved_time_zone */ ;; +/*!50003 SET sql_mode = @saved_sql_mode */ ;; +/*!50003 SET character_set_client = @saved_cs_client */ ;; +/*!50003 SET character_set_results = @saved_cs_results */ ;; +/*!50003 SET collation_connection = @saved_col_connection */ ;; +DELIMITER ; +/*!50106 SET TIME_ZONE= @save_time_zone */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11) +return (select sum(a+b) from t1) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11) +return (select sum(a+b) from t1) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11) +return (select sum(a+b) from t1) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role1` PROCEDURE `pr1`() +insert t1 values (111, 222, 333) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role2`@`%` PROCEDURE `pr2`() +insert t1 values (111, 222, 333) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`() +insert t1 values (111, 222, 333) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; + +USE `test`; +/*!50001 DROP TABLE IF EXISTS `v1`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v2`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v3`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`role3`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v4`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`foo`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v4` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v5`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`role4`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +USE `mysqltest1`; +drop trigger tr1; +drop trigger tr2; +drop trigger tr3; +drop procedure pr1; +drop procedure pr2; +drop procedure pr3; +drop function fn1; +drop function fn2; +drop function fn3; +drop event e1; +drop event e2; +drop event e3; +drop view test.v1, test.v2, test.v3, test.v4, test.v5; +drop table t1, t2; +drop role role1, role2; +drop user foo@localhost; +drop database mysqltest1; +use test; +create user utest; +prepare stmt1 from 'grant select on *.* to utest'; +execute stmt1; +show grants for utest; +Grants for utest@% +GRANT SELECT ON *.* TO 'utest'@'%' +drop user utest; +create role utest; +execute stmt1; +show grants for utest; +Grants for utest +GRANT SELECT ON *.* TO 'utest' +drop role utest; diff --git a/mysql-test/r/acl_roles_rename_user.result b/mysql-test/r/acl_roles_rename_user.result index 9108a34c143..2145c7e117c 100644 --- a/mysql-test/r/acl_roles_rename_user.result +++ b/mysql-test/r/acl_roles_rename_user.result @@ -1,11 +1,9 @@ -create user 'test_user'@'localhost'; -create user 'test_role1'@''; -update mysql.user set is_role='Y' where user='test_role1'; +create user test_user@localhost; +create role test_role1; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', 'test_user', 'test_role1'); -create user 'test_role2'@''; -update mysql.user set is_role='Y' where user='test_role2'; +create role test_role2; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'test_role1', 'test_role2'); @@ -25,26 +23,6 @@ select * from roles_mapping; HostFk UserFk RoleFk test_role1 test_role2 newhost test_user_rm test_role1 -rename user 'test_role2'@'' to 'test_role2_rm'@''; -select user, host from user where user like 'test%'; -user host -test_role1 -test_role2_rm -test_user_rm newhost -select * from roles_mapping; -HostFk UserFk RoleFk - test_role1 test_role2_rm -newhost test_user_rm test_role1 -rename user 'test_role1'@'' to 'test_role1_rm'@''; -select user, host from user where user like 'test%'; -user host -test_role1_rm -test_role2_rm -test_user_rm newhost -select * from roles_mapping; -HostFk UserFk RoleFk - test_role1_rm test_role2_rm -newhost test_user_rm test_role1_rm delete from mysql.roles_mapping; delete from mysql.user where user like 'test%'; flush privileges; diff --git a/mysql-test/r/acl_roles_rpl_definer.result b/mysql-test/r/acl_roles_rpl_definer.result new file mode 100644 index 00000000000..158e420c03e --- /dev/null +++ b/mysql-test/r/acl_roles_rpl_definer.result @@ -0,0 +1,49 @@ +include/master-slave.inc +[connection master] +create role role1; +grant execute on test.* to role1; +grant role1 to current_user; +set role role1; +create definer=current_user procedure pcu() select current_user; +create definer=root@localhost procedure pu() select "root@localhost"; +create definer=current_role procedure pcr() select current_role; +create definer=role1 procedure pr() select "role1"; +show create procedure pcu; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pcu CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`() +select current_user latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pu; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pu CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`() +select "root@localhost" latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pcr; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pcr CREATE DEFINER=`role1` PROCEDURE `pcr`() +select current_role latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pr; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pr CREATE DEFINER=`role1` PROCEDURE `pr`() +select "role1" latin1 latin1_swedish_ci latin1_swedish_ci +[connection slave] +show create procedure pcu; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pcu CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`() +select current_user latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pu; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pu CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`() +select "root@localhost" latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pcr; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pcr CREATE DEFINER=`role1` PROCEDURE `pcr`() +select current_role latin1 latin1_swedish_ci latin1_swedish_ci +show create procedure pr; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +pr CREATE DEFINER=`role1` PROCEDURE `pr`() +select "role1" latin1 latin1_swedish_ci latin1_swedish_ci +drop procedure pcu; +drop procedure pu; +drop procedure pcr; +drop procedure pr; +drop role role1; +include/rpl_end.inc diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 168960fbe62..7c1f46aa1d9 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -27,8 +27,7 @@ Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv D % test_role1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y select * from mysql.user where user like 'test_role2'; Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role - test_role2 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y -% test_role2 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +% test_role2 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y flush privileges; select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' diff --git a/mysql-test/r/trigger_notembedded.result b/mysql-test/r/trigger_notembedded.result index d94713cbe02..3349d5b007b 100644 --- a/mysql-test/r/trigger_notembedded.result +++ b/mysql-test/r/trigger_notembedded.result @@ -162,7 +162,7 @@ SELECT trigger_name, definer FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_n trigger_name definer trg1 trg2 @ -trg3 @abc@def@@ +trg3 @abc@def@@% trg4 @hostname trg5 @abcdef@@@hostname @@ -170,7 +170,7 @@ SELECT * FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_name; TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION def mysqltest_db1 trg1 INSERT def mysqltest_db1 t1 0 NULL SET @a = 1 ROW BEFORE NULL NULL OLD NEW NULL latin1 latin1_swedish_ci latin1_swedish_ci def mysqltest_db1 trg2 INSERT def mysqltest_db1 t1 0 NULL SET @a = 2 ROW AFTER NULL NULL OLD NEW NULL @ latin1 latin1_swedish_ci latin1_swedish_ci -def mysqltest_db1 trg3 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 3 ROW BEFORE NULL NULL OLD NEW NULL @abc@def@@ latin1 latin1_swedish_ci latin1_swedish_ci +def mysqltest_db1 trg3 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 3 ROW BEFORE NULL NULL OLD NEW NULL @abc@def@@% latin1 latin1_swedish_ci latin1_swedish_ci def mysqltest_db1 trg4 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 4 ROW AFTER NULL NULL OLD NEW NULL @hostname latin1 latin1_swedish_ci latin1_swedish_ci def mysqltest_db1 trg5 DELETE def mysqltest_db1 t1 0 NULL SET @a = 5 ROW BEFORE NULL NULL OLD NEW NULL @abcdef@@@hostname latin1 latin1_swedish_ci latin1_swedish_ci diff --git a/mysql-test/t/acl_roles_definer.test b/mysql-test/t/acl_roles_definer.test new file mode 100644 index 00000000000..49e948d60c3 --- /dev/null +++ b/mysql-test/t/acl_roles_definer.test @@ -0,0 +1,330 @@ +# create view +# create trigger +# create procedure +# create event +# mysqldump dumping the definer + +let MYSQLD_DATADIR=`select @@datadir`; + +create database mysqltest1; +use mysqltest1; + +create table t1 (a int, b int, c int); +insert t1 values (1,10,100),(2,20,200); + +# non-priv role granted +create role role1; +grant select (a) on mysqltest1.t1 to role1; +grant event,execute,trigger on mysqltest1.* to role1; + +grant role1 to current_user; + +# priv role +create role role2; +grant insert,select on mysqltest1.t1 to role2; +grant event,execute,trigger on mysqltest1.* to role2; + +# create a non-priv user and a priv role granted to him +grant create view on mysqltest1.* to foo@localhost; +create role role4; +grant select on mysqltest1.t1 to role4; +grant role4 to foo@localhost; + +################################################## +# views +################################################## + +# no curent role = error +--error ER_MALFORMED_DEFINER +create definer=current_role view test.v1 as select a+b,c from t1; + +# definer=current_role, but it has doesn't have enough privileges +set role role1; +create definer=current_role view test.v1 as select a+b,c from t1; +show create view test.v1; +set role none; + +# definer=role_name, privileges ok +create definer=role2 view test.v2 as select a+b,c from t1; +show create view test.v2; + +# definer=non_existent_role +create definer=role3 view test.v3 as select a+b,c from t1; +show create view test.v3; + +connect (c1, localhost, foo,,mysqltest1); +connection c1; +show grants; + +# role1 doesn't have enough privileges for v1 to work +--error ER_VIEW_INVALID +select * from test.v1; + +# role2 is ok, v2 is ok +select * from test.v2; + +# role3 is treated as a user name role3@%, doesn't exist, v3 fails +--error ER_ACCESS_DENIED_ERROR +select * from test.v3; + +# fails, no SUPER - cannot specify a definer arbitrarily +--error ER_TABLEACCESS_DENIED_ERROR +create definer=role4 view test.v4 as select a+b,c from t1; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from t1; +set role role4; +select * from t1; + +# can select from t1, but the view won't work, by default definer=current_user +create view test.v4 as select a+b,c from t1; + +# now role4 is the current_role, can be specified as a definer +create definer=role4 view test.v5 as select a+b,c from t1; + +--error ER_VIEW_INVALID +select * from test.v4; +select * from test.v5; +set role none; +--error ER_VIEW_INVALID +select * from test.v4; +select * from test.v5; + +connection default; + +drop role role4; + +show create view test.v5; +--error ER_NO_SUCH_USER +select * from test.v5; + +grant select on mysqltest1.t1 to role4; +show create view test.v5; +--error ER_NO_SUCH_USER +select * from test.v5; + +# pretend it's an old view from before 10.0.5 +perl; +local $/; +my $f= "$ENV{MYSQLD_DATADIR}/test/v5.frm"; +open(F, '<', $f) or die "open(<$f): $!"; +$_=<F>; +s/create-version=2/create-version=1/; +open(F, '>', $f) or die "open(>$f): $!"; +syswrite F, $_ or die "syswrite($f): $!" +EOF + +show create view test.v5; +select * from test.v5; +drop user role4; + + +################################################## +# trigger +################################################## + +create table t2 select * from t1; + +# no curent role = error +--error ER_MALFORMED_DEFINER +create definer=current_role trigger tr1 before insert on t2 for each row + insert t1 values (111, 222, 333); + +# definer=current_role, but it has doesn't have enough privileges +set role role1; +create definer=current_role trigger tr1 before insert on t2 for each row + insert t1 values (111, 222, 333); +show create trigger tr1; +set role none; + +--error ER_TABLEACCESS_DENIED_ERROR +insert t2 values (11,22,33); +select * from t1; +select * from t2; + +# definer=role_name, privileges ok +create definer=role2 trigger tr2 before delete on t2 for each row + insert t1 values (111, 222, 333); +show create trigger tr2; +delete from t2 where a=1; +select * from t1; +select * from t2; +delete from t1 where a=111; + +# definer=non_existent_role +create definer=role3 trigger tr3 before update on t2 for each row + insert t1 values (111, 222, 333); +show create trigger tr3; +--error ER_NO_SUCH_USER +update t2 set b=2 where a=2; +select * from t1; +select * from t2; + +flush tables; + +# change triggers to use pre-10.0.5 definer with an empty hostname +perl; +local $/; +my $f= "$ENV{MYSQLD_DATADIR}/mysqltest1/t2.TRG"; +open(F, '<', $f) or die "open(<$f): $!"; +$_=<F>; +s/'role2'/'role2\@'/; +s/`role2`/$&\@``/; +open(F, '>', $f) or die "open(>$f): $!"; +syswrite F, $_ or die "syswrite($f): $!" +EOF + +show create trigger tr2; +--error ER_NO_SUCH_USER +delete from t2 where a=2; +select * from t1; +select * from t2; + +################################################## +# stored procedures +################################################## + +# no curent role = error +--error ER_MALFORMED_DEFINER +create definer=current_role procedure pr1() insert t1 values (111, 222, 333); + +# definer=current_role, but it has doesn't have enough privileges +set role role1; +create definer=current_role procedure pr1() insert t1 values (111, 222, 333); +show create procedure pr1; +set role none; + +--error ER_TABLEACCESS_DENIED_ERROR +call pr1(); +select * from t1; + +# definer=role_name, privileges ok +create definer=role2 procedure pr2() insert t1 values (111, 222, 333); +show create procedure pr2; +call pr2(); +select * from t1; +delete from t1 where a=111; + +# definer=non_existent_role +create definer=role3 procedure pr3() insert t1 values (111, 222, 333); +show create procedure pr3; +--error ER_NO_SUCH_USER +call pr3(); +select * from t1; + +# change a procedure to use pre-10.0.5 definer with an empty hostname +update mysql.proc set definer='role2@' where definer='role2'; +--error ER_NO_SUCH_USER +call pr2(); + +################################################## +# stored functions +################################################## + +# no curent role = error +--error ER_MALFORMED_DEFINER +create definer=current_role function fn1() returns int return (select sum(a+b) from t1); + +# definer=current_role, but it has doesn't have enough privileges +set role role1; +create definer=current_role function fn1() returns int return (select sum(a+b) from t1); +show create function fn1; +set role none; + +--error ER_COLUMNACCESS_DENIED_ERROR +select fn1(); +select * from t1; + +# definer=role_name, privileges ok +create definer=role2 function fn2() returns int return (select sum(a+b) from t1); +show create function fn2; +select fn2(); + +# definer=non_existent_role +create definer=role3 function fn3() returns int return (select sum(a+b) from t1); +show create function fn3; +--error ER_NO_SUCH_USER +select fn3(); + +################################################## +# events +################################################## + +set global event_scheduler=on; + +# no curent role = error +--error ER_MALFORMED_DEFINER +create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do + insert t1 values (111, 1, 0); + +# definer=current_role, but it has doesn't have enough privileges +set role role1; +create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do + insert t1 values (111, 2, 0); +show create event e1; +set role none; + +# definer=non_existent_role +create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do + insert t1 values (111, 3, 0); +show create event e3; + +# definer=role_name, privileges ok +create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do + insert t1 values (111, 4, 0); +show create event e2; + +let $wait_condition=select count(*) >= 4 from t1; +--source include/wait_condition.inc + +set global event_scheduler=off; + +--sorted_result +select distinct * from t1; +delete from t1 where a=111; + +################################################## +# mysqldump +################################################## + +# note that LOCK TABLES won't work because v3 has invalid definer + +--exec $MYSQL_DUMP --compact --events --routines --skip-lock-tables --databases test mysqltest1 + +################################################## +# cleanup +################################################## + +drop trigger tr1; +drop trigger tr2; +drop trigger tr3; +drop procedure pr1; +drop procedure pr2; +drop procedure pr3; +drop function fn1; +drop function fn2; +drop function fn3; +drop event e1; +drop event e2; +drop event e3; +drop view test.v1, test.v2, test.v3, test.v4, test.v5; +drop table t1, t2; +drop role role1, role2; +drop user foo@localhost; +drop database mysqltest1; +use test; + +################################################## +# reexecution +################################################## + +create user utest; +prepare stmt1 from 'grant select on *.* to utest'; +execute stmt1; +show grants for utest; +drop user utest; +create role utest; +execute stmt1; +show grants for utest; +drop role utest; + diff --git a/mysql-test/t/acl_roles_rename_user.test b/mysql-test/t/acl_roles_rename_user.test index e2808c661ae..37bbfaade2c 100644 --- a/mysql-test/t/acl_roles_rename_user.test +++ b/mysql-test/t/acl_roles_rename_user.test @@ -1,15 +1,11 @@ #create a user with no privileges -create user 'test_user'@'localhost'; -create user 'test_role1'@''; -#manualy create role -update mysql.user set is_role='Y' where user='test_role1'; +create user test_user@localhost; +create role test_role1; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', 'test_user', 'test_role1'); -create user 'test_role2'@''; -#manualy create role -update mysql.user set is_role='Y' where user='test_role2'; +create role test_role2; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'test_role1', 'test_role2'); @@ -26,19 +22,21 @@ select user, host from user where user like 'test%'; --sorted_result select * from roles_mapping; -rename user 'test_role2'@'' to 'test_role2_rm'@''; ---sorted_result -select user, host from user where user like 'test%'; ---sorted_result -select * from roles_mapping; - -#role rename -rename user 'test_role1'@'' to 'test_role1_rm'@''; ---sorted_result -select user, host from user where user like 'test%'; ---sorted_result -select * from roles_mapping; +######### role rename does not work yet +#rename user 'test_role2'@'' to 'test_role2_rm'@''; +#--sorted_result +#select user, host from user where user like 'test%'; +#--sorted_result +#select * from roles_mapping; +# +##role rename +#rename user 'test_role1'@'' to 'test_role1_rm'@''; +#--sorted_result +#select user, host from user where user like 'test%'; +#--sorted_result +#select * from roles_mapping; delete from mysql.roles_mapping; delete from mysql.user where user like 'test%'; flush privileges; + diff --git a/mysql-test/t/acl_roles_rpl_definer.test b/mysql-test/t/acl_roles_rpl_definer.test new file mode 100644 index 00000000000..ba192d1fa2a --- /dev/null +++ b/mysql-test/t/acl_roles_rpl_definer.test @@ -0,0 +1,40 @@ +# +# replication of the DEFINER=current_role +# + +--source include/master-slave.inc + +create role role1; +grant execute on test.* to role1; +grant role1 to current_user; +set role role1; + +create definer=current_user procedure pcu() select current_user; +create definer=root@localhost procedure pu() select "root@localhost"; +create definer=current_role procedure pcr() select current_role; +create definer=role1 procedure pr() select "role1"; + +show create procedure pcu; +show create procedure pu; +show create procedure pcr; +show create procedure pr; + +sync_slave_with_master; +connection slave; +echo [connection slave]; + +show create procedure pcu; +show create procedure pu; +show create procedure pcr; +show create procedure pr; + +connection master; + +drop procedure pcu; +drop procedure pu; +drop procedure pcr; +drop procedure pr; +drop role role1; + +--source include/rpl_end.inc + diff --git a/sql-common/my_user.c b/sql-common/my_user.c index 8d717ea7131..a486f77bc1e 100644 --- a/sql-common/my_user.c +++ b/sql-common/my_user.c @@ -30,34 +30,40 @@ host_name_str [OUT] Buffer to store host name part. Must be not less than HOSTNAME_LENGTH + 1. host_name_len [OUT] A place to store length of the host name part. + + RETURN + 0 - if only a user was set, no '@' was found + 1 - if both user and host were set */ -void parse_user(const char *user_id_str, size_t user_id_len, - char *user_name_str, size_t *user_name_len, - char *host_name_str, size_t *host_name_len) +int parse_user(const char *user_id_str, size_t user_id_len, + char *user_name_str, size_t *user_name_len, + char *host_name_str, size_t *host_name_len) { char *p= strrchr(user_id_str, '@'); if (!p) { - *user_name_len= 0; + *user_name_len= user_id_len; *host_name_len= 0; } else { *user_name_len= (uint) (p - user_id_str); *host_name_len= (uint) (user_id_len - *user_name_len - 1); - - if (*user_name_len > USERNAME_LENGTH) - *user_name_len= USERNAME_LENGTH; - - if (*host_name_len > HOSTNAME_LENGTH) - *host_name_len= HOSTNAME_LENGTH; - - memcpy(user_name_str, user_id_str, *user_name_len); - memcpy(host_name_str, p + 1, *host_name_len); } + if (*user_name_len > USERNAME_LENGTH) + *user_name_len= USERNAME_LENGTH; + + if (*host_name_len > HOSTNAME_LENGTH) + *host_name_len= HOSTNAME_LENGTH; + + memcpy(user_name_str, user_id_str, *user_name_len); + memcpy(host_name_str, p + 1, *host_name_len); + user_name_str[*user_name_len]= 0; host_name_str[*host_name_len]= 0; + + return p != NULL; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 9f33c3d8568..4700cbc4d1c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2853,17 +2853,22 @@ bool Query_log_event::write(IO_CACHE* file) user= thd->get_invoker_user(); host= thd->get_invoker_host(); } - else if (thd->security_ctx->priv_user) + else { Security_context *ctx= thd->security_ctx; - user.length= strlen(ctx->priv_user); - user.str= ctx->priv_user; - if (ctx->priv_host[0] != '\0') + if (thd->need_binlog_invoker() == THD::INVOKER_USER) { + user.str= ctx->priv_user; host.str= ctx->priv_host; - host.length= strlen(ctx->priv_host); + host.length= strlen(host.str); } + else + { + user.str= ctx->priv_role; + host= null_lex_str; // XXX FIXME or empty_lex_str ? + } + user.length= strlen(user.str); } if (user.length > 0) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 7516700e56e..6135f8f1b96 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -5489,9 +5489,8 @@ ER_PS_NO_RECURSION ER_SP_CANT_SET_AUTOCOMMIT eng "Not allowed to set autocommit from a stored function or trigger" ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen" -ER_MALFORMED_DEFINER - eng "Definer is not fully qualified" - ger "Definierer des View ist nicht vollständig spezifiziert" +ER_MALFORMED_DEFINER 0L000 + eng "Invalid definer" ER_VIEW_FRM_NO_USER eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!" ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu" diff --git a/sql/sp.cc b/sql/sp.cc index ca0cd553716..5d91279af2a 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -662,9 +662,14 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, close_system_tables(thd, &open_tables_state_backup); table= 0; - parse_user(definer, strlen(definer), - definer_user_name.str, &definer_user_name.length, - definer_host_name.str, &definer_host_name.length); + if (parse_user(definer, strlen(definer), + definer_user_name.str, &definer_user_name.length, + definer_host_name.str, &definer_host_name.length) && + definer_user_name.length && !definer_host_name.length) + { + // 'user@' -> 'user@%' + definer_host_name= host_not_specified; + } ret= db_load_routine(thd, type, name, sphp, sql_mode, params, returns, body, chistics, @@ -975,7 +980,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) { int ret; TABLE *table; - char definer[USER_HOST_BUFF_SIZE]; + char definer_buf[USER_HOST_BUFF_SIZE]; + LEX_STRING definer; ulonglong saved_mode= thd->variables.sql_mode; MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? MDL_key::FUNCTION : MDL_key::PROCEDURE; @@ -1012,8 +1018,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) restore_record(table, s->default_values); // Get default values for fields /* NOTE: all needed privilege checks have been already done. */ - strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@", - thd->lex->definer->host.str, NullS); + thd->lex->definer->set_lex_string(&definer, definer_buf); if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { @@ -1088,7 +1093,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store_failed= store_failed || table->field[MYSQL_PROC_FIELD_DEFINER]-> - store(definer, (uint)strlen(definer), system_charset_info); + store(definer.str, definer.length, system_charset_info); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 25e64339379..122b6c9b796 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2549,8 +2549,13 @@ sp_head::set_definer(const char *definer, uint definerlen) char host_name_holder[HOSTNAME_LENGTH + 1]; LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH }; - parse_user(definer, definerlen, user_name.str, &user_name.length, - host_name.str, &host_name.length); + if (parse_user(definer, definerlen, user_name.str, &user_name.length, + host_name.str, &host_name.length) && + user_name.length && !host_name.length) + { + // 'user@' -> 'user@%' + host_name= host_not_specified; + } set_definer(&user_name, &host_name); } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 938f92220e1..eea2169f7d1 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -314,27 +314,19 @@ public: #ifndef NO_EMBEDDED_ACCESS_CHECKS static void update_hostname(acl_host_and_ip *host, const char *hostname); static ulong get_sort(uint count,...); -static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, - const char *ip); -static bool show_proxy_grants (THD *thd, - const char *username, const char *hostname, - char *buff, size_t buffsize); -static bool show_role_grants(THD *thd, const char *username, const char *hostname, - ACL_USER_BASE *acl_entry, - char *buff, size_t buffsize); -static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, - bool handle_as_role, - char *buff, size_t buffsize); -static bool show_database_privileges(THD *thd, - const char *username, - const char *hostname, - bool handle_as_role, - char *buff, size_t buffsize); -static bool show_table_and_column_privileges(THD *thd, - const char *username, - const char *hostname, - bool handle_as_role, - char *buff, size_t buffsize); +static bool compare_hostname(const acl_host_and_ip *, const char *, const char *); +static bool show_proxy_grants (THD *, const char *, const char *, + char *, size_t); +static bool show_role_grants(THD *, const char *, const char *, + ACL_USER_BASE *, char *, size_t); +static bool show_global_privileges(THD *, ACL_USER_BASE *, + bool, char *, size_t); +static bool show_database_privileges(THD *, const char *, const char *, + char *, size_t); +static bool show_table_and_column_privileges(THD *, const char *, const char *, + char *, size_t); +static int show_routine_grants(THD *, const char *, const char *, HASH *, + const char *, int, char *, int); class ACL_PROXY_USER :public ACL_ACCESS { @@ -1757,57 +1749,86 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, sctx->db_access= 0; *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0; - /* - Find acl entry in user database. - This is specially tailored to suit the check we do for CALL of - a stored procedure; user is set to what is actually a - priv_user, which can be ''. - */ - for (i=0 ; i < acl_users.elements ; i++) + if (host[0]) // User, not Role { - ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); - if ((!acl_user_tmp->user.str && !user[0]) || - (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0)) + /* + Find acl entry in user database. + This is specially tailored to suit the check we do for CALL of + a stored procedure; user is set to what is actually a + priv_user, which can be ''. + */ + for (i=0 ; i < acl_users.elements ; i++) { - if (compare_hostname(&acl_user_tmp->host, host, ip)) + ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); + if ((!acl_user_tmp->user.str && !user[0]) || + (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0)) { - acl_user= acl_user_tmp; - res= 0; - break; + if (compare_hostname(&acl_user_tmp->host, host, ip)) + { + acl_user= acl_user_tmp; + res= 0; + break; + } } } + + if (acl_user) + { + for (i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); + if (!acl_db->user || + (user && user[0] && !strcmp(user, acl_db->user))) + { + if (compare_hostname(&acl_db->host, host, ip)) + { + if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0))) + { + sctx->db_access= acl_db->access; + break; + } + } + } + } + sctx->master_access= acl_user->access; + + if (acl_user->user.str) + strmake_buf(sctx->priv_user, user); + + if (acl_user->host.hostname) + strmake_buf(sctx->priv_host, acl_user->host.hostname); + } + } + else // Role, not User + { + ACL_ROLE *acl_role= find_acl_role(user); + if (acl_role) + { + res= 0; + for (i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); + if (!acl_db->user || + (user && user[0] && !strcmp(user, acl_db->user))) + { + if (compare_hostname(&acl_db->host, "", "")) + { + if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0))) + { + sctx->db_access= acl_db->access; + break; + } + } + } + } + sctx->master_access= acl_role->access; + + if (acl_role->user.str) + strmake_buf(sctx->priv_user, user); + sctx->priv_host[0]= 0; + } } - if (acl_user) - { - for (i=0 ; i < acl_dbs.elements ; i++) - { - ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); - if (!acl_db->user || - (user && user[0] && !strcmp(user, acl_db->user))) - { - if (compare_hostname(&acl_db->host, host, ip)) - { - if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0))) - { - sctx->db_access= acl_db->access; - break; - } - } - } - } - sctx->master_access= acl_user->access; - - if (acl_user->user.str) - strmake_buf(sctx->priv_user, user); - else - *sctx->priv_user= 0; - - if (acl_user->host.hostname) - strmake_buf(sctx->priv_host, acl_user->host.hostname); - else - *sctx->priv_host= 0; - } mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(res); } @@ -3039,7 +3060,12 @@ bool is_acl_user(const char *host, const char *user) return TRUE; mysql_mutex_lock(&acl_cache->lock); - res= find_user_no_anon(host, user, TRUE) != NULL; + + if (*host) // User + res= find_user_no_anon(host, user, TRUE) != NULL; + else // Role + res= find_acl_role(user) != NULL; + mysql_mutex_unlock(&acl_cache->lock); return res; } @@ -3345,13 +3371,13 @@ static bool test_if_create_new_users(THD *thd) static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, ulong rights, bool revoke_grant, - bool can_create_user, bool no_auto_create, - bool handle_as_role) + bool can_create_user, bool no_auto_create) { int error = -1; bool old_row_exists=0; char what= (revoke_grant) ? 'N' : 'Y'; uchar user_key[MAX_KEY_LENGTH]; + bool handle_as_role= combo.is_role(); LEX *lex= thd->lex; DBUG_ENTER("replace_user_table"); @@ -4832,26 +4858,6 @@ table_error: } -/* - A user name specified without a host can be either a - username@% (where '@%' is added automatically by the parser) - or a role name. Treat it as a role, if such a role exists. -*/ -static ACL_ROLE *find_and_mark_as_role(LEX_USER *user) -{ - if (user->host.str == host_not_specified.str) - { - ACL_ROLE *role= find_acl_role(user->user.str); - if (role) - { - user->host= empty_lex_str; - return role; - } - } - return NULL; -} - - /* Store table level and column level grants in the privilege tables @@ -5018,21 +5024,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { int error; GRANT_TABLE *grant_table; - if (!(Str= get_current_user(thd, tmp_Str))) + if (!(Str= get_current_user(thd, tmp_Str, false))) { result= TRUE; continue; } - - bool handle_as_role= FALSE; - if (find_and_mark_as_role(Str)) - handle_as_role= TRUE; - /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER), handle_as_role); + MODE_NO_AUTO_CREATE_USER)); if (error) { result= TRUE; // Remember error @@ -5227,21 +5228,16 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, { int error; GRANT_NAME *grant_name; - if (!(Str= get_current_user(thd, tmp_Str))) + if (!(Str= get_current_user(thd, tmp_Str, false))) { result= TRUE; continue; } - - bool handle_as_role= FALSE; - if (find_and_mark_as_role(Str)) - handle_as_role=TRUE; - /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER), handle_as_role); + MODE_NO_AUTO_CREATE_USER)); if (error) { result= TRUE; // Remember error @@ -5297,15 +5293,14 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, DBUG_RETURN(result); } -static void append_user(String *str, const char *u, const char *h, - bool handle_as_role) +static void append_user(String *str, const char *u, const char *h) { if (str->length()) str->append(','); str->append('\''); str->append(u); /* hostname part is not relevant for roles, it is always empty */ - if (!handle_as_role) + if (*h) { str->append(STRING_WITH_LEN("'@'")); str->append(h); @@ -5381,13 +5376,13 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* current_role is NONE */ if (!thd->security_ctx->priv_role[0]) { - append_user(&wrong_users, "NONE", "", TRUE); + append_user(&wrong_users, "NONE", ""); result= 1; continue; } if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role))) { - append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE); + append_user(&wrong_users, thd->security_ctx->priv_role, ""); result= 1; continue; } @@ -5395,18 +5390,26 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* can not grant current_role to current_role */ if (granted_role->user.str == current_role.str) { - append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE); + append_user(&wrong_users, thd->security_ctx->priv_role, ""); result= 1; continue; } username= thd->security_ctx->priv_role; - hostname= (char *)""; + hostname= const_cast<char*>(""); + } + else if (user->user.str == current_user.str) + { + role_as_user= NULL; + username= thd->security_ctx->priv_user; + hostname= thd->security_ctx->priv_host; } else { - role_as_user= find_and_mark_as_role(user); + if ((role_as_user= find_acl_role(user->user.str))) + hostname= const_cast<char*>(""); + else + hostname= user->host.str ? user->host.str : host_not_specified.str; username= user->user.str; - hostname= user->host.str; } ROLE_GRANT_PAIR *mapping, *hash_entry; @@ -5421,7 +5424,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* role or user does not exist*/ if (res == -1) { - append_user(&wrong_users, username, hostname, role_as_user != NULL); + append_user(&wrong_users, username, hostname); result= 1; continue; } @@ -5434,7 +5437,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) traverse_role_graph(role, NULL, NULL, NULL, role_explore_detect_cycle, NULL) == 2) { - append_user(&wrong_users, username, hostname, TRUE); + append_user(&wrong_users, username, ""); result= 1; /* need to rollback the mapping added previously */ remove_role_user_mapping(mapping); @@ -5450,7 +5453,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* grant was already removed or never existed */ if (!hash_entry) { - append_user(&wrong_users, username, hostname, role_as_user != NULL); + append_user(&wrong_users, username, hostname); result= 1; continue; } @@ -5458,7 +5461,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) int res= remove_role_user_mapping(mapping); if (res == -1) { - append_user(&wrong_users, username, hostname, role_as_user != NULL); + append_user(&wrong_users, username, hostname); result= 1; continue; } @@ -5467,7 +5470,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* write into the roles_mapping table */ if (replace_roles_mapping_table(tables.table, mapping, revoke)) { - append_user(&wrong_users, username, hostname, TRUE); + append_user(&wrong_users, username, ""); result= 1; if (!revoke) { @@ -5599,10 +5602,17 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, mysql_mutex_lock(&acl_cache->lock); grant_version++; + if (proxied_user) + { + if (!(proxied_user= get_current_user(thd, proxied_user, false))) + DBUG_RETURN(TRUE); + DBUG_ASSERT(proxied_user->host.length); // not a Role + } + int result=0; while ((tmp_Str = str_list++)) { - if (!(Str= get_current_user(thd, tmp_Str))) + if (!(Str= get_current_user(thd, tmp_Str, false))) { result= TRUE; continue; @@ -5615,14 +5625,10 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, if (tmp_Str->user.str == current_user.str && tmp_Str->password.str) Str->password= tmp_Str->password; - bool handle_as_role= FALSE; - if (find_and_mark_as_role(Str)) - handle_as_role= TRUE; - if (replace_user_table(thd, tables[0].table, *Str, (!db ? rights : 0), revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER), handle_as_role)) + MODE_NO_AUTO_CREATE_USER))) result= -1; else if (db) { @@ -6870,14 +6876,6 @@ static uint command_lengths[]= }; -static int show_routine_grants(THD *thd, - const char *username, - const char *hostname, - HASH *hash, - const char *type, int typelen, - bool handle_as_role, - char *buff, int buffsize); - bool print_grants_for_role(THD *thd, ACL_ROLE * role, char *buff, size_t buffsize) { @@ -6887,18 +6885,18 @@ bool print_grants_for_role(THD *thd, ACL_ROLE * role, if (show_global_privileges(thd, role, TRUE, buff, buffsize)) return TRUE; - if (show_database_privileges(thd, role->user.str, "", TRUE, buff, buffsize)) + if (show_database_privileges(thd, role->user.str, "", buff, buffsize)) return TRUE; - if (show_table_and_column_privileges(thd, role->user.str, "", TRUE, buff, buffsize)) + if (show_table_and_column_privileges(thd, role->user.str, "", buff, buffsize)) return TRUE; if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), TRUE, buff, buffsize)) + STRING_WITH_LEN("PROCEDURE"), buff, buffsize)) return TRUE; if (show_routine_grants(thd, role->user.str, "", &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), TRUE, buff, buffsize)) + STRING_WITH_LEN("FUNCTION"), buff, buffsize)) return TRUE; return FALSE; @@ -6951,7 +6949,8 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) } else { - if (find_and_mark_as_role(lex_user)) + lex_user= get_current_user(thd, lex_user, false); + if (lex_user->is_role()) { rolename= lex_user->user.str; } @@ -7010,28 +7009,28 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) }; /* Add database access */ - if (show_database_privileges(thd, username, hostname, FALSE, buff, sizeof(buff))) + if (show_database_privileges(thd, username, hostname, buff, sizeof(buff))) { error= -1; goto end; } /* Add table & column access */ - if (show_table_and_column_privileges(thd, username, hostname, FALSE, buff, sizeof(buff))) + if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff))) { error= -1; goto end; } if (show_routine_grants(thd, username, hostname, &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), FALSE, buff, sizeof(buff))) + STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff))) { error= -1; goto end; } if (show_routine_grants(thd, username, hostname, &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), FALSE, buff, sizeof(buff))) + STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff))) { error= -1; goto end; @@ -7267,7 +7266,6 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, static bool show_database_privileges(THD *thd, const char *username, const char *hostname, - bool handle_as_role, char *buff, size_t buffsize) { ACL_DB *acl_db; @@ -7295,12 +7293,14 @@ static bool show_database_privileges(THD *thd, if (!strcmp(username, user) && !my_strcasecmp(system_charset_info, hostname, host)) { - /* do not print inherited access bits, the role bits present in the - table are what matters */ - if (handle_as_role) - want_access=acl_db->initial_access; - else + /* + do not print inherited access bits for roles, + the role bits present in the table are what matters + */ + if (*hostname) // User want_access=acl_db->access; + else // Role + want_access=acl_db->initial_access; if (want_access) { String db(buff,sizeof(buff),system_charset_info); @@ -7331,7 +7331,7 @@ static bool show_database_privileges(THD *thd, db.append (STRING_WITH_LEN(".* TO '")); db.append(username, strlen(username), system_charset_info); - if (!handle_as_role) + if (*hostname) { db.append (STRING_WITH_LEN("'@'")); // host and lex_user->host are equal except for case @@ -7356,7 +7356,6 @@ static bool show_database_privileges(THD *thd, static bool show_table_and_column_privileges(THD *thd, const char *username, const char *hostname, - bool handle_as_role, char *buff, size_t buffsize) { uint counter, index; @@ -7385,16 +7384,16 @@ static bool show_table_and_column_privileges(THD *thd, { ulong table_access; ulong cols_access; - if (handle_as_role) - { - table_access= grant_table->init_privs; - cols_access= grant_table->init_cols; - } - else + if (*hostname) // User { table_access= grant_table->privs; cols_access= grant_table->cols; } + else // Role + { + table_access= grant_table->init_privs; + cols_access= grant_table->init_cols; + } if ((table_access | cols_access) != 0) { @@ -7435,8 +7434,8 @@ static bool show_table_and_column_privileges(THD *thd, { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) my_hash_element(hash_columns,col_index); - if ((!handle_as_role && (grant_column->rights & j)) || - (handle_as_role && (grant_column->init_rights & j))) + if (j & (*hostname ? grant_column->rights // User + : grant_column->init_rights)) // Role { if (!found_col) { @@ -7475,7 +7474,7 @@ static bool show_table_and_column_privileges(THD *thd, global.append(STRING_WITH_LEN(" TO '")); global.append(username, strlen(username), system_charset_info); - if (!handle_as_role) + if (*hostname) { global.append(STRING_WITH_LEN("'@'")); // host and lex_user->host are equal except for case @@ -7499,9 +7498,7 @@ static bool show_table_and_column_privileges(THD *thd, static int show_routine_grants(THD* thd, const char *username, const char *hostname, - HASH *hash, - const char *type, int typelen, - bool handle_as_role, + HASH *hash, const char *type, int typelen, char *buff, int buffsize) { uint counter, index; @@ -7529,10 +7526,10 @@ static int show_routine_grants(THD* thd, !my_strcasecmp(system_charset_info, hostname, host)) { ulong proc_access; - if (handle_as_role) - proc_access= grant_proc->init_privs; - else + if (*hostname) // User proc_access= grant_proc->privs; + else // Role + proc_access= grant_proc->init_privs; if (proc_access != 0) { @@ -7572,7 +7569,7 @@ static int show_routine_grants(THD* thd, global.append(STRING_WITH_LEN(" TO '")); global.append(username, strlen(username), system_charset_info); - if (!handle_as_role) + if (*hostname) { global.append(STRING_WITH_LEN("'@'")); // host and lex_user->host are equal except for case @@ -8232,9 +8229,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, table->key_info->key_part[1].store_length); key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, - user_key, (key_part_map)3, - HA_READ_KEY_EXACT))) + error= table->file->ha_index_read_idx_map(table->record[0], 0, + user_key, (key_part_map)3, + HA_READ_KEY_EXACT); + if (!error && !*host_str) + { // verify that we got a role or a user, as needed + if (check_is_role(table) != user_from->is_role()) + error= HA_ERR_KEY_NOT_FOUND; + } + if (error) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { @@ -8371,7 +8374,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, DBUG_RETURN(0); /* same. no roles in PROXY_USERS_ACL */ - if (struct_no == PROXY_USERS_ACL && !user_from->host.length) + if (struct_no == PROXY_USERS_ACL && user_from->is_role()) DBUG_RETURN(0); if (struct_no == ROLE_ACL) //no need to scan the structures in this case @@ -8678,8 +8681,12 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, { int result= 0; int found; + bool handle_as_role= user_from->is_role(); DBUG_ENTER("handle_grant_data"); + if (user_to) + DBUG_ASSERT(handle_as_role == user_to->is_role()); + /* Handle user table. */ if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0) { @@ -8688,20 +8695,32 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, } else { - /* Handle user array. */ - if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found) + if (handle_as_role) { - result= 1; /* At least one record/element found. */ - /* If search is requested, we do not need to search further. */ - if (! drop && ! user_to) - goto end; + if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found) + { + result= 1; /* At least one record/element found. */ + /* If search is requested, we do not need to search further. */ + if (! drop && ! user_to) + goto end; + } + else + if (find_user_no_anon(user_from->host.str, user_from->user.str, TRUE)) + goto end; // looking for a role, found a user } - if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found) + else { - result= 1; /* At least one record/element found. */ - /* If search is requested, we do not need to search further. */ - if (! drop && ! user_to) - goto end; + /* Handle user array. */ + if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found) + { + result= 1; /* At least one record/element found. */ + /* If search is requested, we do not need to search further. */ + if (! drop && ! user_to) + goto end; + } + else + if (find_acl_role(user_from->user.str)) + goto end; // looking for a user, found a role } } @@ -8821,14 +8840,14 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, DBUG_RETURN(result); } -static void append_user(String *str, LEX_USER *user, bool handle_as_role) +static void append_user(String *str, LEX_USER *user) { if (str->length()) str->append(','); str->append('\''); str->append(user->user.str); /* hostname part is not relevant for roles, it is always empty */ - if (!handle_as_role) + if (!user->is_role()) { str->append(STRING_WITH_LEN("'@'")); str->append(user->host.str); @@ -8854,7 +8873,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) { int result; String wrong_users; - LEX_USER *user_name, *tmp_user_name; + LEX_USER *user_name; List_iterator <LEX_USER> user_list(list); TABLE_LIST tables[GRANT_TABLES]; bool some_users_created= FALSE; @@ -8868,30 +8887,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) mysql_rwlock_wrlock(&LOCK_grant); mysql_mutex_lock(&acl_cache->lock); - while ((tmp_user_name= user_list++)) + while ((user_name= user_list++)) { - if (handle_as_role) - { - user_name= tmp_user_name; - user_name->host.str= (char *)""; - user_name->host.length= 0; - /* role already exists */ - if (find_acl_role(user_name->user.str)) - { - append_user(&wrong_users, user_name, TRUE); - result = TRUE; - continue; - } - } - else - { - if (!(user_name= get_current_user(thd, tmp_user_name))) - { - result= TRUE; - continue; - } - } - if (!user_name->host.str) user_name->host= host_not_specified; @@ -8901,17 +8898,16 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) */ if (handle_grant_data(tables, 0, user_name, NULL)) { - append_user(&wrong_users, user_name, handle_as_role); + append_user(&wrong_users, user_name); result= TRUE; continue; } some_users_created= TRUE; - if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0, - handle_as_role)) + if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0)) { - append_user(&wrong_users, user_name, handle_as_role); + append_user(&wrong_users, user_name); result= TRUE; } } @@ -8966,31 +8962,17 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) while ((tmp_user_name= user_list++)) { - if (handle_as_role) + user_name= get_current_user(thd, tmp_user_name, false); + if (!user_name || handle_as_role != user_name->is_role()) { - - user_name= tmp_user_name; - user_name->host.str= (char *)""; - user_name->host.length= 0; - if (!find_acl_role(user_name->user.str)) - { - append_user(&wrong_users, user_name, TRUE); - result= TRUE; - continue; - } - } - else - { - if (!(user_name= get_current_user(thd, tmp_user_name))) - { - result= TRUE; - continue; - } + append_user(&wrong_users, tmp_user_name); + result= TRUE; + continue; } if (handle_grant_data(tables, 1, user_name, NULL) <= 0) { - append_user(&wrong_users, user_name, handle_as_role); + append_user(&wrong_users, user_name); result= TRUE; continue; } @@ -9058,18 +9040,21 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) while ((tmp_user_from= user_list++)) { - if (!(user_from= get_current_user(thd, tmp_user_from))) - { - result= TRUE; - continue; - } tmp_user_to= user_list++; - if (!(user_to= get_current_user(thd, tmp_user_to))) + if (!(user_from= get_current_user(thd, tmp_user_from, false)) || + user_from->is_role()) { + append_user(&wrong_users, user_from); + result= TRUE; + continue; + } + if (!(user_to= get_current_user(thd, tmp_user_to, false)) || + user_to->is_role()) + { + append_user(&wrong_users, user_to); result= TRUE; continue; } - DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */ /* Search all in-memory structures and grant tables @@ -9079,7 +9064,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) handle_grant_data(tables, 0, user_from, user_to) <= 0) { /* NOTE TODO renaming roles is not yet implemented */ - append_user(&wrong_users, user_from, FALSE); + append_user(&wrong_users, user_from); result= TRUE; continue; } @@ -9139,7 +9124,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) List_iterator <LEX_USER> user_list(list); while ((tmp_lex_user= user_list++)) { - if (!(lex_user= get_current_user(thd, tmp_lex_user))) + if (!(lex_user= get_current_user(thd, tmp_lex_user, false))) { result= -1; continue; @@ -9150,8 +9135,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) continue; } - if (replace_user_table(thd, tables[0].table, - *lex_user, ~(ulong)0, 1, 0, 0, 0)) + if (replace_user_table(thd, tables[0].table, *lex_user, ~(ulong)0, 1, 0, 0)) { result= -1; continue; @@ -10257,6 +10241,47 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, #endif +/** + Retuns information about user or current user. + + @param[in] thd thread handler + @param[in] user user + + @return + - On success, return a valid pointer to initialized + LEX_USER, which contains user information. + - On error, return 0. +*/ + +LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock) +{ + if (user->user.str == current_user.str) // current_user + return create_default_definer(thd, false); + + if (user->user.str == current_role.str) // current_role + return create_default_definer(thd, true); + + if (user->host.str == NULL) // Possibly a role + { + // to be reexecution friendly we have to make a copy + LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user)); + if (!dup) + return 0; + + if (lock) + mysql_mutex_lock(&acl_cache->lock); + if (find_acl_role(dup->user.str)) + dup->host= empty_lex_str; + else + dup->host= host_not_specified; + if (lock) + mysql_mutex_unlock(&acl_cache->lock); + return dup; + } + + return user; +} + struct ACL_internal_schema_registry_entry { const LEX_STRING *m_name; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index cec46a0a3a7..0f379ba7660 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -938,7 +938,7 @@ THD::THD() thr_lock_info_init(&lock_info); /* safety: will be reset after start */ m_internal_handler= NULL; - m_binlog_invoker= FALSE; + m_binlog_invoker= INVOKER_NONE; arena_for_cached_items= 0; memset(&invoker_user, 0, sizeof(invoker_user)); memset(&invoker_host, 0, sizeof(invoker_host)); @@ -1890,7 +1890,7 @@ void THD::cleanup_after_query() where= THD::DEFAULT_WHERE; /* reset table map for multi-table update */ table_map_for_update= 0; - m_binlog_invoker= FALSE; + m_binlog_invoker= INVOKER_NONE; #ifndef EMBEDDED_LIBRARY if (rli_slave) @@ -4341,9 +4341,9 @@ void THD::leave_locked_tables_mode() locked_tables_mode= LTM_NONE; } -void THD::get_definer(LEX_USER *definer) +void THD::get_definer(LEX_USER *definer, bool role) { - binlog_invoker(); + binlog_invoker(role); #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) if (slave_thread && has_invoker()) { @@ -4355,7 +4355,7 @@ void THD::get_definer(LEX_USER *definer) } else #endif - get_default_definer(this, definer); + get_default_definer(this, definer, role); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 64de6a63ded..13a3adf5909 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3153,9 +3153,11 @@ public: } void leave_locked_tables_mode(); int decide_logging_format(TABLE_LIST *tables); - void binlog_invoker() { m_binlog_invoker= TRUE; } - bool need_binlog_invoker() { return m_binlog_invoker; } - void get_definer(LEX_USER *definer); + + enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE}; + void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; } + enum need_invoker need_binlog_invoker() { return m_binlog_invoker; } + void get_definer(LEX_USER *definer, bool role); void set_invoker(const LEX_STRING *user, const LEX_STRING *host) { invoker_user= *user; @@ -3230,14 +3232,15 @@ private: Diagnostics_area main_da; /** - It will be set TURE if CURRENT_USER() is called in account management - statements or default definer is set in CREATE/ALTER SP, SF, Event, - TRIGGER or VIEW statements. + It will be set if CURRENT_USER() or CURRENT_ROLE() is called in account + management statements or default definer is set in CREATE/ALTER SP, SF, + Event, TRIGGER or VIEW statements. - Current user will be binlogged into Query_log_event if m_binlog_invoker - is TRUE; It will be stored into invoker_host and invoker_user by SQL thread. + Current user or role will be binlogged into Query_log_event if + m_binlog_invoker is not NONE; It will be stored into invoker_host and + invoker_user by SQL thread. */ - bool m_binlog_invoker; + enum need_invoker m_binlog_invoker; /** It points to the invoker in the Query_log_event. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3ac90ea2b7c..27162b1cac4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1840,7 +1840,7 @@ bool sp_process_definer(THD *thd) Query_arena original_arena; Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena); - lex->definer= create_default_definer(thd); + lex->definer= create_default_definer(thd, false); if (ps_arena) thd->restore_active_arena(ps_arena, &original_arena); @@ -1854,20 +1854,24 @@ bool sp_process_definer(THD *thd) } else { + LEX_USER *d= lex->definer= get_current_user(thd, lex->definer); + if (!d) + DBUG_RETURN(TRUE); + /* - If the specified definer differs from the current user, we + If the specified definer differs from the current user or role, we should check that the current user has SUPER privilege (in order to create a stored routine under another user one must have SUPER privilege). */ - if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, lex->definer->host.str, - thd->security_ctx->priv_host)) && - check_global_access(thd, SUPER_ACL, true)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user); + bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role); + bool curuserhost= curuser && d->host.str && + !my_strcasecmp(system_charset_info, d->host.str, + thd->security_ctx->priv_host); + if (!curuserhost && !currole && + check_global_access(thd, SUPER_ACL, false)) DBUG_RETURN(TRUE); - } } /* Check that the specified definer exists. Emit a warning if not. */ @@ -3783,7 +3787,7 @@ end_with_restore_list: goto error; /* Replicate current user as grantor */ - thd->binlog_invoker(); + thd->binlog_invoker(false); if (thd->security_ctx->user) // If not replication { @@ -7686,16 +7690,23 @@ Item *negate_expression(THD *thd, Item *expr) @param[out] definer definer */ -void get_default_definer(THD *thd, LEX_USER *definer) +void get_default_definer(THD *thd, LEX_USER *definer, bool role) { const Security_context *sctx= thd->security_ctx; - definer->user.str= (char *) sctx->priv_user; + if (role) + { + definer->user.str= const_cast<char*>(sctx->priv_role); + definer->host= empty_lex_str; + } + else + { + definer->user.str= const_cast<char*>(sctx->priv_user); + definer->host.str= const_cast<char*>(sctx->priv_host); + definer->host.length= strlen(definer->host.str); + } definer->user.length= strlen(definer->user.str); - definer->host.str= (char *) sctx->priv_host; - definer->host.length= strlen(definer->host.str); - definer->password= null_lex_str; definer->plugin= empty_lex_str; definer->auth= empty_lex_str; @@ -7713,16 +7724,22 @@ void get_default_definer(THD *thd, LEX_USER *definer) - On error, return 0. */ -LEX_USER *create_default_definer(THD *thd) +LEX_USER *create_default_definer(THD *thd, bool role) { LEX_USER *definer; if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) return 0; - thd->get_definer(definer); + thd->get_definer(definer, role); - return definer; + if (role && definer->user.length == 0) + { + my_error(ER_MALFORMED_DEFINER, MYF(0)); + return 0; + } + else + return definer; } @@ -7757,27 +7774,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) } -/** - Retuns information about user or current user. - - @param[in] thd thread handler - @param[in] user user - - @return - - On success, return a valid pointer to initialized - LEX_USER, which contains user information. - - On error, return 0. -*/ - -LEX_USER *get_current_user(THD *thd, LEX_USER *user) -{ - if (user->user.str == current_user.str) // current_user - return create_default_definer(thd); - - return user; -} - - /** Check that byte length of a string does not exceed some limit. diff --git a/sql/sql_parse.h b/sql/sql_parse.h index c87e290119e..16b5280a7c7 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -63,10 +63,10 @@ Comp_creator *comp_ne_creator(bool invert); int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, enum enum_schema_tables schema_table_idx); -void get_default_definer(THD *thd, LEX_USER *definer); -LEX_USER *create_default_definer(THD *thd); +void get_default_definer(THD *thd, LEX_USER *definer, bool role); +LEX_USER *create_default_definer(THD *thd, bool role); LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); -LEX_USER *get_current_user(THD *thd, LEX_USER *user); +LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true); bool sp_process_definer(THD *thd); bool check_string_byte_length(LEX_STRING *str, const char *err_msg, uint max_byte_length); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5fc5afcc0fd..b95d26bd51c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2064,8 +2064,11 @@ void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user, { buffer->append(STRING_WITH_LEN("DEFINER=")); append_identifier(thd, buffer, definer_user->str, definer_user->length); - buffer->append('@'); - append_identifier(thd, buffer, definer_host->str, definer_host->length); + if (definer_host->str[0]) + { + buffer->append('@'); + append_identifier(thd, buffer, definer_host->str, definer_host->length); + } buffer->append(' '); } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e0898740047..c7d47487c6a 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -763,9 +763,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, definer_user= lex->definer->user; definer_host= lex->definer->host; - trg_definer->str= trg_definer_holder; - trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@", - definer_host.str, NullS) - trg_definer->str; + lex->definer->set_lex_string(trg_definer, trg_definer_holder); } else { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 65151d503a4..1a33cd97a48 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -822,7 +822,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, goto err; } - view->file_version= 1; + view->file_version= 2; view->calc_md5(md5); if (!(view->md5.str= (char*) thd->memdup(md5, 32))) { @@ -1113,8 +1113,16 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - get_default_definer(thd, &table->definer); + get_default_definer(thd, &table->definer, false); } + + /* + since 10.0.5 definer.host can never be "" for a User, but it's + always "" for a Role. Before 10.0.5 it could be "" for a User, + but roles didn't exist. file_version helps. + */ + if (!table->definer.host.str[0] && table->file_version < 2) + table->definer.host= host_not_specified; // User, not Role /* Initialize view definition context by character set names loaded from diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index dd874caf4b0..332333da620 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13173,7 +13173,7 @@ user: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; $$->user = $1; - $$->host= host_not_specified; + $$->host= null_lex_str; // User or Role, see get_current_user() $$->password= null_lex_str; $$->plugin= empty_lex_str; $$->auth= empty_lex_str; @@ -14351,7 +14351,7 @@ grant_role: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; $$->user = $1; - $$->host= host_not_specified; + $$->host= empty_lex_str; $$->password= null_lex_str; $$->plugin= empty_lex_str; $$->auth= empty_lex_str; @@ -15044,9 +15044,9 @@ no_definer: ; definer: - DEFINER_SYM EQ user + DEFINER_SYM EQ user_or_role { - thd->lex->definer= get_current_user(thd, $3); + thd->lex->definer= $3; } ; diff --git a/sql/structs.h b/sql/structs.h index a3a54c524e6..f4b85433e3a 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -190,6 +190,14 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *); typedef struct st_lex_user { LEX_STRING user, host, password, plugin, auth; + bool is_role() { return user.str[0] && !host.str[0]; } + void set_lex_string(LEX_STRING *l, char *buf) + { + if (is_role()) + *l= user; + else + l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf; + } } LEX_USER; /*