An AI assistant answers from what retrieval finds. If retrieval finds documents a user may not see, the model will happily summarize them for that user. Access control for AI is therefore access control for retrieval, and in Oracle Database it can be enforced where the data lives, with Virtual Private Database (VPD): a policy function adds a condition to every query of a table, whatever the query and whoever wrote it.
This guide adds an audience to every document, Public or Internal, enforces it on the document chunks with a VPD policy driven by an application context, shows the same question answered for a customer and for a support agent, and sets the audience from an APEX application.
Code for This Guide
The examples are files 06 to 08 in the examples/ch27 folder and file 02 in the examples/ch18 folder of the Oracle AI code repository on GitHub, each with its output.
These examples come from AI Applications with Oracle Database 26ai and APEX 26.1, a book of 237 tested examples of AI in Oracle Database and APEX.
They protect the DOC_CHUNKS table searched by the ASK function from how to build RAG with PL/SQL in Oracle Database, and the APEX part uses the agent from how to add an AI assistant to an Oracle APEX page.
Grant What VPD Needs
The schema needs the DBMS_RLS package, which manages policies, and an application context, ATLAS_CTX: a set of session values that only one trusted package, ATLAS_SECURITY, can set.
Example (run as SYS in the pluggable database):
-- what ATLAS needs for row-level security: DBMS_RLS, and an application context grant execute on dbms_rls to atlas; create or replace context atlas_ctx using atlas.atlas_security;
Output:
Grant succeeded. Context ATLAS_CTX created.
Add an Audience and a Policy
This example adds AUDIENCE to every document and creates ATLAS_SECURITY, with a procedure that records who is asking in the context and a policy function. The function returns a condition limiting DOC_CHUNKS to chunks of public documents, unless the session belongs to an agent. It then adds the policy and an internal document on goodwill credits.
Example:
-- every document is for customers (Public) or for agents only (Internal)
alter table atlas_documents add (audience varchar2(10) default 'Public' not null
constraint atlas_documents_audience_ck check (audience in ('Public', 'Internal')));
-- the session says who is asking; the policy decides which chunks it may see
create or replace package atlas_security is
procedure set_audience (p_audience in varchar2);
function documents_policy (p_schema in varchar2, p_object in varchar2)
return varchar2;
end;
/
create or replace package body atlas_security is
procedure set_audience (p_audience in varchar2) is
begin
dbms_session.set_context('ATLAS_CTX', 'AUDIENCE', p_audience);
end;
function documents_policy (p_schema in varchar2, p_object in varchar2)
return varchar2
is
begin
if sys_context('ATLAS_CTX', 'AUDIENCE') = 'Agent' then
return null; -- agents see every chunk
end if;
return 'doc_id in (select doc_id from atlas_documents where audience = ''Public'')';
end;
end;
/
begin
dbms_rls.add_policy(
object_schema => 'ATLAS',
object_name => 'DOC_CHUNKS',
policy_name => 'DOC_AUDIENCE',
function_schema => 'ATLAS',
policy_function => 'ATLAS_SECURITY.DOCUMENTS_POLICY',
statement_types => 'SELECT');
end;
/
-- an internal document: for agents only
declare
l_doc_id number;
begin
atlas_security.set_audience('Agent'); -- the chunks are inserted and read back
l_doc_id := add_document(
p_file_name => 'goodwill-credits.txt',
p_doc_type => 'Manual',
p_title => 'Goodwill credits (internal)',
p_product_id => 2,
p_content => to_blob(utl_raw.cast_to_raw(
'Goodwill credits. Agents may give a customer a goodwill credit of up to '
|| '50 US dollars for an outage or a billing mistake, without approval. Team leads '
|| 'approve credits up to 200 US dollars; larger credits need the head of '
|| 'support.')));
update atlas_documents set audience = 'Internal', reviewed_on = sysdate
where doc_id = l_doc_id;
commit;
end;
/Output:
Table ATLAS_DOCUMENTS altered. Package ATLAS_SECURITY compiled Package Body ATLAS_SECURITY compiled PL/SQL procedure successfully completed. PL/SQL procedure successfully completed.
| Piece | Role |
|---|---|
| ATLAS_CTX | Holds AUDIENCE for the session; only ATLAS_SECURITY can set it |
| SET_AUDIENCE | Records who is asking |
| DOCUMENTS_POLICY | Returns no condition for agents, and a public-only condition for everyone else |
| DBMS_RLS.ADD_POLICY | Applies the function to every SELECT on DOC_CHUNKS |
Ask as a Customer, Then as an Agent
Example:
-- the same question, asked by a customer and by an agent
exec atlas_security.set_audience('Customer')
select ask('How large a goodwill credit can be given without approval?') as customer_answer
from dual;
exec atlas_security.set_audience('Agent')
select ask('How large a goodwill credit can be given without approval?') as agent_answer
from dual;Output:
PL/SQL procedure successfully completed. CUSTOMER_ANSWER _____________________________________________________ I could not find this in the Atlas knowledge base. PL/SQL procedure successfully completed. AGENT_ANSWER __________________________________________________________________________________________________ Agents can give a goodwill credit of up to 50 US dollars without approval for an outage or a billing mistake [1].
For the customer, the internal document does not exist: retrieval cannot find it, so the model never sees it, and the assistant says it found nothing. For the agent, it answers with the internal rule and its citation.
Nothing in the retrieval function, the RAG function, or the knowledge view changed: the policy applies to every query of DOC_CHUNKS, including theirs. A session that sets no audience counts as a customer, so a mistake hides documents rather than revealing them.
Set the Audience in APEX
An APEX application sets the audience when its database session starts. In Shared Components, Security Attributes, enter the code below in Database Session, Initialization PL/SQL Code. APEX runs it at the start of every request of the application.
Initialization PL/SQL Code of the application:
-- the users of Atlas Support are agents: they may retrieve internal documents
atlas_security.set_audience('Agent');
A customer portal would set Customer, or derive the audience from the signed-in user's role. This example asks the APEX agent a question only the internal document answers, in a session of the application.
Example:
-- the session of Atlas Support is an agent's: the internal document is among the sources
declare
l_answer clob;
begin
dbms_output.put_line('Audience: ' || sys_context('ATLAS_CTX', 'AUDIENCE'));
l_answer := apex_ai.generate(
p_agent_static_id => 'atlas-assistant',
p_prompt => 'How large a goodwill credit can be given without approval?');
dbms_output.put_line('Answer: ' || l_answer);
end;
/Output:
Audience: Agent
Answer: Agents can give a customer a goodwill credit of up to 50 US dollars without approval for
an outage or a billing mistake.
Source: Goodwill credits (internal), part 1
PL/SQL procedure successfully completed.The session's audience is Agent, so the agent's search tool reached the internal document and the answer cites it. Nothing in the agent or its tools knows about audiences: the policy does the work. VPD for ordinary APEX reports is covered in row-level security in Oracle APEX with VPD.
Conclusion
To control what an AI assistant can see, control retrieval: add an audience to the source rows, put a VPD policy on the table retrieval searches, and drive it from an application context that only a trusted package can set. Default to the most restricted audience, and set the audience per session, in APEX with the application's Initialization PL/SQL Code. The RAG code, agent, and tools need no changes.
