Legal | Privacy
Creating a Virtual Private Database
 
 

Creating a Virtual Private Database

Module Objectives

Purpose

In this module, you will learn how to create and test a virtual private database.

Objectives

After completing this module, you should be able to:

Setup an application context

Create the user's application context and corresponding package
Create a logon trigger that executes the context package
Test the application context to ensure that it is working correctly
Create security policies and functions
Test the security policies
Apply the security policies to the required tables
Test the virtual private database implementation

Prerequisites

Before starting this module, you should have completed the following:

Preinstallation Tasks

Install the Oracle9i Database

Postinstallation Tasks

Review the Sample Schema
Downloaded vpd.zip and saved it in your working directory

Reference Material

The following is a list of useful reference material if you want additional information about the topics in this module:

Documentation: Part Number A88876-02: Oracle9i Application Developer's Guide - Fundamentals

 

Overview

Virtual Private Database in Oracle8i and Oracle9i

Oracle8i set a new standard in database security with the introduction of Virtual Private Database (VPD): server-enforced, fine-grained access control, together with secure application context, which enables multiple customers and partners to have secure direct access to mission-critical data. Within a single database, the Virtual Private Database enables data access control by user or by customer with the assurance of physical data separation. For Internet access, the Virtual Private Database can ensure that online banking customers see only their own accounts. Web-hosting companies can maintain data of multiple companies in the same Oracle9i database, while permitting each company to see only its own data.

Within the enterprise, the Virtual Private Database results in lower costs of ownership in deploying applications. Security can be built once, in the data server, rather than in each application that accesses data. Security is stronger, because it is enforced by the database, no matter how a user accesses data. Security is no longer bypassed by a user accessing an ad hoc query tool or new report writer. Virtual Private Database is key technology that enables organizations to build hosted, Web-based applications. Indeed, many Oracle applications themselves use VPD to enforce data separation for hosting, including Oracle SalesOnline.com and Oracle Portal.

The Virtual Private Database secures data in the database by providing security at the row level, across all applications, by attaching a security policy directly to a table or view. Oracle9i expands the Virtual Private Database by adding several new enhancements:

Oracle Policy Manager, a tool to facilitate security policy administration
Partitioned fine-grained access control, to ease VPD deployment in multi-application and hosted environments
Global application context, to support application user models

How the Virtual Private Database Works

The Virtual Private Database is enabled by associating one or more security policies with tables or views. Direct or indirect access to a table with an attached security policy causes the database to consult a function that implements the policy. The policy function returns an access condition known as a predicate (a WHERE clause), which the database appends to the user's SQL statement, thus dynamically modifying the user's data access.

You can implement VPD by writing a stored procedure to append a SQL predicate to each SQL statement that controls row- level access for that statement. For example, if John Doe (who belongs to Department 10) inputs the SELECT * FROM emp statement, you can use VPD to add the WHERE DEPT = 10 clause. In this way, you use query modification to restrict data access to certain rows.

A secure application context enables access conditions to be based on virtually any attributes an application deems significant, such as organization, cost center, account number, or position. For example, a Web order entry system can enforce access based on customer number, and whether the user is a customer or a sales representative. In this way, customers can view their order status online (but only for their own orders), while sales representatives can view multiple orders, but only for their own customers.

The Virtual Private Database ensures that, no matter how a user gets to the data (through an application, a report writing tool, or SQL*Plus), the same strong access control policy is enforced. In this way, VPD can help banks ensure that customers see their only own accounts, that telecommunications firms can keep customer records safely segregated, and that human resources applications can support their complex rules of data access to employee records.

 

Setup

You must be able to identify the type of user who is connecting and to set some attributes about the user that will be used in the security policies. You do this with an application context. You will grant the OE and HR users the privilege to create application contexts, add the user_id column to the EMPLOYEES and CUSTOMERS tables, create the test users, and grant the HR user access to the CUSTOMERS table in the OE schema. Perform the following:

1.

From a SQL*Plus session connected to the your database, execute the following:

@vpd_setup.sql
connect sys/oracle@orcl.world as SYSDBA;
Grant CREATE ANY CONTEXT to OE;
GRANT CREATE ANY CONTEXT to HR;
/***************************************************************/
/* Add USER_ID column to the EMPLOYEES table in the HR schema */
/***************************************************************/
connect hr/hr@orcl.world
Alter table employees add (user_id varchar (8));
update employees set user_id = 'OE1' where employee_id = 201;
commit;
/***************************************************************/
/* Add USER_ID column to the CUSTOMERS table in the OE schema */
/***************************************************************/
connect oe/oe@orcl.world
Alter table customers add (user_id varchar (8));
update customers set user_id = 'CU1' where customer_id = 125;
commit;
/**************************************************************/
/* Add test users and role */
/**************************************************************/
connect sys/oracle@orcl.world as sysdba;
create role order_entry_role;
grant order_entry_role to public;
grant connect, resource, select any sequence, insert any table, update any table to order_entry_role;
create user oe1 identified by oe1;
alter user oe1 default tablespace example temporary tablespace temp default role order_entry_role;
create user cu1 identified by cu1;
alter user cu1 default tablespace example temporary tablespace temp default role order_entry_role;
/**************************************************************/
/* Grant HR access to OE tables */
/**************************************************************/
connect oe/oe@orcl.world
grant select on oe.customers to HR;

 

 

Create the OE and HR User's Application Context and Corresponding Packages

Create the user's application context and the corresponding package that sets variables in this context. Perform the following:

1.

You create the OE and HR context package by executing the following script:

@vpd_create_context.sql
CONNECT hr/hr@orcl.world;
CREATE OR REPLACE CONTEXT HR USING HR.Context_Package;
CREATE OR REPLACE PACKAGE Context_Package AS
PROCEDURE Set_Context;
END;
/
SHOW ERRORS
2.

Now create the package bodies for the Context_Package by executing the following scripts:

@vpd_create_context_package.sql

CONNECT hr/hr@orcl.world ;
CREATE OR REPLACE PACKAGE BODY Context_Package IS
PROCEDURE Set_Context IS
This_PERSON_ID NUMBER;
BEGIN
/* Record the fact that we are setting up a users context */
/* We do this as we need to look up the user in the employee */
/* table when they log on, and we do not want the employee */
/* security policy to interfere with this lookup */
Dbms_Session.Set_Context('HR','SETUP','TRUE');
IF SUBSTR(SYS_CONTEXT('USERENV','SESSION_USER'),1,2)='OE'
THEN
BEGIN
/* User is not a customer, therefore they must be an employee*/
/* Set their Employee Id */
Dbms_Session.Set_Context('HR','ROLE', 'INTERNAL');
SELECT Employee_Id INTO This_PERSON_ID
FROM HR.Employees
WHERE User_Id = SYS_CONTEXT('USERENV','SESSION_USER');
Dbms_Session.Set_Context('HR','PERSON_ID', This_PERSON_ID);
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* No Employee Id found, set a dummy Id */
Dbms_Session.Set_Context('HR','PERSON_ID', 0);
END;
END IF;
IF SUBSTR(SYS_CONTEXT('USERENV','SESSION_USER'),1,2)='CU'
THEN
/* If User is a customer, check to see if they have been */
/* given access */
BEGIN
Dbms_Session.Set_Context('HR','ROLE', 'EXTERNAL');
SELECT customer_id INTO This_PERSON_ID
FROM oe.customers
WHERE user_id = SYS_CONTEXT('USERENV','SESSION_USER');
Dbms_Session.Set_Context('HR','PERSON_ID', This_PERSON_ID);
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* Customer has no access, so set a dummy id */
Dbms_Session.Set_Context('HR','PERSON_ID', 0);
END;
END IF;
Dbms_Session.Set_Context('HR','SETUP','FALSE');
END Set_Context;
END Context_Package;
/
SHOW ERRORS
3.

Now make the packages public by executing the following scripts:

@vpd_grant_context_package_public.sql

Connect hr/hr@orcl.world;
GRANT EXECUTE ON hr.Context_Package to Public;

4.

You now must create the triggers to invoke the Context_Package when necessary by executing the following scripts:

@vpd_create_context_trigger.sql

CONNECT sys/oracle@orcl.world AS SYSDBA;
CREATE OR REPLACE TRIGGER HR.Set_Security_Context
AFTER LOGON ON DATABASE
BEGIN
HR.Context_Package.Set_Context;
END;
/
SHOW ERRORS

 

 

Test the Application Context

Before you implement the security policies, you will quickly test whether the user application context is set correctly.

1.

Run the following script to check which user is an employee:

@vpd_test_context_oe1.sql
CONNECT oe1/oe1@orcl.world 
COLUMN Username FORMAT A8
COLUMN Role FORMAT A8
COLUMN Id FORMAT A4
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id
FROM dual;

 

2. Run the following script to try a customer:
@vpd_test_context_cu1.sql
CONNECT cu1/cu1@orcl.world
COLUMN Username FORMAT A8
COLUMN Role FORMAT A8
COLUMN Id FORMAT A4
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id
FROM dual;



3.

Try other valid users who do not necessarily have order entry capability. Run the following script:

@vpd_test_context_oe.sql
CONNECT oe/oe@orcl.world
COLUMN Username FORMAT A8
COLUMN Role FORMAT A8
COLUMN Id FORMAT A4
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id
FROM dual;



 

Create the Security Policies and Functions

You can now create the PL/SQL functions to implement the security policies that are required. A security policy function decides which SQL predicate to return, based on the user's application context, to qualify that user's access to data. You can also prevent users from inserting data that they are not permitted to insert. You will use three security policies to do the following:

  • Prevent customers from creating anything other than online orders, and internal Order Entry users from creating anything other than direct orders. This prevents Order Entry users from claiming compensation for an order they didn't actually process.
  • Prevent customers from seeing orders for any customer other than themselves
  • Prevent employees from accessing employee data about anyone other than themselves or their direct reports

 

1.

Create the Security_Package package specification by executing the following script:

@vpd_create_security_package.sql 
CONNECT OE/OE@orcl.world;
CREATE OR REPLACE PACKAGE Security_Package AS
FUNCTION Sale_Orders_Insert_Security(Owner VARCHAR2, Objname VARCHAR2)
RETURN VARCHAR2;
FUNCTION Sale_Orders_Select_Security(Owner VARCHAR2, Objname VARCHAR2)
RETURN VARCHAR2;
FUNCTION Employees_Select_Security (Owner VARCHAR2, Objname VARCHAR2)
RETURN VARCHAR2;
END Security_Package;
/
SHOW ERRORS

2.

Create the Security_Package package body by executing the following script:

@vpd_create_security_package_body.sql 
CONNECT Oe/Oe@orcl.world;
CREATE OR REPLACE PACKAGE BODY Security_Package IS
FUNCTION Employees_Select_Security(Owner VARCHAR2, Objname
VARCHAR2)
RETURN VARCHAR2 IS
Predicate VARCHAR2(2000);
BEGIN
/* If we are setting up a users context then we don't */
/* want queries against the employee table to be restricted */
/* so simply return a null predicate */
IF SYS_CONTEXT('OE','SETUP')='TRUE'
THEN
Predicate := null;
RETURN Predicate;
END IF;
/* If the user is a customer (i.e External), or if for some */
/* reason we could not find a valid employee id, return a */
/* predicate that prevents any access to the data. Otherwise*/
/* qualify the query so that users may only see their own */
/* or their sub-ordinates information */
IF (SYS_CONTEXT('HR','ROLE') = 'EXTERNAL')
OR (SYS_CONTEXT('HR', 'PERSON_ID') = 0)
THEN
Predicate := '1=2';
ELSE
Predicate := 'EMPLOYEE_ID = SYS_CONTEXT(''HR'',''PERSON_ID'')
OR MANAGER_ID = SYS_CONTEXT(''HR'',''PERSON_ID'')';
END IF;
RETURN predicate;
END Employees_Select_Security;
FUNCTION Sale_Orders_Select_Security(Owner VARCHAR2, Objname
VARCHAR2)
RETURN VARCHAR2 IS
Predicate VARCHAR2(2000);
BEGIN
Predicate := '1=2';
IF (SYS_CONTEXT('HR', 'ROLE') = 'INTERNAL')
THEN
/* If the user is an employee (i.e. Internal), then they */
/* can see all sale orders. Customers (i.e External) can */
/* only see the sale orders that are their own */
Predicate := NULL;
ELSE
Predicate := 'CUSTOMER_ID =
SYS_CONTEXT(''HR'',''PERSON_ID'')';
END IF;
RETURN Predicate;
END Sale_Orders_Select_Security;
FUNCTION Sale_Orders_Insert_Security(Owner VARCHAR2, Objname
VARCHAR2)
RETURN VARCHAR2 IS
Predicate VARCHAR2(2000);
BEGIN
Predicate := '1=2';
IF (SYS_CONTEXT('HR', 'ROLE') = 'INTERNAL')
THEN
/* Internal employees can only create direct orders */
Predicate := 'ORDER_MODE = ''direct'' ';
ELSE
/* External customers can only create online order */
Predicate := 'ORDER_MODE = ''online'' ';
END IF;
RETURN Predicate;
END Sale_Orders_Insert_Security;
END Security_Package;
/
SHOW ERRORS

3. Now make the security policy functions accessible to the public. Execute the following script:
@vpd_grant_security_package_public.sql

CONNECT OE/OE@orcl.world;
GRANT EXECUTE ON Oe.Security_Package TO PUBLIC;

 

Test the Security Policies

You are now ready to test the security policies that you just created. Perform the following:

1.

You will now test the security policies, first as an Order Entry user. Execute the following:

@vpd_test_security_package_oe1.sql 
CONNECT oe1/oe1@orcl.world;
COLUMN Username FORMAT A8
COLUMN Role FORMAT A5
COLUMN Id FORMAT A2
COLUMN Predicate FORMAT A45
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('OE', 'ROLE') Role,
SYS_CONTEXT('OE', 'PERSON_ID') Id,
'EMPLOYEES' Object,
'SELECT' DML,
OE.security_package.Employees_Select_Security('HR','EMPLOYEES') Predicate
FROM dual
UNION ALL
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('OE', 'ROLE') Role,
SYS_CONTEXT('OE', 'PERSON_ID') Id,
'ORDERS' Object,
'SELECT' DML,
OE.security_package.Sale_Orders_Select_Security('OE','ORDERS') Predicate
FROM dual
UNION ALL
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('OE', 'ROLE') Role,
SYS_CONTEXT('OE', 'PERSON_ID') Id,
'ORDERS' Object,
'INSERT' DML,
OE.security_package.Sale_Orders_Insert_Security('OE','ORDERS') Predicate
FROM dual;


The Order Entry user OE1 is identified as an internal user, and his employee ID is set in the his application context. For SELECT statements on the EMPLOYEES table, the user is able to see only his own details and the details of any subordinates. The Order Entry has a null predicate on the SALE ORDERS table, so you can see all sale orders. The Order Entry user can insert only direct sale orders.

2. Now try this function as a customer. Execute the following:
@vpd_test_security_package_cu1.sql 
CONNECT cu1/cu1@orcl.world;
COLUMN Username FORMAT A8
COLUMN Role FORMAT A8
COLUMN Id FORMAT A3 COLUMN DML FORMAT A6
COLUMN Predicate FORMAT A37
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id,
'EMPLOYEES' Object,
'SELECT' DML,
OE.security_package.Employees_Select_Security('HR','EMPLOYEES')
Predicate
FROM dual
UNION ALL
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id,
'SALE_ORDERS' Object,
'SELECT' DML,
OE.security_package.Sale_Orders_Select_Security('OE','ORDERS')
Predicate
FROM dual
UNION ALL
SELECT SYS_CONTEXT('USERENV','SESSION_USER') Username,
SYS_CONTEXT('HR', 'ROLE') Role,
SYS_CONTEXT('HR', 'PERSON_ID') Id,
'SALE_ORDERS' Object,
'INSERT' DML,
OE.security_package.Sale_Orders_Insert_Security('OE','ORDERS')
Predicate
FROM dual;


The customer user CU1 is identified as an external user and has her customer ID set in her application context. She will not be able to select any information from the EMPLOYEES table, even if given access. The customer can select only sale orders that have been created for her and will be able to insert only online sale orders.

 

 

Apply the Security Policies to the Required Tables

After testing the security policies, you now attach them to the tables to which you want them applied. Note that a single security policy can be applied to multiple tables, and it can also be applied to views. Perform the following:

1.

Add the policy to the table. Execute the following:

@vpd_attach_security_policies.sql 

CONNECT system/oracle@orcl.world;
begin
dbms_rls.add_policy('OE',
'ORDERS',
'SALE_ORDERS_INSERT_POLICY',
'OE',
'SECURITY_PACKAGE.SALE_ORDERS_INSERT_SECURITY',
'INSERT',
TRUE);
dbms_rls.add_policy('OE',
'ORDERS',
'SALE_ORDERS_SELECT_POLICY',
'OE',
'SECURITY_PACKAGE.SALE_ORDERS_SELECT_SECURITY',
'SELECT');
dbms_rls.add_policy('HR',
'EMPLOYEES',
'EMPLOYEES_SELECT_POLICY',
'OE',
'SECURITY_PACKAGE.EMPLOYEES_SELECT_SECURITY',
'SELECT');
end;
/
SHOW ERRORS

 

2. Now check the policies to see whether they have been set and are enabled. Execute the following:
@vpd_show_security_policies.sql 

SELECT object_name,
policy_name,
sel,ins,upd,del,
chk_option,enable
FROM all_policies;

The EMPLOYEES table is protected with EMPLOYEES_SELECT_POLICY for SELECT operations. The SALE_ORDERS table is protected with SALE_ORDERS_INSERT_POLICY for INSERT operations and with SALE_ORDERS_SELECT_POLICY for SELECT operations. Other than for purposes of a naming standard, there is no need for a correlation between the security policy and the tables or views to which it is attached.

Note: Depending which lessons you have done in the Oracle9i by Example Series, you may get additional objects.

 

 

Test the Virtual Private Database Implementation

You will now see the policies in action, first as a customer, then as an Order Entry employee.

1.

After logging in as a customer, you can see how many online orders you have placed. Execute the following:

@vpd_test_select_sale_orders_cu1.sql 
CONNECT Oe/OE@orcl.world ;
GRANT SELECT ON ORDERS TO PUBLIC;
CONNECT CU1/CU1@orcl.world ;
SELECT COUNT(*) FROM Oe.Orders;

 

2.

Now, after logging in as an employee, you can see how many direct orders have been placed. Execute the following:

@vpd_test_select_sale_orders_oe1.sql 
CONNECT OE/OE@orcl.world ;
GRANT SELECT ON ORDERS TO PUBLIC;
CONNECT OE1/OE1@orcl.world ;
SELECT COUNT(*) FROM Oe.orders;

 

3.

Now log in as user OE1 and try to insert 2 orders, a direct order and an online order. Execute the following script:

@vpd_test_insert_sale_orders_oe1.sql
CONNECT OE1/OE1@orcl.world ;
INSERT INTO oe.orders VALUES (3000
,TO_TIMESTAMP('17-AUG-99 02.34.12.234359 PM'
,'DD-MON-YY HH.MI.SS.FF AM'
,'NLS_DATE_LANGUAGE=American')
,'direct'
,123
,0
,78279.6
,153
,NULL);
INSERT INTO oe.orders VALUES (3001
,TO_TIMESTAMP('17-AUG-99 02.34.12.234359 PM'
,'DD-MON-YY HH.MI.SS.FF AM'
,'NLS_DATE_LANGUAGE=American')
,'online'
,125
,0
,78279.6
,154
,NULL);
ROLLBACK;


An Order Entry user can insert a direct sales order, but cannot insert an online sales order.

4.

Try the same exercise as a customer. Execute the following script:

@vpd_test_insert_sale_orders_cu1.sql
CONNECT OE1/OE1@orcl.world ;
INSERT INTO oe.orders VALUES (3000
,TO_TIMESTAMP('17-AUG-99 02.34.12.234359 PM'
,'DD-MON-YY HH.MI.SS.FF AM'
,'NLS_DATE_LANGUAGE=American')
,'direct'
,123
,0
,78279.6
,153
,NULL);
INSERT INTO oe.orders VALUES (3001
,TO_TIMESTAMP('17-AUG-99 02.34.12.234359 PM'
,'DD-MON-YY HH.MI.SS.FF AM'
,'NLS_DATE_LANGUAGE=American')
,'online'
,125
,0
,78279.6
,154
,NULL);

ROLLBACK;


A customer cannot insert a direct sale order, but can insert an online sale order.

 

Reset your Database

Now that you have examined the capabilities of the virtual private database, you should refresh your database. To do this, perform the following:

1.

From a SQL*Plus session, execute the following:

@vpd_cleanup.sql 
spool vpd_cleanup.log

CONNECT system/oracle@orcl.world ;
DROP USER oe CASCADE;
DROP USER hr CASCADE;
DROP USER oe1 CASCADE;
DROP USER cu1 CASCADE;
@d:\oracle\ora90\demo\schema\human_resources\hr_main
@d:\oracle\ora90\demo\schema\order_entry\oe_main

spool off;

Note: You will be prompted for some variables when the hr_main and oe_main SQL scripts.

 

 

Module Summary

In this module, you should have learned how to:

Setup an application context

Create the user's application context and corresponding package
Create a logon trigger that executes the context package
Test the application context to ensure that it is working correctly
Create security policies and functions
Test the security policies
Apply the security policies to the required tables
Test the virtual private database implementation

 

Copyright © 2002 Oracle Corporation. All Rights Reserved.

 

Close Window

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy