[DBA] add KB VPD admin full access condition
This commit is contained in:
@@ -99,6 +99,14 @@ AS
|
|||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
-- The unconditional predicate is a deliberately narrow exception to the
|
||||||
|
-- column-reference rule below. It is used by the explicit KB VPD admin
|
||||||
|
-- permission and is rendered by the backoffice as the physical condition
|
||||||
|
-- "1 = 1", not as an implicit bypass.
|
||||||
|
IF REGEXP_LIKE(v_predicate, '^1[[:space:]]*=[[:space:]]*1$') THEN
|
||||||
|
RETURN '1 = 1';
|
||||||
|
END IF;
|
||||||
|
|
||||||
-- Replace SQL string literals before extracting identifiers. Doubled
|
-- Replace SQL string literals before extracting identifiers. Doubled
|
||||||
-- quotes in a literal are supported by the expression below.
|
-- quotes in a literal are supported by the expression below.
|
||||||
v_lexical := REGEXP_REPLACE(v_predicate, '''(''''|[^''])*''', ' ');
|
v_lexical := REGEXP_REPLACE(v_predicate, '''(''''|[^''])*''', ' ');
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ GRANT EXECUTE ON cb_agent_ctx_pkg TO POC_2;
|
|||||||
DECLARE
|
DECLARE
|
||||||
c_stakeholder_user_id CONSTANT VARCHAR2(30) := 'KB_VPD_ADMIN';
|
c_stakeholder_user_id CONSTANT VARCHAR2(30) := 'KB_VPD_ADMIN';
|
||||||
c_role_name CONSTANT VARCHAR2(80) := 'KB_VPD_ADMIN_ROLE';
|
c_role_name CONSTANT VARCHAR2(80) := 'KB_VPD_ADMIN_ROLE';
|
||||||
c_token_description CONSTANT VARCHAR2(200) := 'KB VPD admin bootstrap token';
|
c_token_description CONSTANT VARCHAR2(200) := 'KB VPD admin full-access token';
|
||||||
|
|
||||||
v_admin_role_id NUMBER;
|
v_admin_role_id NUMBER;
|
||||||
v_identity_role_id NUMBER;
|
v_identity_role_id NUMBER;
|
||||||
@@ -83,7 +83,7 @@ DECLARE
|
|||||||
|
|
||||||
DELETE FROM cb_permission_rule WHERE perm_id = v_permission_id;
|
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)
|
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);
|
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
|
-- CB_ORDS is the non-exempt data runtime. VPD still applies after
|
||||||
-- this object privilege is granted.
|
-- this object privilege is granted.
|
||||||
@@ -187,12 +187,11 @@ BEGIN
|
|||||||
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_GRP');
|
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_GRP');
|
||||||
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_TYPE');
|
allow_original_column('KB_EXTERNAL_HOLDINGS', 'EXT_PRODUCT_TYPE');
|
||||||
|
|
||||||
-- Re-running this script rotates only the bootstrap administrator token.
|
-- Re-running this script rotates the only full-access administrator token.
|
||||||
UPDATE cb_agent_bearer_key
|
UPDATE cb_agent_bearer_key
|
||||||
SET active = 'N',
|
SET active = 'N',
|
||||||
revoked_at = SYSDATE
|
revoked_at = SYSDATE
|
||||||
WHERE stakeholder_user_id = c_stakeholder_user_id
|
WHERE stakeholder_user_id = c_stakeholder_user_id
|
||||||
AND description = c_token_description
|
|
||||||
AND active = 'Y';
|
AND active = 'Y';
|
||||||
|
|
||||||
v_bearer_token := 'kbvpdadm_' || DBMS_RANDOM.STRING('x', 48);
|
v_bearer_token := 'kbvpdadm_' || DBMS_RANDOM.STRING('x', 48);
|
||||||
|
|||||||
198
sql/adb/60_kb_vpd_admin_full_access_token.sql
Normal file
198
sql/adb/60_kb_vpd_admin_full_access_token.sql
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 60_kb_vpd_admin_full_access_token.sql
|
||||||
|
--
|
||||||
|
-- Creates the KB_VPD_ADMIN application user and grants the explicit
|
||||||
|
-- physical condition STATIC_SQL / 1 = 1 for every KB protected object.
|
||||||
|
--
|
||||||
|
-- Execute as ADMIN using SQLcl with one positional argument: the newly
|
||||||
|
-- generated Bearer token. Do not print or commit that argument.
|
||||||
|
-- ============================================================
|
||||||
|
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||||
|
SET ECHO OFF
|
||||||
|
SET FEEDBACK ON
|
||||||
|
SET DEFINE ON
|
||||||
|
SET VERIFY OFF
|
||||||
|
SET SERVEROUTPUT ON
|
||||||
|
|
||||||
|
DEFINE bearer_token = '&1'
|
||||||
|
|
||||||
|
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_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_full_access(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'
|
||||||
|
);
|
||||||
|
|
||||||
|
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,
|
||||||
|
'모든 보호 객체에 STATIC_SQL / 1 = 1 적용' 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_STAKEHOLDERS', 'KB_CLAIMS', 'KB_CONTRACTS', 'KB_COVERAGES',
|
||||||
|
'KB_CUSTOMERS', 'KB_EXTERNAL_HOLDINGS', 'KB_PRODUCTS'
|
||||||
|
))
|
||||||
|
) LOOP
|
||||||
|
ensure_full_access(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');
|
||||||
|
|
||||||
|
UPDATE cb_agent_bearer_key
|
||||||
|
SET active = 'N', revoked_at = SYSDATE
|
||||||
|
WHERE stakeholder_user_id = c_stakeholder_user_id
|
||||||
|
AND active = 'Y';
|
||||||
|
|
||||||
|
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('&&bearer_token', 'SHA256'),
|
||||||
|
SYSDATE, ADD_MONTHS(SYSDATE, 12), NULL, 'Y', c_token_description
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
DBMS_OUTPUT.PUT_LINE('STAKEHOLDER_USER_ID=' || c_stakeholder_user_id);
|
||||||
|
DBMS_OUTPUT.PUT_LINE('ROLE_NAME=' || c_role_name);
|
||||||
|
DBMS_OUTPUT.PUT_LINE('OBJECT_PERMISSION_COUNT=7');
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
|
||||||
|
UNDEFINE bearer_token
|
||||||
|
EXIT
|
||||||
@@ -281,6 +281,13 @@ public class PermissionService {
|
|||||||
throw new AppException("정적 SQL 조건에는 다른 테이블·스키마를 참조할 수 없습니다.");
|
throw new AppException("정적 SQL 조건에는 다른 테이블·스키마를 참조할 수 없습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The VPD function makes the same narrowly-scoped exception. It lets an
|
||||||
|
// administrator express the auditable physical condition "1 = 1" for a
|
||||||
|
// full-access permission instead of relying on an implicit ALL marker.
|
||||||
|
if (predicate.matches("(?i)^1\\s*=\\s*1$")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasObjectColumn = false;
|
boolean hasObjectColumn = false;
|
||||||
Matcher matcher = SQL_IDENTIFIER.matcher(lexical.toUpperCase(Locale.ROOT));
|
Matcher matcher = SQL_IDENTIFIER.matcher(lexical.toUpperCase(Locale.ROOT));
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.cloudhandson.vpdbackoffice.service;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.assertj.core.groups.Tuple.tuple;
|
||||||
|
|
||||||
import com.cloudhandson.vpdbackoffice.domain.audit.AuditEvent;
|
import com.cloudhandson.vpdbackoffice.domain.audit.AuditEvent;
|
||||||
import com.cloudhandson.vpdbackoffice.domain.permission.AppRole;
|
import com.cloudhandson.vpdbackoffice.domain.permission.AppRole;
|
||||||
@@ -271,6 +272,25 @@ class PermissionServiceTest {
|
|||||||
.containsExactly("", "DEPT_CODE IN ('HR', 'FIN')");
|
.containsExactly("", "DEPT_CODE IN ('HR', 'FIN')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acceptsExplicitUnconditionalStaticSqlPredicateForAdminRole() {
|
||||||
|
var command = new PermissionSetCommand(
|
||||||
|
10L,
|
||||||
|
1L,
|
||||||
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
|
List.of(new RuleCommand(null, "STATIC_SQL", "1 = 1")),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
|
||||||
|
permissionService.savePermissionSet(command);
|
||||||
|
|
||||||
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
||||||
|
assertThat(mapper.insertedRules)
|
||||||
|
.extracting(PermissionRule::ruleType, PermissionRule::ruleValue)
|
||||||
|
.containsExactly(tuple("STATIC_SQL", "1 = 1"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsStaticSqlStatementOrUnknownColumn() {
|
void rejectsStaticSqlStatementOrUnknownColumn() {
|
||||||
var statementCommand = new PermissionSetCommand(
|
var statementCommand = new PermissionSetCommand(
|
||||||
|
|||||||
Reference in New Issue
Block a user