Skip to Main Content
  • Questions
  • Check whether a user has a particular role

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: January 31, 2001 - 3:56 pm UTC

Last updated: April 09, 2007 - 10:16 am UTC

Version: 816

Viewed 10K+ times! This question is

You Asked

Hi Tom,

Is there a simple that I can check whether a logged in user possess a particular role from pl/sql. For example, in Sybase, there is a system procedure call 'proc_role' which is used to for this purpose. eg. if (proc_role("sso_role") = 0).
Is there a similar procedure that can be used in Oracle?

Thanks.

David.

and Tom said...

There is nothing builtin but a procedure can easily be written. I'll put two below. One uses the data dictionary views -- the other uses the base tables. The reason is -- the data dictionary views in this case get slow when you have 10's of thousands of users (as I do). The base table queries go very fast. I'll present both:



ops$tkyte@ORA8I.WORLD> create or replace
function proc_role( p_role in varchar2 ) return number
2 is
3 l_cnt number;
4 begin
5 select count(*) into l_cnt
6 from dual
7 where exists ( select NULL
8 from dba_role_privs
9 where grantee = USER
10 and granted_role = upper(p_role) );
11
12 return l_cnt;
13 end;
14 /

Function created.

Elapsed: 00:00:00.61
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec dbms_output.put_line( proc_role('dba') );
1

PL/SQL procedure successfully completed.

Elapsed: 00:00:05.50
ops$tkyte@ORA8I.WORLD> exec dbms_output.put_line( proc_role('resource') );
0

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.72

So, that shows I have the DBA role but not RESOURCE. It took 5 seconds each to figure that out (I 50k users and 100k dba_role_privs). Going to the base tables:


ops$tkyte@ORA8I.WORLD> create or replace function proc_role( p_role in varchar2 ) return number
2 is
3 l_cnt number;
4 begin
5 select count(*) into l_cnt
6 from dual
7 where exists ( select null
8 from sys.user$ u1,
9 sys.sysauth$ sa
10 where u1.user# = sa.privilege#
11 and sa.grantee# = UID
12 and u1.name = upper(p_role) );
13
14 return l_cnt;
15 end;
16 /

Function created.

Elapsed: 00:00:00.26
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec dbms_output.put_line( proc_role('dba') );
1

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
ops$tkyte@ORA8I.WORLD> exec dbms_output.put_line( proc_role('resource') );
0

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.01

is it instantaneous.

For the first version -- the owner of the procedure must have SELECT granted DIRECTLY to them (not via a role) on DBA_ROLE_PRIVES. For the second, they need it on USER$ and SYSAUTH$. See
</code> http://asktom.oracle.com/Misc/RolesAndProcedures.html <code>
for why the direct grant is needed.



Rating

  (6 ratings)

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

Comments

PROC_ROLE

Dana Day, March 28, 2003 - 9:08 am UTC

Very interesting application of data dictionary.

logon trigger condition

Sravan, February 01, 2005 - 4:49 pm UTC

Hello Tom,

my requirement is that if a speific user logs in, am inserting a row into a table. So, am setting a logon trigger to do this work.
=======================
create or replace trigger logontrig
after logon on database
DECLARE
asid NUMBER;
aprogram VARCHAR2(48);
ausername VARCHAR2(30);
aserial NUMBER;
BEGIN
execute immediate 'select v.sid, v.program, v.username, v.serial#
from v$session v
where sid = ( select sid from v$mystat where rownum = 1 )' INTO asid, aprogram, ausername, aserial;

if ausername='SHRAV' THEN
execute immediate 'insert into shrav.shrav_log values (100)';
end if;
COMMIT;
END;
/
=====================

The trigger is created fine(logged in as user sys). Now, when I log into sqlplus as user 'SHRAV', the row is not getting inserted.

I do not believe the trigger is getting fired. I am stuck here, can you please guide me in the right direction in troubleshooting this issue.

Tom Kyte
February 02, 2005 - 4:45 am UTC

why in the world would you use dynamic SQL?????


first, is shrav a DBA... I'm going to guess yes.

what is happening is your trigger is failing, shrav as a dba is allowed to log in anyway. the error will be in your alert log and the error would be one of:

table or view doesn't exist for the query (the definer of this routine needs direct access to v_$session and v_$mystat

table or view doesn't exist for shrav.shrav_log

LOSE the dynamic sql
COMPILE the trigger with STATIC sql

and if you don't have the necessary privs, you'll KNOW right away.


but peek at your alert log right now

Thanks ..... it works now

Sravan, February 02, 2005 - 1:12 pm UTC

Hello Tom,
I removed dynamic sql and implemented static SQL. Peeped into the alert.log and corrected the error. It works fine now. Thanks for the pointer. Yes, Shrav is a DBA. Why do you recommened against the use of dynamic sql in this situation? Extra overhead, resources ???

My real requirement on using these triggers is that when a session gets connected from a specific program, I need to modify the user sessions' Resource Consumer Group. The Resource Consumer Group, Plan and Plan directives are created on the db already.
My logic is when a user logs into the db, a logon trigger checks if its coming from a spf. program and then execute grant_switch_consumer_group, set_initial_consumer_group and set the RESOURCE_MANAGER_PLAN to the new_plan.

Similarly, on logoff, a trigger revokes the privileges, set_initial_consumer_group to default and resets the RESOURCE_MANAGER_PLAN to null.

====== logon trigger =====
create or replace trigger logontrig
after logon on database
DECLARE
asid NUMBER;
aprocess VARCHAR2(12);
aprogram VARCHAR2(48);
ausername VARCHAR2(30);
aosuser VARCHAR2(30);
aserial NUMBER;
BEGIN
select v.sid, v.process, v.program, v.username, v.osuser, v.serial# INTO asid, a
process, aprogram, ausername, aosuser, aserial
from v$session v
where sid = ( select sid from v$mystat where rownum = 1);

insert into shrav.shrav_log values(100); -- debug stt.

IF aprogram='jrew.exe' THEN
dbms_resource_manager_privs.grant_switch_consumer_group
(ausername, 'UI_RESOURCE_GROUP', TRUE );
dbms_resource_manager.set_initial_consumer_group
(ausername, 'UI_RESOURCE_GROUP' );

DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA;
DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA;

insert into shrav.shrav_log values(101); -- debug stt.
execute immediate 'alter system set resource_manager_plan = '||'UI_PLAN';

END IF;
END;
/
============
===== logoff trigger =====
create or replace trigger logouttrig
before logoff on database
DECLARE
asid NUMBER;
aprocess VARCHAR2(12);
aprogram VARCHAR2(48);
ausername VARCHAR2(30);
aosuser VARCHAR2(30);
aserial NUMBER;
BEGIN
select v.sid, v.process, v.program, v.username, v.osuser, v.serial# INTO asid, a
process, aprogram, ausername, aosuser, aserial
from v$session v
where sid = ( select sid from v$mystat where rownum = 1);
insert into shrav.shrav_log values(200); -- debug stt.

IF aprogram='jrew.exe' THEN
dbms_resource_manager_privs.revoke_switch_consumer_group
(ausername, 'UI_RESOURCE_GROUP');
dbms_resource_manager.set_initial_consumer_group
(ausername, 'DEFAULT_CONSUMER_GROUP' );

DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA;
DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA;

insert into shrav.shrav_log values(201); -- debug stt.
execute immediate 'alter system set resource_manager_plan = ''';
END IF;
END;
/
===============

This is my first stab at resource consumer groups. If there is any flaw in my logic and understanding, please let me know. I would greatly appreciate it.

Thanks much
-Sravan


Tom Kyte
February 03, 2005 - 1:05 am UTC

if you have my book "Effective Oracle by Design", I cover in depth why dynamic sql is a tool that should be used only when you really need it.

You were a case study in "why", had you used static sql, you would have found your trigger never compiled!!!!!!!

you lose the dependency mechanism

you lose the plsql cursor cache

you lose lots of stuff by using dynamic sql -- so make sure the price you pay is worth it.

besides, coding in dynamic sql is just HARD as compared to static.


that grant/revoke logic - think about it. Think about it in the context of a multi-user environment. Think about what happens when an "spf" program logs in and then a non spf program.

will the non spf program have that grant?
yes


anyway, why grant on and grant off? just grant them the ability once and do the switch (which won't be needed in 10g by the way, you can set it up -- no trigger) in the trigger.



How about for a session

Sravan, February 03, 2005 - 11:40 am UTC

Tom,
thanks for the info. on dynamic sql. I will try to read the book.

You are absolutely right, I would have that problem of the user loging in from non-spf program with the grant. Alternateively, I was going to try to set the RCG to a session instead of the user. That way, am controlling the grant at the session level and not the user level -

dbms_session.switch_current_consumer_group(newg,oldg,flag);

I will try this option.
Also, I have been getting the pending area not active error, ORA-27391. And when I run clear the pending area and create the pending area stts., then am getting the ORA-29370: pending area is already active. I went through all the articles on metalink on this error but to noavail. Am little lost, if you have seen this error before, pelase let me know.

-Sravan

check on active role

Peter Mommen, April 03, 2007 - 5:56 am UTC

Would it be possible to make a likewise function that checks if the user does not only have the role, but also checks if the role is currently active?
Tom Kyte
April 04, 2007 - 9:41 am UTC

well, if you write a definers rights routine - no, roles are "not active" at all in them (the security domain changes - you become the definer with only their base set of privileges)

if you write an invokers rights routine - sort of - maybe. If you call the function DIRECTLY - as the invoker, yes. But, if you call it from a definers rights procedure, roles are gone again.

And the answer is NOT to use invokers rights all over the place, they should hardly ever be used.


ops$tkyte%ORA10GR2> create or replace function is_role_active( p_name in varchar2 ) return varchar2
  2  authid current_user
  3  as
  4          l_str   varchar2(50);
  5  begin
  6          select 'Role ' || role || ' is active'
  7            into l_str
  8            from session_roles
  9           where role = upper(p_name);
 10
 11          return l_str;
 12  exception
 13          when no_data_found
 14          then
 15                  return 'Role ' || p_name || ' is not active';
 16  end;
 17  /

Function created.

ops$tkyte%ORA10GR2> create or replace procedure p
  2  as
  3  begin
  4       dbms_output.put_line( is_role_active( 'DBA' ) );
  5  end;
  6  /

Procedure created.

ops$tkyte%ORA10GR2> exec p
Role DBA is not active

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> exec dbms_output.put_line( is_role_active( 'DBA' ) );
Role DBA is active

PL/SQL procedure successfully completed.



log off a user from pl/sql

v k ravi, April 07, 2007 - 7:32 am UTC

Hi tom

can we logoff a user forcely from a pl/sql block ?


Tom Kyte
April 09, 2007 - 10:16 am UTC

you can use dynamic sql to issue an alter system kill session, yes.

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