Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.server -> Re: dbms_rls and 815E on NT [LONG]

Re: dbms_rls and 815E on NT [LONG]

From: Mark Townsend <mtownsen_at_us.oracle.com>
Date: Thu, 20 Jan 2000 14:16:33 -0800
Message-ID: <38878941.6DC1D6E4@us.oracle.com>


Jonathan Lewis wrote:
>
> Just to add the icing (dream topping, etc).
>
> If you are using the an RLS function that
> generates a predicate of the form:
> dept_id = sys_context('application_name','legal_department')
>
> and a logon trigger that uses
> dbms_session.set_context()
> to set the context for the user, then your trigger can
> read
> >create trigger my_trigger
> >before insert on T for each row
> >begin
> > :new.dept_id := sys_context('application_name','legal_department')
> >end;
> >/
>
> (at least I assume it should be able to,
> I haven't tested it yet).
>
> --
>
> Jonathan Lewis

>
> Thomas Kyte wrote in message <5lee8s4gcp9vhrmce1ilelhocdhi82krfh_at_4ax.com>...
>
> >you use dbms_rls to restrict access to data.
> >
> >You can use a trigger to supply non-defaultable values.
> >
> >You would code:
> >
> >create trigger my_trigger
> >before insert on T for each row
> >begin
> > :new.dept_id := Your_Function_That_Returns_The_Correct_id_For_This_User;
> >end;
> >/

Hmm - a certain mixture of terms here. In fact, you don't need RLS to do this at all. An application context variable is all that is required. Then this variable can be set when the user logons, and then referenced in the insert trigger - this saves the function (and potential lookup) from being executed for each insert operation. The application context variable can then also be used in the RLS returned predicate. See below for a longish example

First we will create an application context. To do this we give our context owner (in this case Scott) the create context privilege. Typically you would have a dedicated schema owner for application contexts.

SQL> connect system/manager_at_oe
Connected.

SQL> grant create any context to scott;

Grant succeeded.

Now create the context and the associated package that sets a variable in the context - in this case, the users DEPTNO

SQL> connect scott/tiger_at_oe
Connected.

SQL> create context scott using scott.context_package;

Context created.

SQL> create or replace package context_package as   2 procedure set_context;
  3 end;
  4 /

Package created.

SQL> create or replace package body context_package is   2 procedure set_context is
  3 this_employees_deptno number;   4 begin

  5        select deptno into this_employees_deptno from emp
  6              where ename = sys_context('userenv','session_user');
  7       
dbms_session.set_context('scott','deptno',this_employees_deptno);
  8        exception
  9           when no_data_found then
 10              dbms_session.set_context('scott','deptno',0);
 11 end set_context;
 12 end context_package;
 13 /

Package body created.

So we have created a context variable called DEPTNO, that is set whenever the set_context procedure is called. If we find the user that is logging on in the EMP table, we look up and set DEPTNO to the corresponding deptno in the emp table. If the logging on user is not found in the table, we simply set DEPTNO to a 'invalid' value - in this case 0.

We only want to set DEPTNO once for each user, so we use logon triggers to call and set the DEPTNO for the user that is logging on.

SQL> connect system/manager_at_oe
Connected.

SQL> create or replace trigger scott.set_application_context   2 after logon on database
  3 begin
  4 scott.context_package.set_context;   5 end;
  6 /

Trigger created.

Now let's test this as the user scott.

SQL> connect scott/tiger_at_oe
Connected.
SQL> select sys_context('scott','deptno') from dual;

SYS_CONTEXT('SCOTT','DEPTNO')



20

Cool. Lets complete the rest of the example by building a test table to attach the insert triggers to.

SQL> create table test_insert as select ename,deptno from emp where 1=2;

Table created.

SQL> desc test_insert;

 Name                                                  Null?    Type
 ----------------------------------------------------- --------
--------------
 ENAME                                                         
VARCHAR2(10)
 DEPTNO                                                        
NUMBER(2) In the insert trigger I can refer to the DEPTNO application context variable, which is now automatically set each time a user logs on. I don't have to look up this user each time I do an insert - we have stored the relevant DEPTNO for this user once in this session. Real good for scalability

SQL> create or replace trigger insert_trigger   2 before insert on test_insert for each row   3 begin
  4 :new.deptno := sys_context('scott','deptno');   5 end;
  6 /

Trigger created.

SQL> insert into test_insert values('fred',null);

1 row created.

SQL> insert into test_insert values('barney',0);

1 row created.

SQL> insert into test_insert values('wilma',30);

1 row created.

SQL> select * from test_insert;

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20

All working correctly. Now lets try it for a user that is not in the EMP table

SQL> connect system/manager_at_oe
Connected.
SQL> select * from scott.test_insert;

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20

SQL> insert into scott.test_insert values('fred',null)

1 row created.

SQL> insert into scott.test_insert values('barney',0);

1 row created.

SQL> insert into scott.test_insert values('wilma',30);

1 row created.

SQL> select * from scott.test_insert;

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20
fred               0
barney             0
wilma              0


6 rows selected.

These inserts get the 'invalid' deptno.

If I want to prevent inserts of invalid details altogether, I can then create a security policy that prevents the user from inserting a value that does not match their corresponding DEPTNO in their application context.

First I create a security package that returns the perdicate I want used

SQL> connect scott/tiger_at_oe
Connected.
SQL> create or replace package security_package as   2 function insert_security(owner varchar2, objname varchar2) return varchar2;
  3 function select_security(owner varchar2, objname varchar2) return varchar2;
  4 end security_package;
  5 /

Package created.

SQL> create or replace package body security_package as   2 function insert_security(owner varchar2, objname varchar2) return varchar2 is
  3 begin
  4 return 'DEPTNO<>0';
  5 end insert_security;
  6 function select_security(owner varchar2, objname varchar2) return varchar2 is
  7 begin
  8 return 'DEPTNO=sys_context(''scott'',''deptno'')';   9 end select_security;
 10 end security_package;
 11 /

Package body created.

Package body created.

Not that I am only allowing inserts of a deptno value other the 0. However, I am allowing the user to select any rows that match their store DEPTNO. Referring to the context variable here is better than having the predicate hard code a deptno value - as all users can share the same SQL statement. Way good for scalability.

Now we simply use the DBMS_RLS functions to apply these security policies to my target table

SQL> connect system/manager_at_oe
Connected.

SQL> begin
  2 dbms_rls.add_policy('scott','test_insert','insert_security',   3
'scott','security_package.insert_security','insert',true);   4 dbms_rls.add_policy('scott','test_insert','select_security',   5
'scott','security_package.select_security','select');   6 end;
  7 /

PL/SQL procedure successfully completed.

Now test the new select and insert policies. If you remember, here's the current data in the table

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20
fred               0
barney             0
wilma              0

As user scott, I can only see the following

SQL> connect scott/tiger_at_oe
Connected.
SQL> select * from test_insert;

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20

As another 'non valid' user, I can only see the following

SQL> connect system/manager_at_oe
Connected.

SQL> select * from scott.test_insert;

ENAME DEPTNO
---------- ---------

fred               0
barney             0
wilma              0


Now lets try the insert

SQL> connect scott/tiger_at_oe
Connected.
SQL> insert into test_insert values('bam bam',null);

1 row created.

SQL> select * from test_insert;

ENAME DEPTNO
---------- ---------

fred              20
barney            20
wilma             20
bam bam           20

SQL> connect system/manager_at_oe
Connected.
SQL> insert into scott.test_insert values('bam bam',null); insert into scott.test_insert values('bam bam',null)

                  *

ERROR at line 1:
ORA-28115: policy with check option violation

SQL> select * from scott.test_insert;

ENAME DEPTNO
---------- ---------

fred               0
barney             0
wilma              0


As this user is not in the emp table, their DEPTNo value is always going to be 0. And the security polciy is specifcally preventing inserts of colummns with a value of zero. Hence only vaid employees can insert data, and their correct deptno is always used. There is no way to cirumvent this at all. No matter what application or end user tool the

user is using. Cooooooooooooooool !!

Hope this helps

--
Regards,

Mark Townsend                         

Senior Product Manager				Ph: 	(650) 633 5764
Server Division					Fx: 	(650) 506 7222
Oracle Corporation				Email:	mtownsen_at_us.oracle.com

Received on Thu Jan 20 2000 - 16:16:33 CST

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US