[Developer] #619 focus POC on KB VPD permissions

This commit is contained in:
devmrko
2026-07-07 16:02:22 +09:00
parent ef944a6758
commit 2ef04e6ab2
18 changed files with 1238 additions and 167 deletions

View File

@@ -76,7 +76,7 @@ CREATE TABLE cb_permission_rule (
perm_id NUMBER NOT NULL REFERENCES cb_permission(perm_id),
rule_column VARCHAR2(128),
rule_type VARCHAR2(30) NOT NULL,
rule_value VARCHAR2(100)
rule_value VARCHAR2(1000 CHAR)
);
CREATE TABLE cb_permission_column (

View File

@@ -6,8 +6,9 @@
-- CB_AGENT_CTX.USER_ID -> direct CB_USER_ROLE + group CB_USER_GROUP/CB_GROUP_ROLE
-- -> CB_PERMISSION -> CB_PERMISSION_RULE
-- and builds a row predicate for the object passed by DBMS_RLS.
-- Supported rule types include TAG. TAG matches a token in a comma-delimited
-- TECH_TAG value, allowing the vector search view to remain one row per chunk.
-- A permission's rules are combined with AND. Separate ALLOW permissions are
-- combined with OR and DENY permissions are subtracted at the end. This lets
-- a token-context condition be narrowed by a literal static SQL condition.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
@@ -27,6 +28,10 @@ AS
v_target VARCHAR2(128);
v_allow_predicate VARCHAR2(32767);
v_deny_predicate VARCHAR2(32767);
v_active_permission_id NUMBER;
v_active_permission_effect VARCHAR2(10);
v_permission_predicate VARCHAR2(32767);
v_permission_invalid BOOLEAN := FALSE;
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
@@ -64,7 +69,79 @@ AS
RETURN NULL;
END;
PROCEDURE append_or(p_effect IN VARCHAR2, p_clause IN VARCHAR2) IS
FUNCTION safe_static_predicate(
p_owner IN VARCHAR2,
p_table_name IN VARCHAR2,
p_predicate IN VARCHAR2
) RETURN VARCHAR2 IS
v_predicate VARCHAR2(32767) := TRIM(p_predicate);
v_lexical VARCHAR2(32767);
v_token VARCHAR2(128);
v_index PLS_INTEGER := 1;
v_count NUMBER;
v_has_column BOOLEAN := FALSE;
BEGIN
-- STATIC_SQL is intentionally an expression only: no statement
-- separator/comment/bind/database-link/qualified object is accepted.
-- Every identifier outside a string literal must be a column of the
-- protected object or an allowlisted SQL operator/function.
IF v_predicate IS NULL
OR LENGTH(v_predicate) > 1000
OR INSTR(v_predicate, ';') > 0
OR INSTR(v_predicate, '--') > 0
OR INSTR(v_predicate, '/*') > 0
OR INSTR(v_predicate, '*/') > 0
OR INSTR(v_predicate, ':') > 0
OR INSTR(v_predicate, '@') > 0
OR INSTR(v_predicate, '||') > 0
OR REGEXP_LIKE(v_predicate, '[[:cntrl:]]')
OR MOD(LENGTH(v_predicate) - LENGTH(REPLACE(v_predicate, '''', '')), 2) <> 0 THEN
RETURN NULL;
END IF;
-- Replace SQL string literals before extracting identifiers. Doubled
-- quotes in a literal are supported by the expression below.
v_lexical := REGEXP_REPLACE(v_predicate, '''(''''|[^''])*''', ' ');
IF REGEXP_LIKE(v_lexical, '[[:alpha:]_$#][[:alnum:]_$#]*[[:space:]]*\.') THEN
RETURN NULL;
END IF;
LOOP
v_token := UPPER(REGEXP_SUBSTR(
v_lexical, '[[:alpha:]_$#][[:alnum:]_$#]*', 1, v_index
));
EXIT WHEN v_token IS NULL;
v_index := v_index + 1;
IF v_token IN (
'AND', 'OR', 'NOT', 'NULL', 'IS', 'IN', 'LIKE', 'BETWEEN', 'ESCAPE',
'TRUE', 'FALSE', 'UPPER', 'LOWER', 'TRIM', 'NVL', 'COALESCE',
'TO_CHAR', 'TO_DATE', 'REGEXP_LIKE', 'INSTR', 'LENGTH', 'SUBSTR',
'REPLACE', 'CAST', 'AS', 'DATE', 'TIMESTAMP', 'NUMBER', 'VARCHAR2', 'CHAR'
) THEN
NULL;
ELSE
SELECT COUNT(*)
INTO v_count
FROM all_tab_columns
WHERE owner = UPPER(p_owner)
AND table_name = UPPER(p_table_name)
AND column_name = v_token;
IF v_count = 0 THEN
RETURN NULL;
END IF;
v_has_column := TRUE;
END IF;
END LOOP;
RETURN CASE WHEN v_has_column THEN v_predicate ELSE NULL END;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
PROCEDURE append_permission(p_effect IN VARCHAR2, p_clause IN VARCHAR2) IS
BEGIN
IF p_clause IS NULL THEN
RETURN;
@@ -72,16 +149,39 @@ AS
IF p_effect = 'DENY' THEN
IF v_deny_predicate IS NULL THEN
v_deny_predicate := '(' || p_clause || ')';
v_deny_predicate := p_clause;
ELSE
v_deny_predicate := v_deny_predicate || ' OR (' || p_clause || ')';
v_deny_predicate := '(' || v_deny_predicate || ') OR (' || p_clause || ')';
END IF;
ELSIF v_allow_predicate IS NULL THEN
v_allow_predicate := '(' || p_clause || ')';
v_allow_predicate := p_clause;
ELSE
v_allow_predicate := v_allow_predicate || ' OR (' || p_clause || ')';
v_allow_predicate := '(' || v_allow_predicate || ') OR (' || p_clause || ')';
END IF;
END;
PROCEDURE append_rule(p_clause IN VARCHAR2) IS
BEGIN
IF p_clause IS NULL THEN
v_permission_invalid := TRUE;
RETURN;
END IF;
IF v_permission_predicate IS NULL THEN
v_permission_predicate := p_clause;
ELSE
v_permission_predicate := '(' || v_permission_predicate || ') AND (' || p_clause || ')';
END IF;
END;
PROCEDURE flush_permission IS
BEGIN
IF NOT v_permission_invalid THEN
append_permission(v_active_permission_effect, v_permission_predicate);
END IF;
v_permission_predicate := NULL;
v_permission_invalid := FALSE;
END;
BEGIN
BEGIN
v_user_id := TO_NUMBER(SYS_CONTEXT('CB_AGENT_CTX', 'USER_ID'));
@@ -111,7 +211,8 @@ BEGIN
ON gr.group_id = ug.group_id
WHERE ug.user_id = v_user_id
)
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
SELECT p.perm_id,
UPPER(TRIM(r.rule_type)) AS rule_type,
UPPER(TRIM(r.rule_column)) AS rule_column,
NVL(UPPER(TRIM(p.permission_effect)), 'ALLOW') AS permission_effect,
r.rule_value
@@ -122,148 +223,135 @@ BEGIN
ON r.perm_id = p.perm_id
WHERE p.target_name = v_target
AND p.action_name = 'SELECT'
ORDER BY r.rule_id
ORDER BY p.perm_id, r.rule_id
) LOOP
IF v_active_permission_id IS NULL THEN
v_active_permission_id := r.perm_id;
v_active_permission_effect := r.permission_effect;
ELSIF v_active_permission_id <> r.perm_id THEN
flush_permission;
v_active_permission_id := r.perm_id;
v_active_permission_effect := r.permission_effect;
END IF;
DECLARE
v_column VARCHAR2(128);
v_column VARCHAR2(128);
v_static_predicate VARCHAR2(32767);
BEGIN
IF r.rule_type = 'ALL' THEN
IF r.permission_effect = 'DENY' THEN
RETURN '1 = 0';
END IF;
v_allow_predicate := '(1 = 1)';
append_rule('1 = 1');
ELSIF r.rule_type = 'MY_DEPT' THEN
-- NULL rule_column keeps backward compatibility with the demo seed.
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
append_rule(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
END IF;
ELSIF r.rule_type = 'SELF' THEN
-- NULL rule_column keeps backward compatibility with the demo seed.
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
append_rule(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
END IF;
ELSIF r.rule_type = 'DEPT' THEN
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
append_rule(v_column || ' = ' || quote_literal(r.rule_value));
END IF;
ELSIF r.rule_type = 'EMP_NO' THEN
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
append_rule(v_column || ' = ' || quote_literal(r.rule_value));
END IF;
ELSIF r.rule_type = 'TAG' THEN
-- A TAG rule defaults to the ingestion metadata column TECH_TAG.
-- Multiple ALLOW TAG rows are accumulated by append_or, so any
-- matching tag grants visibility. DENY rows are accumulated
-- separately and removed from the final result.
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'TECH_TAG'));
IF v_column IS NOT NULL
AND r.rule_value IS NOT NULL
AND REGEXP_LIKE(TRIM(r.rule_value), '^[A-Za-z0-9_-]+$') THEN
append_or(
r.permission_effect,
append_rule(
'REGEXP_LIKE(UPPER(' || v_column || '), ' ||
quote_literal('(^|,)' || UPPER(TRIM(r.rule_value)) || '(,|$)') || ')'
);
END IF;
ELSIF r.rule_type IN ('STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL') THEN
-- CB_AGENT_CTX_PKG가 Bearer token -> KB_STAKEHOLDERS를 한 번
-- 해석해 USER_ID/ROLE/CHANNEL을 secure context로 설정한다.
-- 따라서 보호 테이블 predicate에서 KB_STAKEHOLDERS를 다시 조인하지
-- 않고, 해당 context와 KB_CONTRACTS 관계만 사용한다.
ELSIF r.rule_type IN (
'TOKEN_SUBJECT', 'OWN_CONTRACT', 'CHANNEL_CONTRACT', 'OWN_CUSTOMER', 'CHANNEL_CUSTOMER'
) THEN
-- 백오피스 권한 설정의 조건 코드를 안전한 WHERE 조각으로 확장한다.
-- token -> stakeholder 해석은 context package가 한 번만 수행하며,
-- VPD filter는 context와 대상/계약원장만 쓴다.
v_column := safe_column(p_schema, p_object, r.rule_column);
IF v_column IS NOT NULL
AND UPPER(p_schema) = 'POC_2' THEN
DECLARE
v_contract_key VARCHAR2(30);
v_role_condition VARCHAR2(500);
BEGIN
-- 각 ALLOW rule은 append_or로 OR 분기가 된다. rule_value는
-- 권한 설정에 저장된 해당 분기의 역할값이다. identity rule은
-- NULL value를 허용해 모든 토큰 주체가 자기 mapping을 해석한다.
v_role_condition := CASE
WHEN TRIM(r.rule_value) IS NULL THEN '1 = 1'
ELSE 'SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_ROLE'') = '
|| quote_literal(TRIM(r.rule_value))
END;
IF r.rule_type = 'STAKEHOLDER_SELF'
AND v_target = 'KB_CONTRACTS'
AND v_column = 'FC_ID' THEN
append_or(
r.permission_effect,
'(' || v_role_condition ||
' AND FC_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID''))'
);
ELSIF r.rule_type = 'STAKEHOLDER_CHANNEL'
AND v_target = 'KB_CONTRACTS'
AND v_column = 'FC_CHANNEL' THEN
append_or(
r.permission_effect,
'(' || v_role_condition ||
' AND FC_CHANNEL = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_CHANNEL''))'
);
ELSIF r.rule_type = 'STAKEHOLDER_SELF'
AND v_target = 'KB_STAKEHOLDERS'
AND v_column = 'USER_ID' THEN
append_or(
r.permission_effect,
'(' || v_role_condition ||
' AND USER_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID''))'
);
ELSIF v_target IN ('KB_CUSTOMERS', 'KB_COVERAGES', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
AND v_column IN ('CUST_ID', 'CONTRACT_NO') THEN
IF r.rule_type = 'TOKEN_SUBJECT'
AND v_target = 'KB_STAKEHOLDERS'
AND v_column = 'USER_ID' THEN
append_rule(
'USER_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID'')'
);
ELSIF r.rule_type = 'OWN_CONTRACT'
AND v_target = 'KB_CONTRACTS'
AND v_column = 'FC_ID' THEN
append_rule(
'FC_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID'')'
);
ELSIF r.rule_type = 'CHANNEL_CONTRACT'
AND v_target = 'KB_CONTRACTS'
AND v_column = 'FC_CHANNEL' THEN
append_rule(
'FC_CHANNEL = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_CHANNEL'')'
);
ELSIF r.rule_type IN ('OWN_CUSTOMER', 'CHANNEL_CUSTOMER')
AND v_target IN ('KB_CUSTOMERS', 'KB_COVERAGES', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
AND v_column IN ('CUST_ID', 'CONTRACT_NO') THEN
DECLARE
v_contract_key VARCHAR2(30);
v_subject_filter VARCHAR2(500);
BEGIN
v_contract_key := CASE v_column
WHEN 'CUST_ID' THEN 'CUST_ID'
WHEN 'CONTRACT_NO' THEN 'CONTRACT_NO'
END;
IF r.rule_type = 'STAKEHOLDER_SELF' THEN
append_or(
r.permission_effect,
'EXISTS (SELECT 1 FROM (' ||
'SELECT c.' || v_contract_key || ' AS access_key ' ||
'FROM POC_2.KB_CONTRACTS c ' ||
'WHERE ' || v_role_condition ||
' AND c.FC_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID'')) allowed_contract ' ||
'WHERE allowed_contract.access_key = ' || v_column || ')'
);
ELSE
append_or(
r.permission_effect,
'EXISTS (SELECT 1 FROM (' ||
'SELECT c.' || v_contract_key || ' AS access_key ' ||
'FROM POC_2.KB_CONTRACTS c ' ||
'WHERE ' || v_role_condition ||
' AND c.FC_CHANNEL = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_CHANNEL'')) allowed_contract ' ||
'WHERE allowed_contract.access_key = ' || v_column || ')'
);
END IF;
END IF;
END;
v_subject_filter := CASE r.rule_type
WHEN 'OWN_CUSTOMER' THEN
'c.FC_ID = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_USER_ID'')'
ELSE
'c.FC_CHANNEL = SYS_CONTEXT(''CB_AGENT_CTX'', ''STAKEHOLDER_CHANNEL'')'
END;
append_rule(
'EXISTS (SELECT 1 FROM (SELECT c.' || v_contract_key || ' AS access_key ' ||
'FROM POC_2.KB_CONTRACTS c WHERE ' || v_subject_filter ||
') allowed_contract WHERE allowed_contract.access_key = ' || v_column || ')'
);
END;
END IF;
END IF;
ELSIF r.rule_type = 'STATIC_SQL' THEN
v_static_predicate := safe_static_predicate(p_schema, p_object, r.rule_value);
append_rule(v_static_predicate);
ELSIF r.rule_type = '=' THEN
v_column := safe_column(p_schema, p_object, r.rule_column);
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
append_rule('TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
END IF;
ELSIF r.rule_type IN ('!=', '<>') THEN
v_column := safe_column(p_schema, p_object, r.rule_column);
IF v_column IS NOT NULL THEN
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
append_rule('TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
END IF;
ELSE
append_rule(NULL);
END IF;
END;
END LOOP;
flush_permission;
IF v_allow_predicate IS NULL THEN
RETURN '1 = 0';
END IF;
IF v_deny_predicate IS NULL THEN
IF v_allow_predicate = '(1 = 1)' THEN
IF v_allow_predicate = '1 = 1' THEN
RETURN '1 = 1';
END IF;
RETURN '(' || v_allow_predicate || ')';

View File

@@ -67,6 +67,12 @@ BEGIN
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
VALUES (456112, 'rule_test_deny_only', 'E456112', 'HR', 'N', 'Y');
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
VALUES (456113, 'rule_test_static_and', 'E456113', 'HR', 'N', 'Y');
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
VALUES (456114, 'rule_test_static_invalid', 'E456114', 'HR', 'N', 'Y');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456101, 'RULE_TEST_MY_DEPT');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456102, 'RULE_TEST_SELF');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456103, 'RULE_TEST_ALL');
@@ -79,6 +85,8 @@ BEGIN
INSERT INTO cb_app_role(role_id, role_name) VALUES (456110, 'RULE_TEST_TARGET_MISMATCH');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456111, 'RULE_TEST_ALLOW_DENY');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456112, 'RULE_TEST_DENY_ONLY');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456113, 'RULE_TEST_STATIC_AND');
INSERT INTO cb_app_role(role_id, role_name) VALUES (456114, 'RULE_TEST_STATIC_INVALID');
INSERT INTO cb_user_role(user_id, role_id) VALUES (456101, 456101);
INSERT INTO cb_user_role(user_id, role_id) VALUES (456102, 456102);
@@ -92,6 +100,8 @@ BEGIN
INSERT INTO cb_user_role(user_id, role_id) VALUES (456110, 456110);
INSERT INTO cb_user_role(user_id, role_id) VALUES (456111, 456111);
INSERT INTO cb_user_role(user_id, role_id) VALUES (456112, 456112);
INSERT INTO cb_user_role(user_id, role_id) VALUES (456113, 456113);
INSERT INTO cb_user_role(user_id, role_id) VALUES (456114, 456114);
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name)
VALUES (456101, 456101, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
@@ -132,6 +142,12 @@ BEGIN
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
VALUES (456113, 456112, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'DENY');
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
VALUES (456114, 456113, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
VALUES (456115, 456114, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (456101, 456101, NULL, 'MY_DEPT', NULL);
@@ -171,6 +187,15 @@ BEGIN
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (456113, 456113, 'DEPT_CODE', '=', 'HR');
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (456114, 456114, 'DEPT_CODE', '=', 'HR');
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (456115, 456114, NULL, 'STATIC_SQL', 'OWNER_EMP_NO = ''E456113''');
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (456116, 456115, NULL, 'STATIC_SQL', 'DEPT_CODE = ''HR''; 1 = 1');
COMMIT;
END;
/
@@ -260,6 +285,8 @@ BEGIN
assert_contains('ALLOW_ALL_MINUS_DENY_DEPT', 456111, 'AND NOT');
assert_contains('ALLOW_ALL_MINUS_DENY_DEPT_DETAIL', 456111, 'TO_CHAR(DEPT_CODE) = ''HR''');
assert_equals('DENY_ONLY_DENIES', 456112, '1 = 0');
assert_contains('CONTEXT_AND_STATIC_SQL', 456113, 'TO_CHAR(DEPT_CODE) = ''HR'') AND (OWNER_EMP_NO = ''E456113'')');
assert_equals('INVALID_STATIC_SQL_DENIES', 456114, '1 = 0');
cb_agent_ctx_pkg.clear_user;
END;
/

View File

@@ -383,20 +383,18 @@ BEGIN
-- 토큰은 모든 이해당사자에게 발급되지만, 아래 역할이 없는 주체는
-- 권한 레코드가 없으므로 VPD가 1=0으로 fail-closed 된다.
ensure_permission(v_designer_role_id, 'KB_STAKEHOLDERS', 'USER_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_STAKEHOLDERS', 'USER_ID', 'STAKEHOLDER_SELF', '지점장');
ensure_permission(v_designer_role_id, 'KB_PRODUCTS', NULL, 'ALL', NULL);
ensure_permission(v_manager_role_id, 'KB_PRODUCTS', NULL, 'ALL', NULL);
ensure_permission(v_designer_role_id, 'KB_CONTRACTS', 'FC_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_CONTRACTS', 'FC_CHANNEL', 'STAKEHOLDER_CHANNEL', '지점장');
ensure_permission(v_designer_role_id, 'KB_CUSTOMERS', 'CUST_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_CUSTOMERS', 'CUST_ID', 'STAKEHOLDER_CHANNEL', '지점장');
ensure_permission(v_designer_role_id, 'KB_COVERAGES', 'CUST_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_COVERAGES', 'CUST_ID', 'STAKEHOLDER_CHANNEL', '지점장');
ensure_permission(v_designer_role_id, 'KB_CLAIMS', 'CUST_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_CLAIMS', 'CUST_ID', 'STAKEHOLDER_CHANNEL', '지점장');
ensure_permission(v_designer_role_id, 'KB_EXTERNAL_HOLDINGS', 'CUST_ID', 'STAKEHOLDER_SELF', '설계사');
ensure_permission(v_manager_role_id, 'KB_EXTERNAL_HOLDINGS', 'CUST_ID', 'STAKEHOLDER_CHANNEL', '지점장');
ensure_permission(v_designer_role_id, 'KB_CONTRACTS', 'FC_ID', 'OWN_CONTRACT', NULL);
ensure_permission(v_manager_role_id, 'KB_CONTRACTS', 'FC_CHANNEL', 'CHANNEL_CONTRACT', NULL);
ensure_permission(v_designer_role_id, 'KB_CUSTOMERS', 'CUST_ID', 'OWN_CUSTOMER', NULL);
ensure_permission(v_manager_role_id, 'KB_CUSTOMERS', 'CUST_ID', 'CHANNEL_CUSTOMER', NULL);
ensure_permission(v_designer_role_id, 'KB_COVERAGES', 'CUST_ID', 'OWN_CUSTOMER', NULL);
ensure_permission(v_manager_role_id, 'KB_COVERAGES', 'CUST_ID', 'CHANNEL_CUSTOMER', NULL);
ensure_permission(v_designer_role_id, 'KB_CLAIMS', 'CUST_ID', 'OWN_CUSTOMER', NULL);
ensure_permission(v_manager_role_id, 'KB_CLAIMS', 'CUST_ID', 'CHANNEL_CUSTOMER', NULL);
ensure_permission(v_designer_role_id, 'KB_EXTERNAL_HOLDINGS', 'CUST_ID', 'OWN_CUSTOMER', NULL);
ensure_permission(v_manager_role_id, 'KB_EXTERNAL_HOLDINGS', 'CUST_ID', 'CHANNEL_CUSTOMER', NULL);
END;
/
COMMIT;

View File

@@ -81,7 +81,7 @@ BEGIN
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
VALUES (v_permission_id, v_role_id, 'KB_STAKEHOLDERS', 'SELECT', 'ALLOW');
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (cb_permission_rule_seq.NEXTVAL, v_permission_id, 'USER_ID', 'STAKEHOLDER_SELF', NULL);
VALUES (cb_permission_rule_seq.NEXTVAL, v_permission_id, 'USER_ID', 'TOKEN_SUBJECT', NULL);
END;
END;
/

View File

@@ -0,0 +1,137 @@
-- ============================================================
-- 48_kb_permission_condition_codes.sql
--
-- Replaces the first KB example's stakeholder-specific predicate types with
-- policy condition codes. CB_AGENT_DOC_VPD_FILTER expands these codes into
-- the target object's WHERE predicate.
-- Execute as ADMIN after 25 and 46.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
ALTER SESSION DISABLE PARALLEL DML;
PROMPT === 1. Replacing KB example rules with filter condition codes ===
UPDATE cb_permission_rule r
SET rule_type = CASE
WHEN EXISTS (
SELECT 1 FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE p.perm_id = r.perm_id AND ar.role_name = 'KB_DESIGNER_ROLE'
AND p.target_name = 'KB_CONTRACTS'
) THEN 'OWN_CONTRACT'
WHEN EXISTS (
SELECT 1 FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE p.perm_id = r.perm_id AND ar.role_name = 'KB_BRANCH_MANAGER_ROLE'
AND p.target_name = 'KB_CONTRACTS'
) THEN 'CHANNEL_CONTRACT'
WHEN EXISTS (
SELECT 1 FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE p.perm_id = r.perm_id AND ar.role_name = 'KB_DESIGNER_ROLE'
AND p.target_name IN ('KB_CUSTOMERS', 'KB_COVERAGES', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
) THEN 'OWN_CUSTOMER'
WHEN EXISTS (
SELECT 1 FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE p.perm_id = r.perm_id AND ar.role_name = 'KB_BRANCH_MANAGER_ROLE'
AND p.target_name IN ('KB_CUSTOMERS', 'KB_COVERAGES', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
) THEN 'CHANNEL_CUSTOMER'
ELSE r.rule_type
END,
rule_value = NULL
WHERE r.rule_type IN ('STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL')
AND EXISTS (
SELECT 1
FROM cb_permission p
JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE p.perm_id = r.perm_id
AND ar.role_name IN ('KB_DESIGNER_ROLE', 'KB_BRANCH_MANAGER_ROLE')
AND p.target_name IN ('KB_CONTRACTS', 'KB_CUSTOMERS', 'KB_COVERAGES', 'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS')
);
COMMIT;
PROMPT === 2. Keeping token subject access as an identity-only permission ===
DECLARE
v_identity_role_id NUMBER;
v_permission_id NUMBER;
v_count NUMBER;
BEGIN
BEGIN
SELECT role_id INTO v_identity_role_id
FROM cb_app_role
WHERE role_name = 'KB_STAKEHOLDER_IDENTITY_ROLE';
EXCEPTION
WHEN NO_DATA_FOUND THEN
SELECT NVL(MAX(role_id), 0) + 1 INTO v_identity_role_id FROM cb_app_role;
INSERT INTO cb_app_role(role_id, role_name, max_sensitivity_level)
VALUES (v_identity_role_id, 'KB_STAKEHOLDER_IDENTITY_ROLE', 'INTERNAL');
END;
FOR u IN (
SELECT user_id FROM cb_app_user
WHERE stakeholder_user_id IS NOT NULL AND active = 'Y'
) LOOP
SELECT COUNT(*) INTO v_count
FROM cb_user_role
WHERE user_id = u.user_id AND role_id = v_identity_role_id;
IF v_count = 0 THEN
INSERT INTO cb_user_role(user_id, role_id) VALUES (u.user_id, v_identity_role_id);
END IF;
END LOOP;
-- Business roles do not need to grant the stakeholder source row. The
-- identity role alone lets a valid token resolve its own stakeholder row.
DELETE FROM cb_permission_column
WHERE permission_id IN (
SELECT p.perm_id
FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE ar.role_name IN ('KB_DESIGNER_ROLE', 'KB_BRANCH_MANAGER_ROLE')
AND p.target_name = 'KB_STAKEHOLDERS'
);
DELETE FROM cb_permission_rule
WHERE perm_id IN (
SELECT p.perm_id
FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE ar.role_name IN ('KB_DESIGNER_ROLE', 'KB_BRANCH_MANAGER_ROLE')
AND p.target_name = 'KB_STAKEHOLDERS'
);
DELETE FROM cb_permission
WHERE perm_id IN (
SELECT p.perm_id
FROM cb_permission p JOIN cb_app_role ar ON ar.role_id = p.role_id
WHERE ar.role_name IN ('KB_DESIGNER_ROLE', 'KB_BRANCH_MANAGER_ROLE')
AND p.target_name = 'KB_STAKEHOLDERS'
);
BEGIN
SELECT perm_id INTO v_permission_id
FROM cb_permission
WHERE role_id = v_identity_role_id
AND target_name = 'KB_STAKEHOLDERS'
AND action_name = 'SELECT';
EXCEPTION
WHEN NO_DATA_FOUND THEN
SELECT cb_permission_seq.NEXTVAL INTO v_permission_id FROM dual;
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
VALUES (v_permission_id, v_identity_role_id, 'KB_STAKEHOLDERS', 'SELECT', 'ALLOW');
END;
DELETE FROM cb_permission_rule WHERE perm_id = v_permission_id;
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (cb_permission_rule_seq.NEXTVAL, v_permission_id, 'USER_ID', 'TOKEN_SUBJECT', NULL);
END;
/
COMMIT;
PROMPT === 3. Condition codes consumed by the VPD filter ===
SELECT ar.role_name, p.target_name, r.rule_column, r.rule_type
FROM cb_permission p
JOIN cb_app_role ar ON ar.role_id = p.role_id
JOIN cb_permission_rule r ON r.perm_id = p.perm_id
WHERE ar.role_name IN (
'KB_DESIGNER_ROLE', 'KB_BRANCH_MANAGER_ROLE', 'KB_STAKEHOLDER_IDENTITY_ROLE'
)
ORDER BY ar.role_name, p.target_name, r.rule_id;
PROMPT === KB permission condition codes ready ===

View File

@@ -0,0 +1,35 @@
-- ============================================================
-- 49_permission_static_sql_condition.sql
--
-- Makes room for a literal, static WHERE condition attached to an
-- allowlisted permission rule. The VPD function validates the expression
-- again before returning it as part of a predicate.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
PROMPT === Enlarging rule value for static SQL conditions ===
DECLARE
v_data_length NUMBER;
BEGIN
SELECT data_length
INTO v_data_length
FROM user_tab_columns
WHERE table_name = 'CB_PERMISSION_RULE'
AND column_name = 'RULE_VALUE';
IF v_data_length < 1000 THEN
EXECUTE IMMEDIATE
'ALTER TABLE cb_permission_rule MODIFY (rule_value VARCHAR2(1000 CHAR))';
END IF;
END;
/
SELECT column_name, data_type, char_length
FROM user_tab_columns
WHERE table_name = 'CB_PERMISSION_RULE'
AND column_name = 'RULE_VALUE';
PROMPT === Static SQL condition storage ready ===

View File

@@ -0,0 +1,229 @@
-- ============================================================
-- 51_kb_vpd_admin_token.sql
--
-- Creates an application-level KB VPD administrator. This is not an
-- EXEMPT ACCESS POLICY database account: the token is resolved to an
-- application context and the existing VPD policy function returns ALL
-- only because KB_VPD_ADMIN_ROLE has explicit ALL permissions.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
SET SERVEROUTPUT ON
PROMPT === Creating KB VPD administrator, permissions, and one-time token ===
GRANT EXECUTE ON cb_agent_ctx_pkg TO POC_2;
DECLARE
c_stakeholder_user_id CONSTANT VARCHAR2(30) := 'KB_VPD_ADMIN';
c_role_name CONSTANT VARCHAR2(80) := 'KB_VPD_ADMIN_ROLE';
c_token_description CONSTANT VARCHAR2(200) := 'KB VPD admin bootstrap token';
v_admin_role_id NUMBER;
v_identity_role_id NUMBER;
v_app_user_id NUMBER;
v_permission_id NUMBER;
v_bearer_token VARCHAR2(128);
v_count NUMBER;
PROCEDURE ensure_role(
p_role_name IN VARCHAR2,
p_level IN VARCHAR2,
p_role_id OUT NUMBER
) IS
BEGIN
SELECT role_id
INTO p_role_id
FROM cb_app_role
WHERE role_name = p_role_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
SELECT NVL(MAX(role_id), 0) + 1
INTO p_role_id
FROM cb_app_role;
INSERT INTO cb_app_role(role_id, role_name, max_sensitivity_level)
VALUES (p_role_id, p_role_name, p_level);
END;
PROCEDURE ensure_user_role(p_user_id IN NUMBER, p_role_id IN NUMBER) IS
BEGIN
SELECT COUNT(*)
INTO v_count
FROM cb_user_role
WHERE user_id = p_user_id
AND role_id = p_role_id;
IF v_count = 0 THEN
INSERT INTO cb_user_role(user_id, role_id) VALUES (p_user_id, p_role_id);
END IF;
END;
PROCEDURE ensure_all_permission(p_target_name IN VARCHAR2) IS
BEGIN
SELECT MIN(perm_id)
INTO v_permission_id
FROM cb_permission
WHERE role_id = v_admin_role_id
AND target_name = p_target_name
AND action_name = 'SELECT';
IF v_permission_id IS NULL THEN
SELECT cb_permission_seq.NEXTVAL INTO v_permission_id FROM dual;
INSERT INTO cb_permission(
perm_id, role_id, target_name, action_name, permission_effect
) VALUES (
v_permission_id, v_admin_role_id, p_target_name, 'SELECT', 'ALLOW'
);
ELSE
UPDATE cb_permission
SET permission_effect = 'ALLOW'
WHERE perm_id = v_permission_id;
END IF;
DELETE FROM cb_permission_rule WHERE perm_id = v_permission_id;
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
VALUES (cb_permission_rule_seq.NEXTVAL, v_permission_id, NULL, 'ALL', NULL);
-- CB_ORDS is the non-exempt data runtime. VPD still applies after
-- this object privilege is granted.
EXECUTE IMMEDIATE
'GRANT SELECT ON POC_2.' || DBMS_ASSERT.SIMPLE_SQL_NAME(p_target_name) || ' TO CB_ORDS';
END;
PROCEDURE allow_original_column(
p_target_name IN VARCHAR2,
p_column_name IN VARCHAR2
) IS
BEGIN
SELECT MIN(perm_id)
INTO v_permission_id
FROM cb_permission
WHERE role_id = v_admin_role_id
AND target_name = p_target_name
AND action_name = 'SELECT';
SELECT COUNT(*)
INTO v_count
FROM cb_permission_column
WHERE permission_id = v_permission_id
AND column_name = p_column_name;
IF v_count = 0 THEN
INSERT INTO cb_permission_column(permission_id, column_name)
VALUES (v_permission_id, p_column_name);
END IF;
END;
BEGIN
MERGE INTO poc_2.kb_stakeholders target
USING (
SELECT c_stakeholder_user_id AS user_id,
'VPD 관리자' AS user_nm,
'관리자' AS role,
'관리자채널' AS channel,
'전체 KB 원장 및 민감 컬럼 접근' AS access_scope,
'권한 테이블의 KB_VPD_ADMIN_ROLE / ALL 규칙으로 적용' AS rls_filter
FROM dual
) source
ON (target.user_id = source.user_id)
WHEN MATCHED THEN UPDATE SET
target.user_nm = source.user_nm,
target.role = source.role,
target.channel = source.channel,
target.access_scope = source.access_scope,
target.rls_filter = source.rls_filter
WHEN NOT MATCHED THEN INSERT (
user_id, user_nm, role, channel, access_scope, rls_filter
) VALUES (
source.user_id, source.user_nm, source.role, source.channel,
source.access_scope, source.rls_filter
);
BEGIN
SELECT user_id
INTO v_app_user_id
FROM cb_app_user
WHERE stakeholder_user_id = c_stakeholder_user_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
SELECT NVL(MAX(user_id), 0) + 1 INTO v_app_user_id FROM cb_app_user;
INSERT INTO cb_app_user(
user_id, user_name, employee_no, dept_code,
stakeholder_user_id, can_read_contents, active
) VALUES (
v_app_user_id, 'VPD 관리자', c_stakeholder_user_id, '관리자채널',
c_stakeholder_user_id, 'Y', 'Y'
);
END;
UPDATE cb_app_user
SET user_name = 'VPD 관리자',
employee_no = c_stakeholder_user_id,
dept_code = '관리자채널',
can_read_contents = 'Y',
active = 'Y'
WHERE user_id = v_app_user_id;
ensure_role(c_role_name, 'RESTRICTED', v_admin_role_id);
ensure_role('KB_STAKEHOLDER_IDENTITY_ROLE', 'INTERNAL', v_identity_role_id);
ensure_user_role(v_app_user_id, v_admin_role_id);
ensure_user_role(v_app_user_id, v_identity_role_id);
FOR object_name IN (
SELECT column_value
FROM TABLE(sys.odcivarchar2list(
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
))
) LOOP
ensure_all_permission(object_name.column_value);
END LOOP;
allow_original_column('KB_CUSTOMERS', 'CUST_NM');
allow_original_column('KB_CUSTOMERS', 'RRN_MASKED');
allow_original_column('KB_CLAIMS', 'CLAIM_AMT');
allow_original_column('KB_CLAIMS', 'PAID_AMT');
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_INSURER');
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_GRP');
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_TYPE');
-- Re-running this script rotates only the bootstrap administrator token.
UPDATE cb_agent_bearer_key
SET active = 'N',
revoked_at = SYSDATE
WHERE stakeholder_user_id = c_stakeholder_user_id
AND description = c_token_description
AND active = 'Y';
v_bearer_token := 'kbvpdadm_' || DBMS_RANDOM.STRING('x', 48);
INSERT INTO cb_agent_bearer_key(
key_id, user_id, stakeholder_user_id, key_prefix, key_hash,
issued_at, expires_at, revoked_at, active, description
) VALUES (
cb_agent_bearer_key_seq.NEXTVAL, v_app_user_id, c_stakeholder_user_id,
'kbvpdadm', STANDARD_HASH(v_bearer_token, 'SHA256'),
SYSDATE, ADD_MONTHS(SYSDATE, 12), NULL, 'Y', c_token_description
);
COMMIT;
-- This is the application step that binds the Bearer Token to the trusted
-- context. DBMS_RLS then invokes CB_AGENT_DOC_VPD_FILTER automatically.
cb_agent_ctx_pkg.set_user_by_bearer(v_bearer_token);
DBMS_OUTPUT.PUT_LINE('STAKEHOLDER_USER_ID='
|| SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID'));
DBMS_OUTPUT.PUT_LINE('EFFECTIVE_ROLE=' || c_role_name);
DBMS_OUTPUT.PUT_LINE('KB_CUSTOMERS_PREDICATE='
|| cb_agent_doc_vpd_filter('POC_2', 'KB_CUSTOMERS'));
DBMS_OUTPUT.PUT_LINE('KB_CLAIMS_PREDICATE='
|| cb_agent_doc_vpd_filter('POC_2', 'KB_CLAIMS'));
DBMS_OUTPUT.PUT_LINE('KB_EXTERNAL_HOLDINGS_PREDICATE='
|| cb_agent_doc_vpd_filter('POC_2', 'KB_EXTERNAL_HOLDINGS'));
cb_agent_ctx_pkg.clear_user;
-- Show once. The raw token is never stored; only its SHA-256 hash remains.
DBMS_OUTPUT.PUT_LINE('BEARER_TOKEN=' || v_bearer_token);
END;
/
PROMPT === KB VPD administrator token ready ===

View File

@@ -0,0 +1,223 @@
-- ============================================================
-- 52_cleanup_to_kb_vpd_poc.sql
--
-- Removes legacy document/Vector/DDS/test examples and keeps only
-- the KB structured-data VPD POC:
-- POC_2.KB_* seven ledgers, stakeholder identity, KB roles,
-- bearer-token permission metadata, and their VPD policies.
--
-- Oracle-maintained ANNOTATIONS_* / VECTOR$* auxiliary objects are
-- not dropped directly. Their owning RAG/vector tables are dropped.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
SET SERVEROUTPUT ON
ALTER SESSION DISABLE PARALLEL DML;
PROMPT === 1. Removing document, legacy role, and test permissions ===
DECLARE
c_role_keep_1 CONSTANT VARCHAR2(80) := 'KB_DESIGNER_ROLE';
c_role_keep_2 CONSTANT VARCHAR2(80) := 'KB_BRANCH_MANAGER_ROLE';
c_role_keep_3 CONSTANT VARCHAR2(80) := 'KB_STAKEHOLDER_IDENTITY_ROLE';
c_role_keep_4 CONSTANT VARCHAR2(80) := 'KB_VPD_ADMIN_ROLE';
BEGIN
DELETE FROM cb_permission_column
WHERE permission_id IN (
SELECT p.perm_id
FROM cb_permission p
JOIN cb_app_role r ON r.role_id = p.role_id
WHERE r.role_name NOT IN (c_role_keep_1, c_role_keep_2, c_role_keep_3, c_role_keep_4)
OR p.target_name NOT IN (
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
)
);
DELETE FROM cb_permission_rule
WHERE perm_id IN (
SELECT p.perm_id
FROM cb_permission p
JOIN cb_app_role r ON r.role_id = p.role_id
WHERE r.role_name NOT IN (c_role_keep_1, c_role_keep_2, c_role_keep_3, c_role_keep_4)
OR p.target_name NOT IN (
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
)
);
DELETE FROM cb_permission
WHERE perm_id IN (
SELECT p.perm_id
FROM cb_permission p
JOIN cb_app_role r ON r.role_id = p.role_id
WHERE r.role_name NOT IN (c_role_keep_1, c_role_keep_2, c_role_keep_3, c_role_keep_4)
OR p.target_name NOT IN (
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
)
);
DELETE FROM cb_user_role
WHERE role_id IN (
SELECT role_id
FROM cb_app_role
WHERE role_name NOT IN (c_role_keep_1, c_role_keep_2, c_role_keep_3, c_role_keep_4)
);
DELETE FROM cb_group_role;
DELETE FROM cb_user_group;
DELETE FROM cb_app_group;
DELETE FROM cb_app_role
WHERE role_name NOT IN (c_role_keep_1, c_role_keep_2, c_role_keep_3, c_role_keep_4);
-- Keep only tokens attached to an actual KB stakeholder.
DELETE FROM cb_agent_bearer_key
WHERE stakeholder_user_id IS NULL;
-- Non-stakeholder users were document/test sample principals.
DELETE FROM cb_app_user
WHERE stakeholder_user_id IS NULL;
DELETE FROM cb_vpd_filter_note;
DELETE FROM cb_vpd_policy_note;
DELETE FROM cb_ords_probe_audit;
DELETE FROM cb_protected_column
WHERE object_id IN (
SELECT object_id
FROM cb_protected_object
WHERE NOT (
owner = 'POC_2'
AND object_name IN (
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
)
)
);
DELETE FROM cb_protected_object
WHERE NOT (
owner = 'POC_2'
AND object_name IN (
'KB_CUSTOMERS', 'KB_PRODUCTS', 'KB_CONTRACTS', 'KB_COVERAGES',
'KB_CLAIMS', 'KB_EXTERNAL_HOLDINGS', 'KB_STAKEHOLDERS'
)
);
COMMIT;
END;
/
PROMPT === 2. Removing legacy document VPD/redaction and source objects ===
BEGIN
DBMS_REDACT.DROP_POLICY(
object_schema => 'ADMIN',
object_name => 'CB_V_SEARCH_DOCUMENTS',
policy_name => 'CB_CONTENTS_REDACT'
);
EXCEPTION
WHEN OTHERS THEN
-- The policy may already be absent in a reduced POC schema.
DBMS_OUTPUT.PUT_LINE('redaction policy not removed: ' || SQLERRM);
END;
/
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM all_policies
WHERE object_owner = 'ADMIN'
AND object_name = 'CB_V_SEARCH_DOCUMENTS'
AND policy_name = 'CB_AGENT_DOC_POLICY';
IF v_count > 0 THEN
DBMS_RLS.DROP_POLICY(
object_schema => 'ADMIN',
object_name => 'CB_V_SEARCH_DOCUMENTS',
policy_name => 'CB_AGENT_DOC_POLICY'
);
END IF;
END;
/
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW cb_v_search_documents';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE cb_search_documents PURGE';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
PROMPT === 3. Dropping unrelated POC_2 backup, RAG, and security-example tables ===
DECLARE
PROCEDURE drop_table_if_exists(p_table_name IN VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE
'DROP TABLE POC_2.' || DBMS_ASSERT.SIMPLE_SQL_NAME(p_table_name)
|| ' CASCADE CONSTRAINTS PURGE';
DBMS_OUTPUT.PUT_LINE('dropped POC_2.' || p_table_name);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
NULL;
ELSE
RAISE;
END IF;
END;
BEGIN
drop_table_if_exists('BKP_CLM_20260701144052');
drop_table_if_exists('BKP_CONT_20260701144052');
drop_table_if_exists('BKP_COV_20260701144052');
drop_table_if_exists('BKP_CUST_20260701144052');
drop_table_if_exists('BKP_EXT_20260701144052');
drop_table_if_exists('BKP_PROD_20260701144052');
drop_table_if_exists('KB_CUST_BAK_202607011343');
drop_table_if_exists('KB_CUST_BAK_20260701134439');
drop_table_if_exists('KB_AIDP_RAG_CORPUS_CONFIG');
drop_table_if_exists('KB_AIDP_RAG_QUERY_CACHE');
drop_table_if_exists('KB_COMPETITOR_TERMS_CHUNKS');
drop_table_if_exists('KB_COMPETITOR_TERMS_DOCUMENTS');
drop_table_if_exists('KB_OWN_TERMS_CHUNKS');
drop_table_if_exists('KB_OWN_TERMS_DOCUMENTS');
drop_table_if_exists('KB_SECURITY_AUDIT_LOG');
drop_table_if_exists('KB_SECURITY_POLICY_CATALOG');
END;
/
PROMPT === 4. Remaining KB VPD POC inventory ===
SELECT object_name
FROM cb_protected_object
WHERE owner = 'POC_2'
ORDER BY object_name;
SELECT role_name
FROM cb_app_role
ORDER BY role_name;
SELECT key_id, stakeholder_user_id, key_prefix, active, expires_at
FROM cb_agent_bearer_key
ORDER BY key_id;
SELECT object_owner, object_name, policy_name, function, enable
FROM all_policies
WHERE object_owner = 'POC_2'
ORDER BY object_name, policy_name;
PROMPT === KB VPD POC cleanup complete ===

View File

@@ -0,0 +1,101 @@
-- ============================================================
-- 53_drop_legacy_poc_objects.sql
-- Completes the physical-object portion of the KB VPD POC cleanup
-- after 52 has removed legacy permissions, roles, tokens and users.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
SET SERVEROUTPUT ON
PROMPT === Removing legacy document VPD/redaction and source objects ===
BEGIN
DBMS_REDACT.DROP_POLICY(
object_schema => 'ADMIN',
object_name => 'CB_V_SEARCH_DOCUMENTS',
policy_name => 'CB_CONTENTS_REDACT'
);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('redaction policy not removed: ' || SQLERRM);
END;
/
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM all_policies
WHERE object_owner = 'ADMIN'
AND object_name = 'CB_V_SEARCH_DOCUMENTS'
AND policy_name = 'CB_AGENT_DOC_POLICY';
IF v_count > 0 THEN
DBMS_RLS.DROP_POLICY(
object_schema => 'ADMIN',
object_name => 'CB_V_SEARCH_DOCUMENTS',
policy_name => 'CB_AGENT_DOC_POLICY'
);
END IF;
END;
/
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW cb_v_search_documents';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE cb_search_documents PURGE';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
PROMPT === Dropping unrelated POC_2 backup, RAG, and security-example tables ===
DECLARE
PROCEDURE drop_table_if_exists(p_table_name IN VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE
'DROP TABLE POC_2.' || DBMS_ASSERT.SIMPLE_SQL_NAME(p_table_name)
|| ' CASCADE CONSTRAINTS PURGE';
DBMS_OUTPUT.PUT_LINE('dropped POC_2.' || p_table_name);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
NULL;
ELSE
RAISE;
END IF;
END;
BEGIN
drop_table_if_exists('BKP_CLM_20260701144052');
drop_table_if_exists('BKP_CONT_20260701144052');
drop_table_if_exists('BKP_COV_20260701144052');
drop_table_if_exists('BKP_CUST_20260701144052');
drop_table_if_exists('BKP_EXT_20260701144052');
drop_table_if_exists('BKP_PROD_20260701144052');
drop_table_if_exists('KB_CUST_BAK_202607011343');
drop_table_if_exists('KB_CUST_BAK_20260701134439');
drop_table_if_exists('KB_AIDP_RAG_CORPUS_CONFIG');
drop_table_if_exists('KB_AIDP_RAG_QUERY_CACHE');
drop_table_if_exists('KB_COMPETITOR_TERMS_CHUNKS');
drop_table_if_exists('KB_COMPETITOR_TERMS_DOCUMENTS');
drop_table_if_exists('KB_OWN_TERMS_CHUNKS');
drop_table_if_exists('KB_OWN_TERMS_DOCUMENTS');
drop_table_if_exists('KB_SECURITY_AUDIT_LOG');
drop_table_if_exists('KB_SECURITY_POLICY_CATALOG');
END;
/
PROMPT === Legacy physical objects removed ===

View File

@@ -0,0 +1,47 @@
-- ============================================================
-- 54_drop_legacy_dds_ords_examples.sql
-- Removes only the former document-search ORDS/DDS example objects.
-- It deliberately keeps CB_ORDS because it is the non-exempt KB VPD
-- runtime principal.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
SET SERVEROUTPUT ON
BEGIN
ORDS.DELETE_MODULE(p_module_name => 'cb.agent.security');
DBMS_OUTPUT.PUT_LINE('dropped ORDS module cb.agent.security');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ORDS module not removed: ' || SQLERRM);
END;
/
DECLARE
PROCEDURE drop_ddl_if_present(p_ddl IN VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE p_ddl;
DBMS_OUTPUT.PUT_LINE('dropped: ' || p_ddl);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('not present: ' || p_ddl);
END;
BEGIN
drop_ddl_if_present('DROP DATA GRANT admin.cb_dg_hr_docs');
drop_ddl_if_present('DROP DATA GRANT admin.cb_dg_fin_docs');
drop_ddl_if_present('DROP DATA GRANT admin.cb_dg_all_docs');
drop_ddl_if_present('DROP END USER "cb_dds_hr"');
drop_ddl_if_present('DROP END USER "cb_dds_fin"');
drop_ddl_if_present('DROP END USER "cb_dds_all"');
drop_ddl_if_present('DROP END USER "cb_dds_none"');
drop_ddl_if_present('DROP DATA ROLE cb_dds_hr_role');
drop_ddl_if_present('DROP DATA ROLE cb_dds_fin_role');
drop_ddl_if_present('DROP DATA ROLE cb_dds_all_role');
drop_ddl_if_present('DROP DATA ROLE cb_dds_connect_only_role');
drop_ddl_if_present('DROP ROLE cb_dds_connect_role');
END;
/
PROMPT === Legacy DDS/ORDS examples removed ===