# create view
# create trigger
# create procedure
# create event
# mysqldump dumping the definer

--source include/not_embedded.inc
--source include/default_charset.inc

call mtr.add_suppression('Event Scheduler:');

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 select on test.* 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;
grant select on test.* to role2;

# create a non-priv user and a priv role granted to him
create user foo@localhost;
grant create view on mysqltest1.* to foo@localhost;
grant select, create view on test.* to foo@localhost;
create role role4;
grant select on mysqltest1.t1 to role4;
grant role4 to foo@localhost;
grant select on test.* to role4;

##################################################
# views
##################################################

# no current role = error
--error ER_INVALID_ROLE
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,current_role() 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_MALFORMED_DEFINER
select * from test.v5;

create user role4;
grant select on mysqltest1.t1 to role4;
show create view test.v5;
--error ER_MALFORMED_DEFINER
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

flush tables;
show create view test.v5;
select * from test.v5;
drop user role4;


##################################################
# trigger
##################################################

create table t2 select * from t1;

# no current role = error
--error ER_INVALID_ROLE
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);
--replace_column 7 #
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);
--replace_column 7 #
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);
--replace_column 7 #
show create trigger tr3;
--error ER_MALFORMED_DEFINER
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

--replace_column 7 #
show create trigger tr2;
--error ER_MALFORMED_DEFINER
delete from t2 where a=2;
select * from t1;
select * from t2;

##################################################
# stored procedures
##################################################

# no current role = error
--error ER_INVALID_ROLE
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_MALFORMED_DEFINER
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_MALFORMED_DEFINER
call pr2();

##################################################
# stored functions
##################################################

# no current role = error
--error ER_INVALID_ROLE
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_MALFORMED_DEFINER
select fn3();

##################################################
# events
##################################################

set global event_scheduler=on;

# no current role = error
--error ER_INVALID_ROLE
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 --no-autocommit=0 --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;
--disable_warnings
drop database mysqltest1;
--enable_warnings
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;

--echo #
--echo # MDEV-13676: Field "create Procedure" is NULL, even if the the user
--echo # has role which is the definer. (SHOW CREATE PROCEDURE)
--echo #

create database rtest;
create role r1;
create role r2;
create role r3;
grant all privileges  on rtest.*  to r1;

create user user1;
grant r1 to user1;
grant r1 to r2;
grant r2 to user1;
grant r3 to user1;

connect (user1, localhost,user1,,"*NO-ONE*",,,);
set role r2;
use rtest;

DELIMITER //;
CREATE DEFINER=current_role() PROCEDURE user1_proc() SQL SECURITY INVOKER
    BEGIN
        SELECT NOW(), VERSION();
    END;//
DELIMITER ;//

set role r2;
show create procedure user1_proc;

--echo #
--echo # Currently one can not use as definer any role except CURRENT_ROLE
--echo #
DELIMITER //;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
    BEGIN
        SELECT NOW(), VERSION();
    END;//
DELIMITER ;//

set role r1;
DELIMITER //;
CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
    BEGIN
        SELECT NOW(), VERSION();
    END;//
DELIMITER ;//

show create procedure user1_proc2;
--echo #
--echo # Test to see if the user can still see the procedure code if the
--echo # role that owns it is granted to him indirectly.
--echo #
set role r2;
show create procedure user1_proc2;

--echo #
--echo # One should not be able to see the procedure code if the role that owns
--echo # the procedure is not set by the user or is not in the subgraph of the
--echo # currently active role.
--echo #
set role r3;
--error ER_SP_DOES_NOT_EXIST
show create procedure user1_proc2;

connection default;

use rtest;

--echo #
--echo # Try a few edge cases, with usernames identical to role name;
--echo #

create user user_like_role;
create user foo;
create role user_like_role;
grant select on rtest.* to user_like_role;
grant select on rtest.* to foo;
grant select on rtest.* to user_like_role@'%';

grant user_like_role to foo;

--echo #
--echo # Here we have a procedure that is owned by user_like_role USER
--echo # We don't want user_like_role ROLE to have access to its code.
--echo #
DELIMITER //;
CREATE DEFINER=`user_like_role`@`%` PROCEDURE sensitive_proc() SQL SECURITY INVOKER
    BEGIN
        SELECT NOW(), VERSION();
    END;//
DELIMITER ;//

connect (user_like_role, localhost, user_like_role,,"*NO-ONE*",,,);
use rtest;
show create procedure sensitive_proc;

connect (foo, localhost, foo,,"*NO-ONE*",,,);
set role user_like_role;
use rtest;

--echo #
--echo # Foo has the set rolename identical to the procedure's definer's username.
--echo # Foo should not have access to this procedure.
--echo #
--error ER_SP_DOES_NOT_EXIST
show create procedure sensitive_proc;

connection default;
drop role r1;
drop role r2;
drop role r3;
drop role user_like_role;
disconnect foo;
disconnect user1;
disconnect user_like_role;
drop user user1;
drop user foo;
drop user user_like_role;
drop procedure user1_proc;
drop procedure user1_proc2;
drop procedure sensitive_proc;
drop database rtest;
