-- ============================================================ -- 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 full-access 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, 'STATIC_SQL', '1 = 1'); -- 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 the only full-access administrator token. UPDATE cb_agent_bearer_key SET active = 'N', revoked_at = SYSDATE WHERE stakeholder_user_id = c_stakeholder_user_id 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 ===