Add a row-level access policy with VPD (DBMS_RLS)
OracleSecurityCommand
-- 1) Policy function returning the predicate:
CREATE OR REPLACE FUNCTION customer_app.customer_vpd_fn (
p_schema IN VARCHAR2,
p_object IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
-- The schema owner sees all rows (NULL = no restriction)
IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'CUSTOMER_APP' THEN
RETURN NULL;
END IF;
-- Other users see only their own city
RETURN 'city = SYS_CONTEXT(''USERENV'', ''CLIENT_IDENTIFIER'')';
END;
/
-- 2) Attach the policy to the table:
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'CUSTOMER_APP',
object_name => 'CUSTOMER',
policy_name => 'CUSTOMER_CITY_VPD',
function_schema => 'CUSTOMER_APP',
policy_function => 'CUSTOMER_VPD_FN',
statement_types => 'SELECT, UPDATE, DELETE',
update_check => TRUE,
policy_type => DBMS_RLS.CONTEXT_SENSITIVE);
END;
/
-- 3) Check:
SELECT object_owner, object_name, policy_name, function,
sel, upd, del, chk_option, enable, policy_type
FROM dba_policies WHERE object_owner = 'CUSTOMER_APP';
-- Test (as the application user): EXEC DBMS_SESSION.SET_IDENTIFIER('Ankara');
-- 4) Temporarily disable / drop:
EXEC DBMS_RLS.ENABLE_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD', FALSE);
EXEC DBMS_RLS.DROP_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD');What it does
Virtual Private Database automatically appends the WHERE predicate returned by the policy function to every SQL against the table, so users of the same table see/modify only the rows they are entitled to. update_check=>TRUE prevents a user from updating a row to a value they could not see.
When to use it
In multi-tenant applications, for region/branch-based data isolation, and to add row security without touching application code.
Caution
Included in Enterprise Edition, no extra license. CLIENT_IDENTIFIER can be set freely by the client; in production use an application context set by a trusted package (CREATE CONTEXT ... USING). SYS and users with EXEMPT ACCESS POLICY bypass the policy; Data Pump exports without that privilege silently export partial data. An error in the function blocks all access with ORA-28112. A wrong policy_type (e.g. STATIC) caches the predicate and returns wrong results.
Related
- Create, open, set the master key of and back up a TDE keystore (19c+)
- Encrypt tablespaces with TDE (new and existing, online/offline)
- Create a Unified Auditing policy, enable it conditionally, review records, remove it
- Create a password policy profile and assign it to users
- Find unused privileges with privilege analysis