Files
vpd-permission-poc/database/adb/63_kb_aso_masking_rule_runtime.sql

680 lines
24 KiB
MySQL

-- ============================================================
-- 63_kb_aso_masking_rule_runtime.sql
--
-- Synchronizes CB_MASKING_RULE / CB_COLUMN_MASKING_RULE and
-- CB_USER_MASKING_RULE into Oracle Data Redaction policies.
--
-- Important behaviour
-- * One selected masking rule is applied to each protected column.
-- * A direct user rule controls whether that user is masked or receives
-- an original-value exception (UNMASK).
-- * VPD still determines row visibility. Existing VPD NULL policies
-- remain in force for users without column permission.
--
-- Run as ADMIN with SQLcl after 62 to initialize the runtime package, or as
-- a manual recovery/reconciliation action. The backoffice synchronizes
-- DBMS_REDACT on normal configuration changes; this script follows the same
-- source-of-truth rule for existing installations.
-- Requires EXECUTE on DBMS_REDACT and ADMINISTER REDACTION POLICY for POC_2
-- objects.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET DEFINE OFF
SET FEEDBACK ON
PROMPT === Validating masking-rule metadata ===
DECLARE
PROCEDURE require_table(p_table_name IN VARCHAR2) IS
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM user_tables
WHERE table_name = p_table_name;
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20001,
'Missing ' || p_table_name || '. Run 62_kb_aso_masking_backoffice_metadata.sql first.');
END IF;
END;
BEGIN
require_table('CB_MASKING_RULE');
require_table('CB_COLUMN_MASKING_RULE');
require_table('CB_USER_MASKING_RULE');
END;
/
PROMPT === Updating trusted bearer context with column exception flags ===
CREATE OR REPLACE PACKAGE cb_agent_ctx_pkg AUTHID DEFINER AS
PROCEDURE clear_user;
PROCEDURE set_user(p_user_id IN NUMBER);
PROCEDURE set_user_values(
p_user_id IN NUMBER,
p_emp_no IN VARCHAR2,
p_dept_code IN VARCHAR2,
p_can_read_contents IN VARCHAR2
);
PROCEDURE set_user_by_bearer(p_bearer_key IN VARCHAR2);
END;
/
CREATE OR REPLACE PACKAGE BODY cb_agent_ctx_pkg AS
PROCEDURE clear_stakeholder_context AS
BEGIN
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID', NULL);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_ROLE', NULL);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_CHANNEL', NULL);
END;
PROCEDURE clear_masking_rule_context AS
BEGIN
FOR r IN (SELECT column_id FROM cb_column_masking_rule) LOOP
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'MR_' || TO_CHAR(r.column_id), NULL);
END LOOP;
END;
PROCEDURE set_masking_rule_context(p_user_id IN NUMBER) AS
BEGIN
clear_masking_rule_context;
FOR r IN (
SELECT link.column_id,
CASE WHEN EXISTS (
SELECT 1
FROM cb_user_masking_rule user_rule
WHERE user_rule.user_id = p_user_id
AND user_rule.column_id = link.column_id
AND user_rule.active_yn = 'Y'
AND user_rule.decision = 'UNMASK'
) THEN 'Y' ELSE 'N' END AS can_unmask
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
WHERE rule.enabled_yn = 'Y'
) LOOP
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'MR_' || TO_CHAR(r.column_id), r.can_unmask);
END LOOP;
END;
PROCEDURE clear_user AS
BEGIN
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'USER_ID', NULL);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'EMP_NO', NULL);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE', NULL);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'CAN_READ_CONTENTS', NULL);
clear_stakeholder_context;
clear_masking_rule_context;
END;
PROCEDURE set_user_values(
p_user_id IN NUMBER,
p_emp_no IN VARCHAR2,
p_dept_code IN VARCHAR2,
p_can_read_contents IN VARCHAR2
) AS
BEGIN
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'USER_ID', TO_CHAR(p_user_id));
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'EMP_NO', p_emp_no);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE', p_dept_code);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'CAN_READ_CONTENTS', p_can_read_contents);
clear_stakeholder_context;
clear_masking_rule_context;
END;
PROCEDURE set_stakeholder_context(
p_stakeholder_user_id IN VARCHAR2,
p_role IN VARCHAR2,
p_channel IN VARCHAR2
) AS
v_channel VARCHAR2(50);
BEGIN
v_channel := CASE TRIM(p_channel)
WHEN '설계사채널' THEN '설계사'
WHEN 'GA채널' THEN 'GA'
ELSE TRIM(p_channel)
END;
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID', p_stakeholder_user_id);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_ROLE', p_role);
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_CHANNEL', v_channel);
END;
PROCEDURE set_user(p_user_id IN NUMBER) AS
v_emp_no cb_app_user.employee_no%TYPE;
v_dept_code cb_app_user.dept_code%TYPE;
v_can_read_contents cb_app_user.can_read_contents%TYPE;
BEGIN
SELECT employee_no,
dept_code,
CASE
WHEN EXISTS (
SELECT 1
FROM cb_user_role ur
JOIN cb_permission p ON p.role_id = ur.role_id
JOIN cb_permission_column pc ON pc.permission_id = p.perm_id
WHERE ur.user_id = p_user_id
AND p.target_name = 'CB_V_SEARCH_DOCUMENTS'
AND p.action_name = 'SELECT'
AND pc.column_name = 'CONTENTS'
) THEN 'Y'
ELSE 'N'
END
INTO v_emp_no, v_dept_code, v_can_read_contents
FROM cb_app_user
WHERE user_id = p_user_id
AND active = 'Y';
set_user_values(p_user_id, v_emp_no, v_dept_code, v_can_read_contents);
set_masking_rule_context(p_user_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
clear_user;
RAISE_APPLICATION_ERROR(-20003, 'Mapped application user not found');
END;
PROCEDURE set_user_by_bearer(p_bearer_key IN VARCHAR2) AS
v_user_id cb_app_user.user_id%TYPE;
v_emp_no cb_app_user.employee_no%TYPE;
v_dept_code cb_app_user.dept_code%TYPE;
v_can_read_contents cb_app_user.can_read_contents%TYPE;
v_stakeholder_user_id VARCHAR2(30);
v_stakeholder_role VARCHAR2(50);
v_stakeholder_channel VARCHAR2(50);
BEGIN
IF p_bearer_key IS NULL THEN
clear_user;
RAISE_APPLICATION_ERROR(-20001, 'Authorization Bearer key is required');
END IF;
SELECT u.user_id,
u.employee_no,
u.dept_code,
CASE
WHEN EXISTS (
SELECT 1
FROM cb_user_role ur
JOIN cb_permission p ON p.role_id = ur.role_id
JOIN cb_permission_column pc ON pc.permission_id = p.perm_id
WHERE ur.user_id = u.user_id
AND p.target_name = 'CB_V_SEARCH_DOCUMENTS'
AND p.action_name = 'SELECT'
AND pc.column_name = 'CONTENTS'
) THEN 'Y'
ELSE 'N'
END,
COALESCE(k.stakeholder_user_id, u.stakeholder_user_id)
INTO v_user_id, v_emp_no, v_dept_code, v_can_read_contents, v_stakeholder_user_id
FROM cb_agent_bearer_key k
JOIN cb_app_user u ON u.user_id = k.user_id
WHERE k.key_hash = STANDARD_HASH(p_bearer_key, 'SHA256')
AND k.active = 'Y'
AND k.revoked_at IS NULL
AND (k.expires_at IS NULL OR k.expires_at > SYSDATE)
AND u.active = 'Y';
set_user_values(v_user_id, v_emp_no, v_dept_code, v_can_read_contents);
IF v_stakeholder_user_id IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID', v_stakeholder_user_id);
SELECT role, channel
INTO v_stakeholder_role, v_stakeholder_channel
FROM poc_2.kb_stakeholders
WHERE user_id = v_stakeholder_user_id;
set_stakeholder_context(v_stakeholder_user_id, v_stakeholder_role, v_stakeholder_channel);
END IF;
set_masking_rule_context(v_user_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
clear_user;
RAISE_APPLICATION_ERROR(-20002, 'Invalid or expired Bearer key');
END;
END;
/
SHOW ERRORS PACKAGE BODY cb_agent_ctx_pkg
GRANT EXECUTE ON cb_agent_ctx_pkg TO cb_ords;
PROMPT === Preserving the all-access admin raw-display contract ===
MERGE INTO cb_user_masking_rule dst
USING (
SELECT user_row.user_id, column_rule.column_id
FROM cb_app_user user_row
CROSS JOIN cb_column_masking_rule column_rule
JOIN cb_masking_rule rule ON rule.rule_id = column_rule.rule_id
WHERE user_row.stakeholder_user_id = 'KB_VPD_ADMIN'
AND user_row.active = 'Y'
AND rule.enabled_yn = 'Y'
) src
ON (dst.user_id = src.user_id AND dst.column_id = src.column_id)
WHEN NOT MATCHED THEN
INSERT (user_id, column_id, decision, active_yn, updated_at)
VALUES (src.user_id, src.column_id, 'UNMASK', 'Y', SYSTIMESTAMP);
COMMIT;
PROMPT === Removing stale columns and disabling unconfigured KB policies ===
-- A policy definition is deliberately retained when no column is configured.
-- DISABLE_POLICY makes the effective state match the empty backoffice setting
-- and lets a later column assignment re-enable the same named policy safely.
DECLARE
PROCEDURE reconcile_existing_policy(
p_object_name IN VARCHAR2,
p_policy_name IN VARCHAR2
) IS
v_policy_count NUMBER;
v_configured_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_policy_count
FROM redaction_policies
WHERE object_owner = 'POC_2' AND object_name = p_object_name AND policy_name = p_policy_name;
IF v_policy_count = 0 THEN
RETURN;
END IF;
SELECT COUNT(*) INTO v_configured_count
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
JOIN cb_protected_column column_row ON column_row.column_id = link.column_id
JOIN cb_protected_object object_row ON object_row.object_id = column_row.object_id
WHERE object_row.owner = 'POC_2'
AND object_row.object_name = p_object_name
AND rule.enabled_yn = 'Y';
IF v_configured_count = 0 THEN
DBMS_REDACT.DISABLE_POLICY('POC_2', p_object_name, p_policy_name);
RETURN;
END IF;
FOR stale_column IN (
SELECT policy_column.column_name
FROM redaction_columns policy_column
WHERE policy_column.object_owner = 'POC_2'
AND policy_column.object_name = p_object_name
AND NOT EXISTS (
SELECT 1
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
JOIN cb_protected_column column_row ON column_row.column_id = link.column_id
JOIN cb_protected_object object_row ON object_row.object_id = column_row.object_id
WHERE object_row.owner = 'POC_2'
AND object_row.object_name = p_object_name
AND column_row.column_name = policy_column.column_name
AND rule.enabled_yn = 'Y'
)
) LOOP
DBMS_REDACT.ALTER_POLICY(
object_schema => 'POC_2', object_name => p_object_name, policy_name => p_policy_name,
action => DBMS_REDACT.DROP_COLUMN, column_name => stale_column.column_name
);
END LOOP;
END;
BEGIN
reconcile_existing_policy('KB_CUSTOMERS', 'KB_CUSTOMER_PII_REDACT');
reconcile_existing_policy('KB_CLAIMS', 'KB_CLAIM_AMOUNT_REDACT');
reconcile_existing_policy('KB_CONTRACTS', 'KB_CONTRACT_PREMIUM_REDACT');
reconcile_existing_policy('KB_EXTERNAL_HOLDINGS', 'KB_EXT_HOLDING_REDACT');
END;
/
PROMPT === Integrating configured columns into the existing KB Data Redaction policies ===
DECLARE
v_policy_name VARCHAR2(128);
v_expression_name VARCHAR2(128);
v_expression VARCHAR2(4000);
PROCEDURE require_existing_policy(
p_object_name IN VARCHAR2,
p_policy_name IN VARCHAR2
) IS
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM redaction_policies
WHERE object_owner = 'POC_2'
AND object_name = p_object_name
AND policy_name = p_policy_name;
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20012,
'Existing Data Redaction policy not found: POC_2.' || p_object_name || '.' || p_policy_name);
END IF;
END;
PROCEDURE upsert_expression(
p_expression_name IN VARCHAR2,
p_expression IN VARCHAR2
) IS
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM redaction_expressions
WHERE policy_expression_name = p_expression_name;
IF v_count = 0 THEN
DBMS_REDACT.CREATE_POLICY_EXPRESSION(
policy_expression_name => p_expression_name,
expression => p_expression,
policy_expression_description => 'Mask unless trusted bearer context grants an original-value exception'
);
ELSE
DBMS_REDACT.UPDATE_POLICY_EXPRESSION(
policy_expression_name => p_expression_name,
expression => p_expression,
policy_expression_description => 'Mask unless trusted bearer context grants an original-value exception'
);
END IF;
END;
PROCEDURE modify_column(
p_object_name IN VARCHAR2,
p_policy_name IN VARCHAR2,
p_column_name IN VARCHAR2,
p_template_code IN VARCHAR2
) IS
PROCEDURE apply(
p_function_type IN BINARY_INTEGER,
p_pattern IN VARCHAR2 DEFAULT NULL,
p_replace IN VARCHAR2 DEFAULT NULL
) IS
BEGIN
DBMS_REDACT.ALTER_POLICY(
object_schema => 'POC_2',
object_name => p_object_name,
policy_name => p_policy_name,
action => DBMS_REDACT.MODIFY_COLUMN,
column_name => p_column_name,
function_type => p_function_type,
regexp_pattern => p_pattern,
regexp_replace_string => p_replace
);
END;
BEGIN
CASE p_template_code
WHEN 'NULLIFY' THEN
apply(DBMS_REDACT.NULLIFY);
WHEN 'FULL' THEN
apply(DBMS_REDACT.FULL);
WHEN 'TEXT_PARTIAL' THEN
apply(DBMS_REDACT.REGEXP, '(^.).*$', '\1***');
WHEN 'RRN_PARTIAL' THEN
apply(DBMS_REDACT.REGEXP, '(^[0-9]{6})-?[0-9]{7}$', '\1-*******');
ELSE
RAISE_APPLICATION_ERROR(-20013, 'Unsupported masking template: ' || p_template_code);
END CASE;
END;
BEGIN
FOR configured_column IN (
SELECT protected_object.object_name,
protected_column.column_id,
protected_column.column_name,
masking_rule.template_code
FROM cb_column_masking_rule link
JOIN cb_masking_rule masking_rule ON masking_rule.rule_id = link.rule_id
JOIN cb_protected_column protected_column ON protected_column.column_id = link.column_id
JOIN cb_protected_object protected_object ON protected_object.object_id = protected_column.object_id
WHERE protected_object.owner = 'POC_2'
AND masking_rule.enabled_yn = 'Y'
ORDER BY protected_object.object_name, protected_column.column_id
) LOOP
v_policy_name := CASE configured_column.object_name
WHEN 'KB_CUSTOMERS' THEN 'KB_CUSTOMER_PII_REDACT'
WHEN 'KB_CLAIMS' THEN 'KB_CLAIM_AMOUNT_REDACT'
WHEN 'KB_CONTRACTS' THEN 'KB_CONTRACT_PREMIUM_REDACT'
WHEN 'KB_EXTERNAL_HOLDINGS' THEN 'KB_EXT_HOLDING_REDACT'
ELSE NULL
END;
IF v_policy_name IS NULL THEN
RAISE_APPLICATION_ERROR(-20014,
'No existing KB Data Redaction policy mapping for ' || configured_column.object_name);
END IF;
require_existing_policy(configured_column.object_name, v_policy_name);
DBMS_REDACT.ENABLE_POLICY('POC_2', configured_column.object_name, v_policy_name);
modify_column(
configured_column.object_name,
v_policy_name,
configured_column.column_name,
configured_column.template_code
);
v_expression_name := 'CBMR_' || TO_CHAR(configured_column.column_id);
v_expression := 'SYS_CONTEXT(''CB_AGENT_CTX'', ''MR_' || TO_CHAR(configured_column.column_id)
|| ''') IS NULL OR SYS_CONTEXT(''CB_AGENT_CTX'', ''MR_' || TO_CHAR(configured_column.column_id) || ''') <> ''Y''';
upsert_expression(v_expression_name, v_expression);
DBMS_REDACT.APPLY_POLICY_EXPR_TO_COL(
object_schema => 'POC_2',
object_name => configured_column.object_name,
column_name => configured_column.column_name,
policy_expression_name => v_expression_name
);
END LOOP;
END;
/
COMMIT;
PROMPT === Verification ===
SELECT object_name, policy_name, expression
FROM redaction_policies
WHERE object_owner = 'POC_2'
AND object_name IN ('KB_CUSTOMERS', 'KB_CLAIMS', 'KB_CONTRACTS', 'KB_EXTERNAL_HOLDINGS')
ORDER BY object_name, policy_name;
SELECT object_name, column_name, function_type
FROM redaction_columns
WHERE object_owner = 'POC_2'
AND object_name IN ('KB_CUSTOMERS', 'KB_CLAIMS', 'KB_CONTRACTS', 'KB_EXTERNAL_HOLDINGS')
ORDER BY object_name, column_name;
SELECT policy_expression_name, object_name, column_name, expression
FROM redaction_expressions
WHERE policy_expression_name LIKE 'CBMR_%'
ORDER BY policy_expression_name;
SELECT user_row.user_name,
protected_object.object_name,
protected_column.column_name,
user_rule.decision
FROM cb_user_masking_rule user_rule
JOIN cb_app_user user_row ON user_row.user_id = user_rule.user_id
JOIN cb_protected_column protected_column ON protected_column.column_id = user_rule.column_id
JOIN cb_protected_object protected_object ON protected_object.object_id = protected_column.object_id
ORDER BY user_row.user_name, protected_object.object_name, protected_column.column_name;
EXIT
-- Legacy replacement implementation retained below for migration reference.
-- SQLcl exits above, so it never executes.
PROMPT === Rebuilding managed Oracle Data Redaction policies ===
DECLARE
v_policy_name VARCHAR2(128);
v_expression_name VARCHAR2(128);
v_first BOOLEAN;
PROCEDURE apply_column(
p_first IN BOOLEAN,
p_object_name IN VARCHAR2,
p_policy_name IN VARCHAR2,
p_column_name IN VARCHAR2,
p_template_code IN VARCHAR2
) IS
PROCEDURE first_policy(
p_function_type IN BINARY_INTEGER,
p_pattern IN VARCHAR2 DEFAULT NULL,
p_replace IN VARCHAR2 DEFAULT NULL
) IS
BEGIN
DBMS_REDACT.ADD_POLICY(
object_schema => 'POC_2',
object_name => p_object_name,
policy_name => p_policy_name,
policy_description => 'Managed by CB masking rule backoffice',
column_name => p_column_name,
function_type => p_function_type,
expression => '1=1',
regexp_pattern => p_pattern,
regexp_replace_string => p_replace,
enable => TRUE
);
END;
PROCEDURE additional_column(
p_function_type IN BINARY_INTEGER,
p_pattern IN VARCHAR2 DEFAULT NULL,
p_replace IN VARCHAR2 DEFAULT NULL
) IS
BEGIN
DBMS_REDACT.ALTER_POLICY(
object_schema => 'POC_2',
object_name => p_object_name,
policy_name => p_policy_name,
action => DBMS_REDACT.ADD_COLUMN,
column_name => p_column_name,
function_type => p_function_type,
regexp_pattern => p_pattern,
regexp_replace_string => p_replace
);
END;
PROCEDURE invoke(
p_function_type IN BINARY_INTEGER,
p_pattern IN VARCHAR2 DEFAULT NULL,
p_replace IN VARCHAR2 DEFAULT NULL
) IS
BEGIN
IF p_first THEN
first_policy(p_function_type, p_pattern, p_replace);
ELSE
additional_column(p_function_type, p_pattern, p_replace);
END IF;
END;
BEGIN
CASE p_template_code
WHEN 'NULLIFY' THEN
invoke(DBMS_REDACT.NULLIFY);
WHEN 'FULL' THEN
invoke(DBMS_REDACT.FULL);
WHEN 'TEXT_PARTIAL' THEN
invoke(DBMS_REDACT.REGEXP, '(^.).*$', '\1***');
WHEN 'RRN_PARTIAL' THEN
invoke(DBMS_REDACT.REGEXP, '(^[0-9]{6})-?[0-9]{7}$', '\1-*******');
ELSE
RAISE_APPLICATION_ERROR(-20011, 'Unsupported template: ' || p_template_code);
END CASE;
END;
PROCEDURE drop_managed_policy(p_object_name IN VARCHAR2, p_policy_name IN VARCHAR2) IS
BEGIN
DBMS_REDACT.DROP_POLICY(
object_schema => 'POC_2',
object_name => p_object_name,
policy_name => p_policy_name
);
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
PROCEDURE drop_expression(p_expression_name IN VARCHAR2) IS
BEGIN
DBMS_REDACT.DROP_POLICY_EXPRESSION(p_expression_name);
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
-- These three policies are legacy test policies found on the KB objects.
-- Data Redaction permits only one policy per object, so replace them with
-- the managed policy rather than leaving their client-identifier tests live.
drop_managed_policy('KB_CUSTOMERS', 'KB_CUSTOMER_PII_REDACT');
drop_managed_policy('KB_CLAIMS', 'KB_CLAIM_AMOUNT_REDACT');
drop_managed_policy('KB_EXTERNAL_HOLDINGS', 'KB_EXT_HOLDING_REDACT');
FOR object_row IN (
SELECT DISTINCT po.object_name
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
JOIN cb_protected_column pc ON pc.column_id = link.column_id
JOIN cb_protected_object po ON po.object_id = pc.object_id
WHERE rule.enabled_yn = 'Y'
AND po.owner = 'POC_2'
) LOOP
v_policy_name := 'CB_ASO_' || object_row.object_name || '_MASK';
drop_managed_policy(object_row.object_name, v_policy_name);
END LOOP;
FOR column_row IN (
SELECT link.column_id
FROM cb_column_masking_rule link
) LOOP
drop_expression('CBMR_' || TO_CHAR(column_row.column_id));
END LOOP;
FOR object_row IN (
SELECT DISTINCT po.object_name
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
JOIN cb_protected_column pc ON pc.column_id = link.column_id
JOIN cb_protected_object po ON po.object_id = pc.object_id
WHERE rule.enabled_yn = 'Y'
AND po.owner = 'POC_2'
ORDER BY po.object_name
) LOOP
v_policy_name := 'CB_ASO_' || object_row.object_name || '_MASK';
v_first := TRUE;
FOR column_row IN (
SELECT pc.column_id,
pc.column_name,
rule.template_code,
rule.rule_name
FROM cb_column_masking_rule link
JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
JOIN cb_protected_column pc ON pc.column_id = link.column_id
JOIN cb_protected_object po ON po.object_id = pc.object_id
WHERE po.owner = 'POC_2'
AND po.object_name = object_row.object_name
AND rule.enabled_yn = 'Y'
ORDER BY pc.column_id
) LOOP
apply_column(v_first, object_row.object_name, v_policy_name,
column_row.column_name, column_row.template_code);
v_expression_name := 'CBMR_' || TO_CHAR(column_row.column_id);
DBMS_REDACT.CREATE_POLICY_EXPRESSION(
policy_expression_name => v_expression_name,
expression => 'SYS_CONTEXT(''CB_AGENT_CTX'', ''MR_' || TO_CHAR(column_row.column_id)
|| ''') IS NULL OR SYS_CONTEXT(''CB_AGENT_CTX'', ''MR_' || TO_CHAR(column_row.column_id) || ''') <> ''Y''',
policy_expression_description => 'Mask unless trusted bearer context grants an original-value exception'
);
DBMS_REDACT.APPLY_POLICY_EXPR_TO_COL(
object_schema => 'POC_2',
object_name => object_row.object_name,
column_name => column_row.column_name,
policy_expression_name => v_expression_name
);
v_first := FALSE;
END LOOP;
END LOOP;
END;
/
COMMIT;
PROMPT === Verification ===
SELECT object_name, policy_name, expression
FROM redaction_policies
WHERE object_owner = 'POC_2'
AND policy_name LIKE 'CB_ASO_%'
ORDER BY object_name, policy_name;
SELECT object_name, column_name, function_type
FROM redaction_columns
WHERE object_owner = 'POC_2'
AND object_name IN ('KB_CUSTOMERS', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
ORDER BY object_name, column_name;
SELECT user_row.user_name,
protected_object.object_name,
protected_column.column_name,
user_rule.decision
FROM cb_user_masking_rule user_rule
JOIN cb_app_user user_row ON user_row.user_id = user_rule.user_id
JOIN cb_protected_column protected_column ON protected_column.column_id = user_rule.column_id
JOIN cb_protected_object protected_object ON protected_object.object_id = protected_column.object_id
ORDER BY user_row.user_name, protected_object.object_name, protected_column.column_name;
EXIT