Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Jens.

Asked: August 15, 2000 - 4:21 am UTC

Last updated: January 30, 2005 - 10:17 am UTC

Version: 8.1.5

Viewed 1000+ times

You Asked

Tom,
I have enjoyed reading yours and others articles on Fine Grained Access Control, but most of the stuff assume you issue your selects in client/server environment. My application would be Designer-generated packages and manually written Pl/Sql procedures executed by accessing agents on an Oracle Application Server 4.0.8.1. The agents are protected using Basic_Oracle authentication with roles defined against realms. DCD username / passwords are stored with the DCD.
I'm trying to pick up the web-server username using OWA_UTIL.GET_CGI_ENV('REMOTE_USER'), and select this value into a variable in my security function to define the where-predicate for FGAC. However, I always get an ORA-28112 error, so I'm assuming that a NO_DATA_FOUND is returned when the function tries to build my predicate.
Question is: Can web-server username be used at all with FGAC?
Below is my function BUILD_PREDICATE, which is applied on the IMT_WEB.EMP table, as well as the PH_SHOW_DEMO procedure that is called from the browser via a protected url.

Please note that PH_SHOW_DEMO works fine if the call to OWA_UTIL is taken out, and a userid is hard-coded.
Any input would be appreciated.
Regards,
Jens

function build_predicate(
obj_schema varchar2,
obj_name varchar2)
return varchar2
is
my_level varchar2(10);
retval varchar2(2000);
my_userid varchar2(100);
begin
--if coming in from web-server:
if sys_context('userenv','ip_address') = '<MY_WEB_SERVERS_IP_ADDRESS>' then
select owa_util.get_cgi_env('REMOTE_USER') into my_userid from dual;

select accesslevel into my_level
from sec_matrix
where upper(table_owner) = 'IMT_WEB'
and upper(table_name) like 'DEMO_ANS'
and upper(bbrno)=upper(my_userid);
--below doesn't work either....
-- and upper(bbrno)=upper(owa_util.get_cgi_env('remote_user'));

retval := 'accesslevel='''||my_level||'''';
else
--from other machines
retval := 'accesslevel=''none''';
end if;

return retval;
end build_predicate;


PROCEDURE ph_show_demo IS
str varchar2(2000);
sql_stmt varchar2(100);
me varchar2(100);
BEGIN
htp.htmlopen;
htp.headopen;
htp.headclose;
htp.bodyopen;
htp.p('Employees limited by row-level security:');
htp.br;
htp.br;
htp.br;
select build_predicate('IMT_WEB','DEMO_ANS') into str from dual;
htp.p(str);
htp.br;
select owa_util.get_cgi_env('remote_user') into me from dual;
--htp.p(owa_util.get_cgi_env('remote_user'));
htp.p('I am user: ');
htp.p(me);
htp.br;
htp.br;
sql_stmt := 'select * from demo_ans';
htp.tableopen;
htp.tablerowopen;
htp.tabledata('ANSID');
htp.tabledata('NAVN');
htp.tabledata('DEPTID');
htp.tabledata('ACCESSLEVEL');
htp.tablerowclose;
owa_util.cellsprint(sql_stmt,2000);
htp.tableclose;
htp.bodyclose;
htp.htmlclose;

END;

and Tom said...

The fine grained access control code is running in a different "space" then your code. It does not have access to your package states and such.

You need to set the username in a context before using the FGAC. The application context is the way to get the state to the FGAC routines. Consider this small example:



ppr_test@8i> create or replace package demo_pkg
2 as
3 procedure set_remote_user;
4
5 function security_function( p_schema in varchar2,
6 p_object in varchar2 )
7 return varchar2;
8
9 end;
10 /

Package created.

ppr_test@8i> create or replace context my_context
2 using demo_pkg
3 /

Context created.


Thats my security policy and a function to set up the application context (and the create context). Now I can implement the package body. My policy will be really simply -- I will return a predicate that is basically "where owner = REMOTE_USERNAME"

ppr_test@8i> create or replace package body demo_pkg
2 as
3
4
5 procedure set_remote_user
6 is
7 begin
8 dbms_session.set_context( 'my_context', 'remote_user',
9 upper(owa_util.get_cgi_env('remote_user')) );
10 end;
11
12
13 function security_function( p_schema in varchar2,
14 p_object in varchar2 )
15 return varchar2
16 is
17 begin
18 return 'owner = '||
19 'sys_context( ''my_context'', ' ||
20 ' ''remote_user'' )';
21 end;
22
23
24 end;
25 /

Package body created.

Notice how the procedure set_remote_user stuffs the cgi_env into the application context (and remember - the only way to set the context is via a procedure/function in the demo_pkg, we've BOUND that package to that context -- there will be no "spoofing"). The security policy on the other hand does not refer to the cgi-env but rather the application context

Continuing on:


ppr_test@8i> create table my_table
2 ( data varchar2(30),
3 OWNER varchar2(30) default USER
4 )
5 /

Table created.

ppr_test@8i>
ppr_test@8i> begin
2 dbms_rls.add_policy
3 ( object_schema => user,
4 object_name => 'MY_TABLE',
5 policy_name => 'MY_POLICY',
6 function_schema => user,
7 policy_function => 'demo_pkg.security_function',
8 statement_types => 'select',
9 update_check => FALSE );
10 end;
11 /

PL/SQL procedure successfully completed.

Created a table and attached the policy to it. Now lets put some data in there (my username on the web is WEB$TKYTE)..

ppr_test@8i> insert into my_table values ( 'abc', 'WEB$TKYTE' );
ppr_test@8i> insert into my_table values ( 'abc', 'WEB$TKYTE' );
ppr_test@8i> insert into my_table values ( 'abc', 'WEB$TKYTE' );
ppr_test@8i> insert into my_table values ( 'abc', 'NOT ME' );
ppr_test@8i> insert into my_table values ( 'abc', 'NOT ME' );
ppr_test@8i> insert into my_table values ( 'abc', 'NOT ME' );

and now I create a small package for the web:



ppr_test@8i> create or replace package web_demo_pkg
2 as
3 procedure show_some_data;
4 end;
5 /

Package created.

ppr_test@8i>
ppr_test@8i>
ppr_test@8i> create or replace package body web_demo_pkg
2 as
3
4 procedure show_some_data
5 is
6 begin
7 htp.header( 1, 'testing' );
8 for x in ( select * from my_table )
9 loop
10 htp.bold( x.data || ',' || x.owner || '<br>' );
11 end loop;
12 end;
13
14
15 BEGIN
16 demo_pkg.set_remote_user;
17 end;

18 /

Package body created.

ppr_test@8i> grant execute on web_demo_pkg to public
2 /


Notice how I used the elaboration code in the package body to automagically set the application context. Every package body may have such code -- its a snippet of code that will be executed as the package is loaded for the first time -- its initialization code. It does all of the work of setting the username for us. Now when I run this, I get:




testing

abc,WEB$TKYTE
abc,WEB$TKYTE
abc,WEB$TKYTE

as expected....



Rating

  (2 ratings)

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

Comments

Is it relevant to ask here..

A reader, January 30, 2005 - 6:02 am UTC

Tom,
I have an application (web based) which uses connection pools to login in this application.so all the procedures will be executed by a single user say xxx_web..now I want to implement the Row level Security for this App..which needs user id's. User id's will be avaliable in LDAP which we use for authentication, so can i use this user id to apply row level security --if ye -- How ? any pointer to a document would be a help.
I will re-phrase -- all the users uses their user id's to login on this portal -- and there is just one user which is used by connection pool --executes all the procedures (pl/sql) which basicaly do all the work and returns the data on web --Now i want to apply RLS -- based on the individual user's user ids..can i do this..is it even possible?? or i have to go whole nine yard to implement my own code to do this?

Thanks so much
Ajeet

Tom Kyte
January 30, 2005 - 9:34 am UTC

<quote>
I have an application (web based) which uses connection pools to login in this
application.so all the procedures will be executed by a single user say
xxx_web..
</quote>

I wonder why everyone seems to take that as a given, connection_pool -> single account. It isn't true at all. (search docs for proxy authentication)


you would be using a global application context, with session identifiers.

or even just dbms_session.set_client_identifier (but I'm not a fan of that)

you can search for "global application context" for some examples.


if you click on the links I like above and get David Knox's book -- a lot of options will become clear to you -- even though it says 10g, most of it apples to 9i as well.

A reader, January 30, 2005 - 10:17 am UTC

Quote from Tom's reply above: "...most of it apples to 9i as well."

That gave me a chuckle, and I don't know why.

More to Explore

Security

All of the vital components for a secure database are covered in the Security guide.