feat: enforce DDS token access through common grants
This commit is contained in:
163
sql/adb/34_dds_token_data_grant_common_auth.sql
Normal file
163
sql/adb/34_dds_token_data_grant_common_auth.sql
Normal file
@@ -0,0 +1,163 @@
|
||||
-- ============================================================
|
||||
-- 34_dds_token_data_grant_common_auth.sql
|
||||
-- Token-driven DDS enforcement using the common CB_* permission model.
|
||||
--
|
||||
-- This is the product-shaped DDS path:
|
||||
-- 1. One local DDS END USER is used as the technical query identity.
|
||||
-- 2. The application calls CB_AGENT_CTX_PKG.SET_USER_BY_BEARER(token).
|
||||
-- 3. A DATA GRANT is attached to one DDS DATA ROLE on the protected VIEW.
|
||||
-- 4. Its predicate function reads CB_PERMISSION / CB_PERMISSION_RULE,
|
||||
-- including direct roles and active group-inherited roles.
|
||||
--
|
||||
-- The token is not a bind parameter in CREATE DATA GRANT syntax. It is
|
||||
-- resolved before SELECT and stored in the trusted CB_AGENT_CTX namespace;
|
||||
-- the DATA GRANT predicate then evaluates the common permission tables.
|
||||
--
|
||||
-- Prerequisite: 17_agent_ords_security_local_vpd_setup.sql,
|
||||
-- 25_agent_ords_security_backoffice_support.sql,
|
||||
-- 31_dds_standalone_demo_setup.sql,
|
||||
-- 32_dds_vector_tag_setup.sql
|
||||
-- Run as ADMIN on an Oracle AI Database release with Deep Data Security.
|
||||
-- ============================================================
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
SET ECHO ON
|
||||
SET FEEDBACK ON
|
||||
SET DEFINE ON
|
||||
SET VERIFY OFF
|
||||
|
||||
PROMPT === 1. Creating one technical DDS END USER and its DATA ROLE ===
|
||||
CREATE END USER IF NOT EXISTS "dds_demo_token" IDENTIFIED BY "&DDSUSER_TOKEN_PASSWORD";
|
||||
|
||||
BEGIN
|
||||
EXECUTE IMMEDIATE 'CREATE ROLE cb_dds_token_connect_role';
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
IF SQLCODE != -1921 THEN
|
||||
RAISE;
|
||||
END IF;
|
||||
END;
|
||||
/
|
||||
GRANT CREATE SESSION TO cb_dds_token_connect_role;
|
||||
|
||||
CREATE DATA ROLE IF NOT EXISTS cb_dds_token_role;
|
||||
GRANT cb_dds_token_connect_role TO cb_dds_token_role;
|
||||
GRANT DATA ROLE cb_dds_token_role TO "dds_demo_token";
|
||||
|
||||
-- A DDS END USER is not a conventional database user. Standard object
|
||||
-- privileges therefore go to a standard role inherited by the DATA ROLE.
|
||||
GRANT EXECUTE ON admin.cb_agent_ctx_pkg TO cb_dds_token_connect_role;
|
||||
|
||||
PROMPT === 2. Creating the common permission predicate function ===
|
||||
CREATE OR REPLACE FUNCTION admin.cb_dds_vector_tag_allowed(
|
||||
p_tech_tag IN VARCHAR2
|
||||
) RETURN NUMBER
|
||||
AUTHID DEFINER
|
||||
AS
|
||||
v_user_id NUMBER;
|
||||
v_allow NUMBER := 0;
|
||||
v_deny NUMBER := 0;
|
||||
BEGIN
|
||||
BEGIN
|
||||
v_user_id := TO_NUMBER(SYS_CONTEXT('CB_AGENT_CTX', 'USER_ID'));
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RETURN 0;
|
||||
END;
|
||||
|
||||
IF v_user_id IS NULL OR p_tech_tag IS NULL THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
-- Direct roles and roles inherited from active application groups are
|
||||
-- deliberately expanded at query time. CB_* remains the source of truth.
|
||||
SELECT COUNT(*)
|
||||
INTO v_allow
|
||||
FROM (
|
||||
SELECT ur.role_id
|
||||
FROM cb_user_role ur
|
||||
WHERE ur.user_id = v_user_id
|
||||
UNION
|
||||
SELECT gr.role_id
|
||||
FROM cb_user_group ug
|
||||
JOIN cb_app_group g
|
||||
ON g.group_id = ug.group_id
|
||||
AND g.active_yn = 'Y'
|
||||
JOIN cb_group_role gr
|
||||
ON gr.group_id = ug.group_id
|
||||
WHERE ug.user_id = v_user_id
|
||||
) effective_role
|
||||
JOIN cb_app_user u
|
||||
ON u.user_id = v_user_id
|
||||
AND u.active = 'Y'
|
||||
JOIN cb_permission p
|
||||
ON p.role_id = effective_role.role_id
|
||||
JOIN cb_permission_rule r
|
||||
ON r.perm_id = p.perm_id
|
||||
WHERE p.target_name = 'CB_VECTOR_SEARCH_DOCUMENTS'
|
||||
AND p.action_name = 'SELECT'
|
||||
AND p.permission_effect = 'ALLOW'
|
||||
AND (
|
||||
r.rule_type = 'ALL'
|
||||
OR (
|
||||
r.rule_type = 'TAG'
|
||||
AND REGEXP_LIKE(
|
||||
UPPER(p_tech_tag),
|
||||
'(^|,)' || UPPER(TRIM(r.rule_value)) || '(,|$)'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
SELECT COUNT(*)
|
||||
INTO v_deny
|
||||
FROM (
|
||||
SELECT ur.role_id
|
||||
FROM cb_user_role ur
|
||||
WHERE ur.user_id = v_user_id
|
||||
UNION
|
||||
SELECT gr.role_id
|
||||
FROM cb_user_group ug
|
||||
JOIN cb_app_group g
|
||||
ON g.group_id = ug.group_id
|
||||
AND g.active_yn = 'Y'
|
||||
JOIN cb_group_role gr
|
||||
ON gr.group_id = ug.group_id
|
||||
WHERE ug.user_id = v_user_id
|
||||
) effective_role
|
||||
JOIN cb_permission p
|
||||
ON p.role_id = effective_role.role_id
|
||||
JOIN cb_permission_rule r
|
||||
ON r.perm_id = p.perm_id
|
||||
WHERE p.target_name = 'CB_VECTOR_SEARCH_DOCUMENTS'
|
||||
AND p.action_name = 'SELECT'
|
||||
AND p.permission_effect = 'DENY'
|
||||
AND r.rule_type = 'TAG'
|
||||
AND REGEXP_LIKE(
|
||||
UPPER(p_tech_tag),
|
||||
'(^|,)' || UPPER(TRIM(r.rule_value)) || '(,|$)'
|
||||
);
|
||||
|
||||
IF v_allow > 0 AND v_deny = 0 THEN
|
||||
RETURN 1;
|
||||
END IF;
|
||||
RETURN 0;
|
||||
END;
|
||||
/
|
||||
|
||||
SHOW ERRORS
|
||||
|
||||
PROMPT === 3. Creating one object-level DATA GRANT ===
|
||||
CREATE OR REPLACE DATA GRANT admin.dds_demo_token_vector_grant
|
||||
AS SELECT
|
||||
ON admin.cb_dds_vector_search_documents
|
||||
WHERE admin.cb_dds_vector_tag_allowed(tech_tag) = 1
|
||||
TO cb_dds_token_role;
|
||||
|
||||
PROMPT === 4. Verifying the object-level grant ===
|
||||
SELECT grant_name, object_name, grantee
|
||||
FROM dba_data_grants
|
||||
WHERE grant_name = 'DDS_DEMO_TOKEN_VECTOR_GRANT';
|
||||
|
||||
PROMPT === Token-driven DDS DATA GRANT setup complete ===
|
||||
PROMPT Call ADMIN.CB_AGENT_CTX_PKG.SET_USER_BY_BEARER(:token) on the
|
||||
PROMPT technical connection, then SELECT from ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS.
|
||||
EXIT;
|
||||
Reference in New Issue
Block a user