Search notes:

Oracle: Virtual Private Database

Simple demonstration

Define SQL*Plus/sqlcl variables

define dbService=localhost:1521/ora21
define sysPw=elCarosSecret
define tablespaceName=users

Create users

connect sys/&sysPw@&dbService as sysdba

create user vpd_admin  identified by secretGarden;

create user data_owner
   default   tablespace &tablespaceName
   temporary tablespace temp
   identified by secretGarden
   quota unlimited on &tablespaceName;


grant
   connect,
   create table
to
   data_owner;

grant
   connect,
   create procedure,
   create trigger,
   administer database trigger -- Required for 'after logon to database' trigger.
to
   vpd_admin;

grant
   administer row level security policy on schema data_owner
to
   vpd_admin;

grant
   execute on dbms_rls
to
   vpd_admin;


create user frank identified by secretGarden;
create user peter identified by secretGarden;
create user julia identified by secretGarden;

grant create session to frank;
grant create session to peter;
grant create session to julia;

Create tables

In this example, it is assumed that a company consists of different departments (with each having an entry in the departments table). An employee belongs to exactly on department. A department can have secrets that go into the department_secrets table.
connect data_owner/secretGarden@&dbService

create table data_owner.department (
   dep_id  int primary key,
   name    varchar2(30)
);

create table data_owner.employee (
   dep_id  references department,
   name    varchar2(30)
);

create table data_owner.department_secrets (
   dep_id references department,
   secret varchar2(30)
);

grant select on department to vpd_admin;
grant select on employee   to vpd_admin;

grant all on department_secrets to frank;
grant all on department_secrets to peter;
grant all on department_secrets to julia;
Insert some data into the tables (of which the data in department_secrets is confidential and private to the associated department):
begin
   insert into department values (1, 'Research and Development');
   insert into department values (2, 'Sales'                   );
   insert into department values (3, 'Human Resources'         );

   insert into employee values (2, 'Peter');
   insert into employee values (3, 'Julia');
   insert into employee values (3, 'Sandy');
   insert into employee values (1, 'Frank');
   insert into employee values (2, 'Eric' );
   insert into employee values (1, 'Joel' );

   insert into department_secrets values (1, 'R+D Secret #1'  );
   insert into department_secrets values (1, 'R+D Secret #2'  );
   insert into department_secrets values (2, 'Sales Secret #1');
   insert into department_secrets values (2, 'Sales Secret #2');
   insert into department_secrets values (3, 'HR Secret #1'   );
   insert into department_secrets values (3, 'HR Secret #2'   );

   commit;
end;
/

Create the VPD/policy package

For any employee, it must be possible to see all secrets of his department, but no secret of another department.
In order to make that happen with Oracle, we need to create a package, a trigger, and set a policy.
First, the package is created:
connect vpd_admin/secretGarden@&dbService

create or replace package vpd_admin.pck_vpd
   authid definer
as
   dep_id data_owner.department.dep_id%type;

   procedure set_dep_id(v_dep_id data_owner.department.dep_id%type);

   function predicate (obj_schema varchar2, obj_name varchar2) return varchar2;
end pck_vpd;
/

create or replace package body vpd_admin.pck_vpd as

   procedure set_dep_id(v_dep_id data_owner.department.dep_id%type) is
   begin
      dep_id := v_dep_id;
   end set_dep_id;


   function predicate(obj_schema varchar2, obj_name varchar2) return varchar2 is
   begin
      return 'dep_id = ' || dep_id;
   end predicate;

end pck_vpd;
/

Create a logon trigger

This trigger fires whenever someone logs on to the database. It finds the user's departement id (dep_id) and calls set_dep_id in the VPD package:
create or replace trigger vpd_admin.trg_vpd
   after logon on database
declare
   v_dep_id data_owner.department.dep_id%type;
begin

   select
      dep_id into v_dep_id
   from
      data_owner.employee
   where
      upper(name) = user;

   pck_vpd.set_dep_id(v_dep_id);
end;
/

Add a policy to the sensitive table

Finally, the policy is defined.
The policy states which procedure is used to add a where clause part to the where clause if someone executes a select statement:
begin
--
-- Requires
--    administer row level security policy on schema data_owner
-- privilage
--
   sys.dbms_rls.add_policy  (
      object_schema          => 'DATA_OWNER',
      object_name            => 'department_secrets',
      policy_name            => 'pol_dept_secrets',
      function_schema        => 'VPD_ADMIN',
      policy_function        => 'pck_vpd.predicate',
      statement_types        => 'select,update,delete'
   );
end;
/

Different users executing the same select statement:

Frank

Frank (belonging to R+D) executes a query:
connect frank/secretGarden@&dbService

@ select-stmt

Julia

Julia (belonging to HR) executes a query:
connect julia/secretGarden@&dbService

@ select-stmt

Peter

Peter (belonging to Sales) executes a query:
connect peter/secretGarden@&dbService

@ select-stmt

Cleaning up

connect sys/&sysPw@&dbService as sysdba

drop user frank      cascade;
drop user peter      cascade;
drop user julia      cascade;
drop user data_owner cascade;
drop user vpd_admin  cascade;

See also

dbms_rls
v$vpd_policy

Index