Skip to Main Content
  • Questions
  • Invoker rights and Before Insert Trigger

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Dusan.

Asked: September 29, 2001 - 12:02 pm UTC

Last updated: February 06, 2013 - 2:08 pm UTC

Version: 8.1.6

Viewed 10K+ times! This question is

You Asked

Hi Tom,

I found out that if I call procedure with authid current_user hint from Before Insert Trigger for each row, the invoker rights do not work there. Please, have a look at following:

SQL> select user from dual
2 ;

USER
------------------------------
DV

SQL>
SQL> PROMPT Creating Table 'DV_T2'
Creating Table 'DV_T2'
SQL> CREATE TABLE DV_T2
2 (RGPT_ID NUMBER(10) NOT NULL
3 ,SEQ_ID NUMBER(10) NOT NULL
4 )
5 /

Table created.

SQL> grant select,insert,update,delete on DV_T2 to v30;

Grant succeeded.

SQL> PROMPT Creating Primary Key on 'DV_T2'
Creating Primary Key on 'DV_T2'
SQL> ALTER TABLE DV_T2
2 ADD (CONSTRAINT DVT2_PK PRIMARY KEY
3 (RGPT_ID
4 ,SEQ_ID))
5 /

Table altered.

SQL>
SQL>
SQL> PROMPT Creating Sequence 'DDVT2_SEQ'
Creating Sequence 'DDVT2_SEQ'
SQL> CREATE SEQUENCE DDVT2_SEQ
2 NOMAXVALUE
3 NOMINVALUE
4 NOCYCLE
5 NOCACHE
6 /

Sequence created.

SQL>
SQL> PROMPT Creating Sequence 'DVT2_SEQ'
Creating Sequence 'DVT2_SEQ'
SQL> CREATE SEQUENCE DVT2_SEQ
2 NOMAXVALUE
3 NOMINVALUE
4 NOCYCLE
5 NOCACHE
6 /

Sequence created.

SQL> create synonym v30.dvt2_seq for ddvt2_seq;

Synonym created.


SQL> grant select on ddvt2_seq to v30
2 /

Grant succeeded.

SQL>
SQL>
SQL> create or replace function f_return_sequence
2 ( p_table_name in varchar2)
3 return number
4 authid current_user
5 as
6 v_return number(10);
7 begin
8 dbms_output.put_line('User in proc='||user);
9 if p_table_name='DV_T2' then
10 select dvt2_seq.nextval into v_return from dual;
11 end if;
12 return v_return;
13 end;
14 /

Function created.

SQL> grant execute on f_return_sequence to v30
2 /

Grant succeeded.

SQL> create synonym v30.f_return_sequence for f_return_sequence
2 /

Synonym created.

SQL> CREATE OR REPLACE TRIGGER BIT_DVT2
2 BEFORE INSERT
3 ON DV_T2
4 FOR EACH ROW
5 -- PL/SQL Block
6 begin
7 :new.seq_id :=f_return_sequence('DV_T2');
8 end;
9 /

Trigger created.

SQL> insert into DV_T2(rgpt_id) values(1);

1 row created.

SQL> insert into DV_T2(rgpt_id) values(1);

1 row created.

SQL> insert into DV_T2(rgpt_id) values(1);

1 row created.

SQL> connect v30/v30
Connected.

SQL> set serveroutput on
SQL> insert into DV.DV_T2(rgpt_id) values(1);
User in proc=V30

1 row created.
SQL> select * from DV2.DV_T2;

RGPT_ID SEQ_ID
--------- ---------
1 1
1 2
1 3
1 4

This surprised me. From BIR trigger I called function with invoker rights, my synonym dvt2_seq was for dv.ddvt2_seq, as is demonstrated below:


SQL> select dvt2_seq.nextval from dual;

NEXTVAL
---------
1

SQL> begin
2 dbms_output.put_line(f_return_sequence('DV_T2'));
3 end;
4 /
User in proc=V30
2

PL/SQL procedure successfully completed.

SQL>

Why trigger did not respect function with invoker rights? How can we outsmart trigger?

Thanks,

Dusan



and Tom said...

It is working exactly as it should.

An invokers rights routine takes on the identity of the invoker. In this case, the invoker is the trigger and triggers ALWAYS run in "definer rights" mode.

It would be exactly the same as if you called that procedure from a definer rights routine. As soon as you enter the definer rights routine the invoker IS the definer of that procedure -- not the user who is logged in.

You cannot "outsmart" the trigger. The trigger quite simply runs as the OWNER of the trigger as it must. Anything it calls will consider that OWNER the invoker.

Btw: the way to see "whats in effect schema wise" would be:

4 for x in
5 ( select sys_context( 'userenv', 'current_user' ) current_user,
6 sys_context( 'userenv', 'session_user' ) session_user,
7 sys_context( 'userenv', 'current_schema' ) current_schema
8 from dual )
9 loop
10 dbms_output.put_line( 'Current User: ' || x.current_user );
11 dbms_output.put_line( 'Session User: ' || x.session_user );
12 dbms_output.put_line( 'Current Schema: ' || x.current_schema );
13 end loop;

USER is constant in a session -- it'll never change. The above will show you

o CURRENT_USER: The name of the user whose privilege the session is currently executing under.
o SESSION_USER: The name of the user who originally created this session ? who is logged in. This is constant for a session.
o CURRENT_SCHEMA: The name of the default schema that will be used to resolve references to unqualified objects.
which is more relevant then user.



Rating

  (17 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

More clear now

Dusan, September 30, 2001 - 12:08 pm UTC

At least, it diverted me from wrong direction.

Reader

Reader, September 30, 2001 - 11:30 pm UTC

Tom,

Can you tell us what is the rational in
PL/SQL Procedures having Definer's rights as default and
Java Stored Procedures ( extrenal routines written in Java)
having Invoker's rights as the default

Thsnks

Tom Kyte
October 01, 2001 - 6:30 am UTC

When you load java into the database, the java itself is compiled with invokers rights. However -- since all java is called via a call spec written in SQL and this call spec will compile by default with DEFINER rights -- most people don't even notice this nuance (it appears to be definer rights).

The reasons why this is so -- there are a couple:

1) you typically use java in the database as a "utility" as a way to extend plsql to let it do what plsql cannot. By loading with IR (invoker rights) -- this is facilitated. Instead of loading the java per schema to get the rights privs -- we load it once and when you use it -- it'll take on "your" identity (you being another stored procedure). So, if both user A and user B create a call spec to the java AND call the java from their procedures, it will be as if they put the java into their schema.

2) the sql in java is typically in jdbc and is invisible to the compiler. Unlike plsql where the sql is right there and visible typically. When you compile java, we do not "rip the sql" out of it like with do with plsql. With plsql there are great efficiencies to be gained by using definer rights and doing the security checks and such right then and there at compile time. DR (definer rights) are very efficient with plsql -- they would add nothing to java.

So, in short -- since the call spec is a DR routine -- the fact that the java was loaded with IR isn't typically relevant at all (or even noticed).

But why triggers "Always" have definers rights...

Subhrajyoti Paul, September 13, 2002 - 1:45 am UTC

Tom, what is the philosphy behind triggers always having definers rights...?

Tom Kyte
September 14, 2002 - 1:35 pm UTC

Because they are considered part of the table itself.

Tell me -- can you give a logical, real world, practical case where it should run with invokers rights?

Thanks, Tom, excellent explanation, but....

Olga, May 13, 2003 - 5:01 pm UTC

... My goal is to track WHO updated certain column of some table and to store username in rec_upd_username column of the said table, for each row. I tried various solutions, including the one described in this topic, but came to the same conclusion that you just described.

I believe, there must be the way to do this in ORACLE, would you be so kind to give me a hint? :)

I run Oracle 8.1.7 database.
Thank you!

Tom Kyte
May 13, 2003 - 5:18 pm UTC

WHO = user?

create trigger before update on t for each row
begin
:new.updated_by := user;
end;



Oracle Triggers invoking a java class or servlet or component

Ram, January 14, 2004 - 9:08 am UTC

I am new to oracle. And want to know can Oracle triggers invoke a java class( can be a servler, class or component). If so can you help me in this. It is urgent

Tom Kyte
January 14, 2004 - 3:55 pm UTC

sure, the trigger can call any java STORED PROCEDURE, which could call a jsp or anything

would it be "slow"
would it be "wrong"
would it be "not a good idea"
would it be "totally non-transactional" (hence having the ability to destroy data integrity)

yes to all of the above.

Oracle Triggers invoking a java class or servlet or component

Ram, January 14, 2004 - 4:38 pm UTC

yes if it is a bad idea . It is Ok. My idea is to implement connection pooling between two machines. THe client is independent from which machine the response is coming. But in my case the session is dying. So I want to create a class and call that class in trigger , so that session can be maintained whatever the server may be. Is there any better idea to implement this rather than thinking it is bad idea.
I searched in google , found like we can use loadjava .....
. It is giving tablename space is not sufficient.
Is there any alternative.



Tom Kyte
January 14, 2004 - 5:56 pm UTC

er?

no idea where you are going with this? we sort of have connection pooling in the db already. guess I'd need "an example"

Triggers

Natasha, January 25, 2004 - 9:40 am UTC

Toms,

To trap the created date & the name of the user who logged in...Which practice is good. Please advice.

Table Available:

Create Table Xyz
( eno pls_integer,
ename Varchar2(20),
created_date date,
created_user varchar2(50),
modified_date date,
modified_user varchar2(50)
)


Ex #1:

To create a before insert trigger and we can track the created date & created user.

Ex #2:

Assinging a default values to Created_user & Created_Dates at the table level.

Please suggest for the developers.

Thanks in advance.

Natasha





Tom Kyte
January 25, 2004 - 10:12 am UTC

I would use defaults whenever possible (more efficient than a trigger) and a trigger only when I could not use something declaritive.

and when granting on this table, do not grant the ability to modify these columns as demonstrated (else someone can override the defaults if they wanted to spoof you)

ops$tkyte@ORA9IR2> create table t
  2  ( id int primary key,
  3    data varchar2(20),
  4    create_date date default sysdate,
  5    creator     varchar2(30) default user,
  6    update_date date,
  7    updated_by  varchar2(30)
  8  )
  9  /
 
Table created.
 
ops$tkyte@ORA9IR2> create or replace trigger t_trigger
  2  before update on t for each row
  3  begin
  4          :new.update_date := sysdate;
  5          :new.updated_by := user;
  6  end;
  7  /
 
Trigger created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> grant select,  update (id,data), insert(id,data) on t to scott;
 
Grant succeeded.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> @connect scott/tiger
ops$tkyte@ORA9IR2> set termout off
scott@ORA9IR2> REM GET afiedt.buf NOLIST
scott@ORA9IR2> set termout on
scott@ORA9IR2> insert into ops$tkyte.t ( id, data ) values ( 1, 'hello world' );
 
1 row created.
 
scott@ORA9IR2> insert into ops$tkyte.t ( id, data, create_date ) values ( 1, 'hello world', sysdate-1 );
insert into ops$tkyte.t ( id, data, create_date ) values ( 1, 'hello world', sysdate-1 )
                      *
ERROR at line 1:
ORA-01031: insufficient privileges
 
 
scott@ORA9IR2> update ops$tkyte.t set updated_by = 'OPS$TKYTE';
update ops$tkyte.t set updated_by = 'OPS$TKYTE'
                 *
ERROR at line 1:
ORA-01031: insufficient privileges
 
 
scott@ORA9IR2> update ops$tkyte.t set data = 'goodbye';
 
1 row updated.
 
scott@ORA9IR2> select * from ops$tkyte.t;
 
        ID DATA                 CREATE_DA CREATOR                        UPDATE_DA UPDATED_BY
---------- -------------------- --------- ------------------------------ --------- ------------------------------
         1 goodbye              25-JAN-04 SCOTT                          25-JAN-04 SCOTT
 
 

definer's right

Mike, September 11, 2004 - 11:25 pm UTC

we have a trigger to prevent schema other than the two schemes (admuser and privuser) to DML on table admuser.taskrsrc. I wanted to test the definer's right.
As schema usace, i created a function to update admuser.taskrsrc:
create or replace function update_taskrsrc

(p_owner in varchar2,p_newcost in number, p_id in number, p_rowid out varchar2)

return number

is

begin

execute immediate

'update '||p_owner||'.taskrsrc

set target_cost =:bv1

where taskrsrc_id = :pk

returning rowid into :out'

using p_newcost, p_id

returning into p_rowid;

return sql%rowcount;

end;

/

as USACE, use the function to update record of ADMUSER.TASKRSRC, and failed on the trigger:



USACE@S0P2PVD5> set serveroutput on

USACE@S0P2PVD5> declare

2 l_rowid varchar(50);

3 l_rows number;

4 begin

5 l_rows := usace.update_taskrsrc( 'ADMUSER', 1,

6 6639, l_rowid );

7

8 dbms_output.put_line( 'Updated ' || l_rows || ' rows' );

9 dbms_output.put_line( 'its rowid was ' || l_rowid );

10 end;

11 /

declare

*

ERROR at line 1:

ORA-06550: line 1, column 22:

PLS-00201: identifier 'FND_GLOBAL.CONC_REQUEST_ID' must be declared

ORA-06550: line 1, column 11:

PL/SQL: Statement ignored

ORA-06512: at "SYS.DBMS_SYS_SQL", line 1118

ORA-06512: at "SYS.DBMS_SQL", line 316

ORA-06512: at "ADMUSER.OP3_UTIL", line 532

ORA-06512: at "ADMUSER.OP3_UTIL", line 560

ORA-06512: at "ADMUSER.OP3_UTIL", line 585

ORA-06512: at "ADMUSER.OP3_ACTUALS2", line 268

ORA-06512: at "ADMUSER.OP3_ACTUALS2_BEFORE_UPDATE", line 2

ORA-04088: error during execution of trigger 'ADMUSER.OP3_ACTUALS2_BEFORE_UPDATE'

ORA-06512: at "USACE.UPDATE_TASKRSRC", line 6

ORA-06512: at line 5


I granted the execution priveldge to admuser. it should fail also by admuser. But it does sucess.

admuser@s0p2pvd5> set serveroutput on

admuser@s0p2pvd5> declare

2 l_rowid varchar(50);

3 l_rows number;

4 begin

5 l_rows := usace.update_taskrsrc( 'ADMUSER', 1,

6 6639, l_rowid );

7

8 dbms_output.put_line( 'Updated ' || l_rows || ' rows' );

9 dbms_output.put_line( 'its rowid was ' || l_rowid );

10 end;

11 /

Updated 1 rows

its rowid was AAAF+iAAFAAACA1AAA

PL/SQL procedure successfully completed.
admuser@s0p2pvd5>

If i understood the definer's right correctly, admuser's execution should fail also, because usace's privilege had been used.


Tom Kyte
September 12, 2004 - 11:04 am UTC

<quote>
we have a trigger to prevent schema other than the two schemes (admuser and
privuser) to DML on table admuser.taskrsrc.
</quote>

you have a trigger?!? In my world I would use "grant".


but in any case, unless you can provide a

CONCISE (as in small)
yet 100% COMPLETE (as in anyone that wanted to run the test case could do so)

testcase -- nothing we can really say. I mean -- hey, the error is being thrown in code we don't even have a clue as to what it looks like.

Trigger and invoker...

Ajeet, October 28, 2004 - 1:08 pm UTC

Tom,

I have a trigger on a table say T1 in a schema 'FLT' at one oracle instance.This trigger insert a row into a table say T2 in a differnet schema 'RDO' which is at a different oracle instance.
There is a public db link between the 2 oracle instances.

Now when a user 'A' tries to insert a row in table T1 at
then this trigger fails to insert a row in table T2 (which is at remote instance or other instance)..

This user 'A' does not exist at other instance.

I know that the for a public db link to work we have to have user 'A' at both instances with same password.

But using trigger (which is owned by same user which owns the table T1) --is there a way to do this.

the error i get is obvious -- invalid user/password...which tell me the reason..but is there a way to solve this problem.

Please advice.

Tom Kyte
October 28, 2004 - 7:29 pm UTC

trigger would use a database created via:

create database link foo connect to A identified by A_PW using 'connect_string'

else, database link connects as the current user, with their privs.

invoker's right does not work in nested stored procedure

jianhui, May 11, 2005 - 10:07 am UTC

Tom,
I need to turn on sql trace in a stored procedure but the user does not have ALTER SESSION granted directly, although it has it granted indirectly through a role.

I tried to use invoker's right to work around but it seems not working in nested procedure calls, see below example. Could you explain why it does not work in nested call but it works  in direct call? Does not procedure foo thinks the invoker is the current user? I can not put invoker right in procedure gee since it will be called by other users, which will break the application.

  1  create or replace procedure foo
  2  authid current_user
  3  is
  4  begin
  5    dbms_session.set_sql_trace(true);
  6* end;
F5DBOD@dor_f501>/

Procedure created.

SQL>create or replace procedure gee
SQL>is 
SQL>begin
SQL>  foo;
SQL>end;
SQL>/

Procedure created.

SQL>exec gee
BEGIN gee; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 126
ORA-06512: at "F5DBOD.FOO", line 5
ORA-06512: at "F5DBOD.GEE", line 4
ORA-06512: at line 1

SQL>exec foo

PL/SQL procedure successfully completed. 

Tom Kyte
May 11, 2005 - 12:09 pm UTC

sure it is working - your issue is that the INVOKER of foo is in fact GEE and GEE without any roles.

(if you have expert one on one oracle handy, i cover this in large detail)


before you call gee - you have your privs and roles, when you call gee, you have gee's owners BASE privs. when gee calls foo -- foo is running as gee's owner with no roles.

java definer vs invoker rights

Clark Pearson, April 02, 2012 - 9:50 am UTC

Over ten years ago now (!) you stated that effectively all java calls run as AUTHID DEFINER. Is this still true, or is there now an option to make the java run as AUTHID CURRENT_USER?

I've tried putting AUTHID CURRENT_USER after CREATE OR REPLACE AND COMPILE JAVA... but seemingly to no avail.

Thanks,
Clark.
Tom Kyte
April 03, 2012 - 6:27 am UTC

I do not believe anything changed there, no.

difference among 'current_user', 'session_user', 'current_schema'

A reader, April 05, 2012 - 1:56 am UTC

Hi Tom,

I am testing to have a better undestanding about 'current_user', 'session_user' and 'current_schema', but seems they are always same.
Anything wrong with my testing?

My understanding per your reply here is:
current_user should always equal to current_schema(definer should refer to definer's schema, while invoker of course need to refer invoker's schema
but sessoin_user can be different from them

Basically what i did was create 2 users, use 1 user to create 2 procedures, 1 is deifiner and 1 is invoker.
When call those 2 procedures with another user, the output:
for definer procedure are all definer, should not 'session_user' be different from the other 2?
for invoker procedure are all invoker, this one is understandable

Below is my testing script

create user leo identified by leo;
create user stt identified by stt;
grant sysdba,dba to leo,stt;
conn leo/leo
create or replace procedure leop1
as
begin
for x in (select sys_context( 'userenv', 'current_user' ) current_user,
sys_context( 'userenv', 'session_user' ) session_user,
sys_context( 'userenv', 'current_schema' ) current_schema
from dual) loop
dbms_output.put_line('Current User: '||x.current_user);
dbms_output.put_line('Session User: '||x.current_user);
dbms_output.put_line('Schema User: '||x.current_user);
end loop;
end;
/
create or replace procedure leop2
authid current_user
as
begin
for x in (select sys_context( 'userenv', 'current_user' ) current_user,
sys_context( 'userenv', 'session_user' ) session_user,
sys_context( 'userenv', 'current_schema' ) current_schema
from dual) loop
dbms_output.put_line('Current User: '||x.current_user);
dbms_output.put_line('Session User: '||x.current_user);
dbms_output.put_line('Schema User: '||x.current_user);
end loop;
end;
/
create public synonym leop1 for leo.leop1;
create public synonym leop2 for leo.leop2;
conn stt/stt
show user
set serveroutput on
exec leop1;
--Current User: LEO
--Session User: LEO
--Schema User: LEO
exec leop2;
--Current User: STT
--Session User: STT
--Schema User: STT
Tom Kyte
April 06, 2012 - 10:02 am UTC

look at your dbms_outputs! you printed out the same variable three times.

couple of notes:

a) do not use public synonyms, avoid them at all times. I'll let you have at most ONE per application but that is about it. Do not use them in your demos/test cases/etc especially - be explicit

b) DO NOT EVER GRANT SYSDBA/DBA to a developer account, a test account, nothing. use the concept of LEAST PRIVS (you need an account with create session/create procedure and another with create session and execute on two procedures). this is very very very important. do not over privilege yourself ever.

c) use better names when possible :)

Here is a better example:


stt%ORA11GR2> connect /
Connected.
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> drop user leo cascade;

User dropped.

ops$tkyte%ORA11GR2> drop user stt cascade;

User dropped.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> create user leo identified by leo;

User created.

ops$tkyte%ORA11GR2> create user stt identified by stt;

User created.

ops$tkyte%ORA11GR2> grant create session, create procedure to leo;

Grant succeeded.

ops$tkyte%ORA11GR2> grant create session to stt;

Grant succeeded.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> conn leo/leo
Connected.
leo%ORA11GR2> create or replace procedure dr( dont_call in boolean default false )
  2  as
  3  begin
  4      for x in (select sys_context( 'userenv', 'current_user' ) current_user,
  5                       sys_context( 'userenv', 'session_user' ) session_user,
  6                       sys_context( 'userenv', 'current_schema' ) current_schema
  7                  from dual)
  8      loop
  9          dbms_output.put_line('dr Current User: '||x.current_user);
 10          dbms_output.put_line('dr Session User: '||x.session_user);
 11          dbms_output.put_line('dr Schema User: '||x.current_schema);
 12      end loop;
 13      if (not dont_call)
 14      then
 15              execute immediate 'begin leo.ir( true ); end;';
 16      end if;
 17  end;
 18  /

Procedure created.

leo%ORA11GR2> create or replace procedure ir( dont_call in boolean default false )
  2  authid current_user
  3  as
  4  begin
  5      for x in (select sys_context( 'userenv', 'current_user' ) current_user,
  6                       sys_context( 'userenv', 'session_user' ) session_user,
  7                       sys_context( 'userenv', 'current_schema' ) current_schema
  8                  from dual)
  9      loop
 10          dbms_output.put_line('ir Current User: '||x.current_user);
 11          dbms_output.put_line('ir Session User: '||x.session_user);
 12          dbms_output.put_line('ir Schema User: '||x.current_schema);
 13      end loop;
 14      if (not dont_call)
 15      then
 16              execute immediate 'begin leo.dr( true ); end;';
 17      end if;
 18  end;
 19  /

Procedure created.

leo%ORA11GR2> 
leo%ORA11GR2> grant execute on dr to stt;

Grant succeeded.

leo%ORA11GR2> grant execute on ir to stt;

Grant succeeded.

leo%ORA11GR2> 
leo%ORA11GR2> conn stt/stt
Connected.
stt%ORA11GR2> show user
USER is "STT"
stt%ORA11GR2> alter session set current_schema=SCOTT;

Session altered.

stt%ORA11GR2> set serveroutput on
stt%ORA11GR2> exec leo.ir;
ir Current User: STT
ir Session User: STT
ir Schema User: SCOTT
dr Current User: LEO
dr Session User: STT
dr Schema User: LEO

PL/SQL procedure successfully completed.

stt%ORA11GR2> exec leo.dr;
dr Current User: LEO
dr Session User: STT
dr Schema User: LEO
ir Current User: LEO
ir Session User: STT
ir Schema User: LEO

PL/SQL procedure successfully completed.


invoker VS definer follow up

A reader, April 09, 2012 - 1:16 am UTC

Hi Tom,

Sorry for my previous bad test example. Thanks a ton for your clear explaination.

At first glance, i was wondering why you use "execute immediate 'begin leo.dr/ir(true);' end;", after testing i know your purpose
is to compile them successfully, otherwise both of them will result in INVALID. I have not met this kind of circle function call
before, so this is the only way to handle it?

Meanwhile, just learned from your example that a invoker will get lost if called from a definer right procedure/function.
Is not it a issue?

Let's see i have a schema(we call it parent schema) which define a 'logger' procedure which can be called by any other schema.
Its purpose is to keep log in 'app_log' table of this parent schema.
Besides this i also want to keep log in 'app_log' table of individual other schema who call 'logger' procedure.
So i create a 'child_logger' with invoker's right with parent schema, it will be called by 'logger' procedure.
Per my testing, result is not what i expected, all log flow into table of parent schema. (invoker right lost)
Any idea? I guess my example is not a good design.
But my point is inevitably 'invoker' and 'definer' procedures will call each other, which will be not our expectation.

drop user leo cascade;
drop user stt cascade;
create user leo identified by leo;
create user stt identified by stt;
grant create session, create procedure to leo;
grant create session to stt;
grant create table,resource to leo;
grant create table,resource to stt;
conn leo/leo
create table app_log(log_time date, log_msg varchar2(1000));
create or replace procedure child_logger
authid current_user
as
begin
insert into app_log(log_time,log_msg) values(sysdate,'This is log in individual schema as child');
end;
/
create or replace procedure logger as
begin
insert into app_log(log_time,log_msg) values(sysdate,'This is log in parent schema');
child_logger;
end;
/

grant execute on logger to stt;
grant execute on child_logger to stt;
conn stt/stt
create table app_log(log_time date, log_msg varchar2(1000));
exec leo.logger;

Tom Kyte
April 09, 2012 - 11:39 am UTC

so this is the only way to handle it?

No, we could have used a package with forward declares or by having the functions be exposed in the specification of the package.


Meanwhile, just learned from your example that a invoker will get lost if
called from a definer right procedure/function.
Is not it a issue?


it doesn't get lost - it is quite sensible the way it works.

If you invoke the invokers rights routine, it runs with your privs.
If someone else invokes the invokers rights routine, it runs with their privs.

If you run a definers rights routine, you "become" them from a security perspective. Anything that routine does - is done "as them". Hence, if a definers rights routine runs an invokers rights routine - the invokers rights routine has to run as the owner of the definers rights routine.

Think about the security black hole that would be opened up otherwise!


The invoker of an invoker rights routine is the schema that was "in charge" when the call was made.

Authid Current_User in Triggers

Guruswam D, August 17, 2012 - 11:47 am UTC

Tom,
With continuation on the current thread.

## Scott
create table uom_master
(
uom_code varchar2(240),
uom_code_desc varchar2(2400),
uom_client_id varchar2(3)
);

Insert into uom_master Values
('U001','Unit of Measurement 1','001');
Insert into uom_master Values
('U002','Unit of Measurement 1','001');
Insert into uom_master Values
('U001','Unit of Measurement 2','002');
Insert into uom_master Values
('U002','Unit of Measurement 2','002');

grant select, insert, update, delete, references, alter, index on uom_master to hr;

CREATE OR REPLACE TRIGGER RND_TRG_UOM_MASTER
BEFORE INSERT ON UOM_MASTER
FOR EACH ROW
DECLARE
M_SYS_CTX_VALUE VARCHAR2(240);
BEGIN
SELECT VALUE INTO M_SYS_CTX_VALUE
FROM SESSION_CONTEXT
WHERE NAMESPACE LIKE 'RND_CLIENT%';
:NEW.uom_CLIENT_ID := M_SYS_CTX_VALUE;
END;

## HR Schema
create or replace procedure p_set_clid_context_001(p_client_id in varchar2 default '001')
--authid current_user
is
begin
sys.dbms_session.set_context('RND_CLIENT_ID_001','CLIENT_ID_001',P_CLIENT_ID);
exception
when others then
dbms_output.put_line('code '|| sqlcode || 'message '|| sqlerrm);
dbms_output.put_line('Exception '||dbms_utility.format_error_backtrace);
end p_set_clid_context_001;
/

create or replace context RND_CLIENT_ID_001 using p_set_clid_context_001;

CREATE OR REPLACE TRIGGER hr.after_logon_trg
AFTER LOGON ON hr.SCHEMA
BEGIN
DBMS_APPLICATION_INFO.set_module(USER, ' Logon Context Initialized');
p_set_clid_context_001(); -- Default value will be Set
END;

create or replace view uom_master as
select * from
SCOTT.uom_master
where UOM_CLIENT_ID = sys_context('RND_CLIENT_ID_001','CLIENT_ID_001');

Insert into uom_master(UOM_CODE,UOM_CODE_DESC) Values
('U005','Unit of Measurement 2');


During this Insert the Trigger of the Table Fires and Fetches the Sys context Value from HR Schema. There is no Context defined in Scott Schema, the following select Statement
 SELECT * 
 FROM 
 SESSION_CONTEXT 

will return No Rows when run from Scott. But the context is picked from HR Schema where its defined. Though there is No Context defined in Scott Schema. The trigger fired for the view UOM_MASTER in HR is through the trigger RND_TRG_UOM_MASTER defined in Scott. Is the behaviour correct and kindly explain and clarify.


Tom Kyte
August 17, 2012 - 3:31 pm UTC

when others then
dbms_output.put_line('code '|| sqlcode || 'message '|| sqlerrm);
dbms_output.put_line('Exception '||dbms_utility.format_error_backtrace);
end p_set_clid_context_001;
/


i hate your code


There is no Context defined in Scott Schema, ,

there are no contexts defined in ANY schema outside of PUBLIC. they are all public.


the context is always going to be set in the session that invoked the set context call.


This is all very convoluted and confusing. I don't even know who I should be logging in as for the various steps.


you don't fetch a context value from a schema, that doens't make sense.

I don't really follow what you are trying to do here at all

DDL triggers

Rustam Kafarov, January 23, 2013 - 4:31 am UTC

Hi Tom,

You said that triggers always have definer rights because they are part of tables. OK. What about DDL triggers created on DATABASE? I want to handle every DDL statement in database (let's say for audit) and write it into table in current schema (definer of this trigger doesn't have those rights).
You can say that you can to create SCHEMA triggers on every schema to be able to do that. But I think that's a good real example when we need to execute trigger under invoker rights. Correct me if I'm wrong.

Thanks
Tom Kyte
January 30, 2013 - 12:30 pm UTC

why in the world would you want a bunch of audit trails????

No, I don't think this is a good idea for "invokers rights" triggers - I would never want

a) the audit trail for a schema to actually be in that schema (think about it, what can the owner of that audit trail do to the audit trail? everything). that doesn't even begin to make sense.

b) more than one audit trail for an event like this.

DDL triggers

Rustam Kafarov, February 04, 2013 - 2:26 pm UTC

I agree that in most cases you don't need to do that or you should not do that.

However I faced the situation when it might be done just not to create triggers on every schema. I found following solution which probably not ideal:
Create dbms_scheduler job as
dbms_scheduler.create_job(
job_name => <schema_name>||'.'||<job_name>,
job_type => 'STORED_PROCEDURE',
job_action => '<proc_name>',
enabled => FALSE,
auto_drop => FALSE
);
In database level trigger call
dbms_scheduler.run_job(
job_name => <schema_name>||'.'||<job_name>,
use_current_session => true
);

Owner of this trigger must have privilege "create any job" which is very powerfull.

Obviously if owner is very powerfull user it can make changes in any schema directly. So this approch suits only if we need to call some existing complex procedure created with current user right without possibility to change it.

Tom Kyte
February 06, 2013 - 7:50 am UTC

I don't follow this - what is the meaning of <schema_name> || '.' || <job_name> ??

what is the use case here, what were you trying to accomplish at a high level (what was the original requirement - forget for a moment the implementation you tried, what were you trying to accomplish...)

DDL triggers

Rustam Kafarov, February 06, 2013 - 10:18 am UTC

Let's say we have 2 schemas: schema_a and schema_b. Schema_a owns DDL trigger on some event and procedure proc_current with user rights. Trigger calls this procedure. When schema_b fires this trigger we need to execute proc_current with user schema_b rights.

The idea is when you create scheduler_job using schema name like: 'schema_b.test_job' it will be created in schema_b and executed with schema_b user rights.
Tom Kyte
February 06, 2013 - 2:08 pm UTC

but it won't, it doesn't work that way, ddl triggers have to run with definers rights.

what is in proc_current that needs to do this - please tell me what you are trying to accomplish NOT HOW you would like to accomplish it.

back up and explain the requirement, not the approach you would like to take (that cannot possibly work).

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library