680 lines
24 KiB
MySQL
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
|