- Accepts
exactly two parameters: the schema
name (p_schema in
varchar2)
and the table name (p_table
in varchar2)
- Returns
one value only, a string—return
varchar2 as l_retstr varchar2(2000);—comprising
the predicate that will be appended
to every query on the table
- Uses a
cursor routine—for cust_
rec in—to
get the list of values, because each
user may have several
cust_ids
listed in the table
- Returns a
constructed string—l_retstr—to
be used as a predicate whenever any
user attempts to access the
underlying table
The function
returns the predicate where
cust_id in
with the appended list of customer
accounts (cust_id),
separated by commas, that the user (am_name
= USER) is
allowed to see.
Note that
before entering the loop that builds the
list of cust_ids
for the user, the code in
Listing 2
checks to see if the user is the table
owner BANK,
in which case the function returns a
NULL
predicate, as follows:
if (p_schema = user) then
1_retstr := null;
After building
this function, you want to make sure it
returns the appropriate predicate, by
testing some sample data. Connect to the
database as SECMAN,
and insert some records into the
ACCESS_POLICY
table, giving SECMAN
read privileges on a few sample
accounts, as follows:
insert into access_policy values ('SECMAN',123,'S');
insert into access_policy values ('SECMAN',456,'S');
insert into access_policy values ('SECMAN',789,'S');
Now execute
the function:
select get_sel_cust_id
('BANK','CUSTOMERS') from dual;
The function
returns a string that will be applied as
a predicate, as shown in the following
sample output:
GET_SEL_CUST_ID('BANK','CUSTOMERS')
------------------------
CUST_ID IN (123,456,789)
You need to
create similar functions for the other
types of access. For simplicity's sake,
create a single function for all the
other access types—UPDATE,
DELETE, INSERT—as
shown in
Listing 3.
However, note that for a real-world
application, each type of access should
have its own individual function
defined, to ensure appropriate privacy.
The policy
function in
Listing 3
is nearly identical to the policy
function in
Listing 2,
except that the predicate is further
qualified by use of the information from
the ACCESS_CONTROL
table:
and access_type in ('I', 'U', 'D')
Creating a
policy function is just the first step.
You now need to ensure that the function
will be used, by defining the policy
that should control its use in your
system.
Step 2.
Define a Policy
Policies are
defined with the DBMS_RLS
package, which is Oracle-supplied. Be
aware that the policies themselves are
not database objects owned by any user
(schema); they are logical constructs.
Any user who has the execute privilege
on the DBMS_RLS
package can modify or drop a policy
created by another user. Privileges to
DBMS_RLS
should be judiciously controlled and
granted with caution.
In the
following example, user
SECMAN is
granted execute privileges (by
SYS) on
the DBMS_RLS
package:
grant execute on dbms_rls to secman;
Listing 4
creates a policy named
CUST_SEL_POLICY
on the table CUSTOMERS
of schema BANK.
This policy applies the predicate
returned by the function
GET_SEL_CUST_ID
(which is shown in
Listing 2)
owned by schema SECMAN
to all SELECT
statements on the table.
Similarly, you
place another policy on the table for
other access types, as shown in
Listing 5.
This policy applies to inserts, updates,
and deletes in the CUSTOMERS
table.
It is almost
identical to the SELECT
policy, except that this policy includes
a check that ensures that the policy
will remain compliant even after an
update:
update_check => TRUE
Data cannot be
added to the table unless it adheres to
the policy.
Step 3. Test
the Setup
Now that the
building blocks are in place, let's see
how they work. Connecting as user
BANK and
issuing a simple select *
from customers;
query displays the following:
CUST_ID CUST_NAME
------- ------------
123 Jay Kulkarni
456 Wim Patel
These two
records are the full contents of the
CUSTOMERS
table, and both records are shown
because BANK
owns the table, so the predicate clause
is NULL—that
is, no predicate is applied. However,
when user LARA
makes the same query, she sees the
following:
select * from customers;
CUST_ID CUST_NAME
------- ---------
456 WIM PATEL
LARA
sees only CUST_ID 456,
not 123,
because that is the row she is
authorized to see, as determined by the
ACCESS_ POLICY
table. Note that the query has no
WHERE
clause but that the selection from the
table is automatically filtered to show
only the authorized rows.
If user
SCOTT
makes the same query, his results are
different from the results for
LARA: select * from customers;
CUST_ID CUST_NAME
------- ------------
123 Jay Kulkarni
456 Wim Patel
User
SCOTT
sees both rows, because he is authorized
to do so, as shown in the
ACCESS_POLICY
table. When user LARA
issues the query, the policy function
get_sel_cust_id
returns the predicate where
cust_id in (456).
Lara's original query select *
from customers is
rewritten as
select * from
(select * from customers)
where cust_id in (456)
The predicate is
automatically appended to the user's
original query. The same thing happens
when the user updates the table:
SQL> update bank.customers
2 set cust_name = 'PAT TERRY';
1 row updated.
Note that in this
example, only one row is updated, even
though there are actually two rows in
the underlying table. The policy (CUST_IUD_POLICY)
appends the predicate where
cust_id in (456)
to the update statement. Similarly,
while the table is being deleted, only
the rows for which the user is
authorized are deleted.
Attempting to
insert a row containing data for which
the user is not authorized results in an
error message. For example, in this
query, LARA
is attempting to add a record to the
CUSTOMER
table for an account not under her
purview:
SQL> insert into bank.customers
2 values (789,'KIM PARK');
insert into bank.customers
*
ERROR at line 1:
ORA-28115: policy with check option
violation
According to the
ACCESS_POLICY
table, Scott has SELECT
privileges on account 789—no other
privileges for any other AM are listed
in the table.
Using policies in
conjunction with functions ensures
authorized access to specific records of
a table. The rules are applied
regardless of how the table is accessed,
whether through an application or
directly through an ad hoc query tool,
such as SQL*Plus. Users see only rows
for which they have been authorized
Policies can be
applied to multiple tables, and a single
policy function can be used by any
number of policies. Listing 6 shows a
policy on an ACCOUNTS
table that uses the
get_sel_cust_id
function initially created for use with
the CUSTOMERS
table.