From 28379c47f84c8f85d92065746dedd0c91b210642 Mon Sep 17 00:00:00 2001 From: devmrko Date: Tue, 30 Jun 2026 17:41:11 +0900 Subject: [PATCH] [Developer] #576 clarify verification user names --- .../README.md | 4 +- .../README.md | 35 ++++++++++++++ ...17_agent_ords_security_local_vpd_setup.sql | 6 +-- ...9_agent_ords_default_user_display_name.sql | 47 +++++++++++++++++++ .../resources/templates/effective-matrix.html | 2 +- .../templates/fragments/probe-result.html | 2 +- src/main/resources/templates/users.html | 4 +- .../web/GuidedFlowTemplateTest.java | 14 ++++++ 8 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 docs/design/576-human-readable-verification-users/README.md create mode 100644 sql/adb/39_agent_ords_default_user_display_name.sql diff --git a/docs/design/573-vector-all-role-normalization/README.md b/docs/design/573-vector-all-role-normalization/README.md index 6b0f8d8..736a23e 100644 --- a/docs/design/573-vector-all-role-normalization/README.md +++ b/docs/design/573-vector-all-role-normalization/README.md @@ -29,7 +29,7 @@ ## 3. 인수조건 -- [x] 기본 whitelist 역할 사용자(`agent_hr`, `agent_fin_self`, `agent_all`)의 VPD predicate가 정확히 `1 = 1`이다. +- [x] 기본 whitelist 역할 사용자(`이에이치알`, `박파이넨스`, `김어드민`)의 VPD predicate가 정확히 `1 = 1`이다. - [x] 기본 whitelist 역할의 vector effective SQL에 `REGEXP_LIKE`와 `AND (1 = 1)`이 없다. - [x] 관리자가 TAG rule을 실제로 부여한 역할만 해당 TAG predicate를 포함한다. - [x] 기본 역할 10·20·30의 기존 vector TAG rule은 migration 후 제거되고 ALL rule은 각 한 개만 존재한다. @@ -60,5 +60,5 @@ whitelist role - `38_agent_ords_vector_all_permission_normalization.sql`을 운영 DB에 적용해 기본 role 10·20·30의 legacy TAG rule을 제거하고 `ALL` rule을 보장했다. - ORDS vector handler(29)를 재등록하고 백오피스를 배포했다. - `mvn test`: 59 tests, 0 failures. -- 운영 `/probe` smoke(2026-06-30): `agent_hr(101)`, `agent_fin_self(102)`, `agent_all(103)` 모두 `1 = 1 (ALL: 추가 행 필터 없음)` 및 `WHERE d.embedding IS NOT NULL ORDER BY score`를 반환했다. 세 결과에 `REGEXP_LIKE`와 `AND (1 = 1)`은 없었다. +- 운영 `/probe` smoke(2026-06-30): `이에이치알(101)`, `박파이넨스(102)`, `김어드민(103)` 모두 `1 = 1 (ALL: 추가 행 필터 없음)` 및 `WHERE d.embedding IS NOT NULL ORDER BY score`를 반환했다. 세 결과에 `REGEXP_LIKE`와 `AND (1 = 1)`은 없었다. - 권한 없는 역할의 VPD 함수 경로는 변경하지 않아 기존 `1 = 0` fail-closed 동작을 유지한다. diff --git a/docs/design/576-human-readable-verification-users/README.md b/docs/design/576-human-readable-verification-users/README.md new file mode 100644 index 0000000..d5becbf --- /dev/null +++ b/docs/design/576-human-readable-verification-users/README.md @@ -0,0 +1,35 @@ +# 설계서: 기본 검증 사용자의 사람 이름 표시 (#576) + +> **상태**: Implemented · QA passed +> **작성**: [AI] Architect · **최종수정**: 2026-06-30 +> **추적성** — Redmine: #576 · 구현: `17_agent_ords_security_local_vpd_setup.sql`, `39_agent_ords_default_user_display_name.sql`, 사용자·매트릭스·probe 화면 + +## 목적 + +`agent_all`, `agent_fin_self`, `agent_hr`는 사용자명인데 역할명처럼 읽혀 사용자와 권한 범위를 혼동시킨다. 사용자명은 사람이 식별되는 이름으로 표시하고, 권한 범위는 기존 역할명으로 분리한다. + +| user_id | 이전 사용자명 | 새 사용자명 | 유지되는 역할 | +|---:|---|---|---| +| 101 | `agent_hr` | `이에이치알` | `HR_DEPT_ROLE` | +| 102 | `agent_fin_self` | `박파이넨스` | `FIN_SELF_ROLE` | +| 103 | `agent_all` | `김어드민` | `ALL_DOC_ROLE` | + +## 영향과 안전성 + +- `user_id`를 유지하므로 `CB_AGENT_BEARER_KEY.user_id`, `CB_USER_ROLE`, 그룹 상속, VPD context 및 기존 토큰은 변하지 않는다. +- 운영 DB에는 idempotent rename migration(39)을 적용한다. seed(17)도 같은 이름으로 갱신해 새 환경이 일관되게 생성된다. +- `agent_sales` 등 별도 업무 시나리오 사용자는 이번 범위에서 바꾸지 않는다. + +## 인수조건 + +- [x] 사용자·유효 권한·probe 화면이 새 사용자명을 표시한다. +- [x] role 10·20·30과 user ID 101·102·103의 매핑은 유지된다. +- [x] 기존 발급 토큰으로 VPD/ORDS 조회가 계속 동작한다. +- [x] seed와 운영 migration이 같은 이름을 적용한다. + +## QA 결과 + +- `39_agent_ords_default_user_display_name.sql`을 운영 DB에 적용했다. +- `mvn test`: 60 tests, 0 failures. +- 인증된 사용자 목록·유효 권한 화면에서 새 사용자명과 기존 역할명 분리를 확인했다. +- vector `/probe` smoke: 101=`이에이치알`, 102=`박파이넨스`, 103=`김어드민`으로 표시되며 각각 기존 `1 = 1` VPD predicate, `SCORE` Top-K, `WHERE d.embedding IS NOT NULL ORDER BY score` 결과를 유지했다. diff --git a/sql/adb/17_agent_ords_security_local_vpd_setup.sql b/sql/adb/17_agent_ords_security_local_vpd_setup.sql index c4369ff..1d0be46 100644 --- a/sql/adb/17_agent_ords_security_local_vpd_setup.sql +++ b/sql/adb/17_agent_ords_security_local_vpd_setup.sql @@ -96,9 +96,9 @@ CREATE TABLE cb_agent_bearer_key ( ); PROMPT === 3. Seeding permissions === -INSERT INTO cb_app_user VALUES (101, 'agent_hr', 'E10234', 'HR', 'N', 'Y'); -INSERT INTO cb_app_user VALUES (102, 'agent_fin_self', 'E2001', 'FIN', 'N', 'Y'); -INSERT INTO cb_app_user VALUES (103, 'agent_all', 'E99999', 'HQ', 'Y', 'Y'); +INSERT INTO cb_app_user VALUES (101, '이에이치알', 'E10234', 'HR', 'N', 'Y'); +INSERT INTO cb_app_user VALUES (102, '박파이넨스', 'E2001', 'FIN', 'N', 'Y'); +INSERT INTO cb_app_user VALUES (103, '김어드민', 'E99999', 'HQ', 'Y', 'Y'); INSERT INTO cb_app_role VALUES (10, 'HR_DEPT_ROLE', 'INTERNAL'); INSERT INTO cb_app_role VALUES (20, 'FIN_SELF_ROLE', 'INTERNAL'); diff --git a/sql/adb/39_agent_ords_default_user_display_name.sql b/sql/adb/39_agent_ords_default_user_display_name.sql new file mode 100644 index 0000000..d39f89a --- /dev/null +++ b/sql/adb/39_agent_ords_default_user_display_name.sql @@ -0,0 +1,47 @@ +-- ============================================================ +-- 39_agent_ords_default_user_display_name.sql +-- Give the seeded verification users human-readable display names. +-- User IDs, roles, bearer keys and VPD authorization remain unchanged. +-- ============================================================ +WHENEVER SQLERROR EXIT SQL.SQLCODE +SET ECHO ON +SET FEEDBACK ON +SET DEFINE OFF + +PROMPT === Renaming default verification users === +DECLARE + PROCEDURE rename_user( + p_user_id IN NUMBER, + p_user_name IN VARCHAR2 + ) IS + BEGIN + UPDATE cb_app_user + SET user_name = p_user_name + WHERE user_id = p_user_id; + + IF SQL%ROWCOUNT <> 1 THEN + RAISE_APPLICATION_ERROR(-20839, 'Default verification user not found: ' || p_user_id); + END IF; + END; +BEGIN + rename_user(101, '이에이치알'); -- HR_DEPT_ROLE + rename_user(102, '박파이넨스'); -- FIN_SELF_ROLE + rename_user(103, '김어드민'); -- ALL_DOC_ROLE + COMMIT; +END; +/ + +PROMPT === Verifying renamed users and their roles === +SELECT u.user_id, + u.user_name, + u.employee_no, + u.dept_code, + r.role_name +FROM cb_app_user u +LEFT JOIN cb_user_role ur ON ur.user_id = u.user_id +LEFT JOIN cb_app_role r ON r.role_id = ur.role_id +WHERE u.user_id IN (101, 102, 103) +ORDER BY u.user_id, r.role_id; + +PROMPT === Default verification user display names updated === +EXIT; diff --git a/src/main/resources/templates/effective-matrix.html b/src/main/resources/templates/effective-matrix.html index 86ac610..8cdd8e3 100644 --- a/src/main/resources/templates/effective-matrix.html +++ b/src/main/resources/templates/effective-matrix.html @@ -90,7 +90,7 @@ - agent_hr + 이에이치알
E100 / HR
ACTIVE diff --git a/src/main/resources/templates/fragments/probe-result.html b/src/main/resources/templates/fragments/probe-result.html index 5093706..41273b0 100644 --- a/src/main/resources/templates/fragments/probe-result.html +++ b/src/main/resources/templates/fragments/probe-result.html @@ -125,7 +125,7 @@

이 컨텍스트로 권한 규칙을 계산한 뒤 아래 VPD predicate와 effective SQL을 만들었습니다.

CB_AGENT_CTX.USER_ID
103
-
사용자
agent_all
+
사용자
김어드민
직접 역할
ALL_DOC_ROLE
diff --git a/src/main/resources/templates/users.html b/src/main/resources/templates/users.html index 8a0f606..ba6724e 100644 --- a/src/main/resources/templates/users.html +++ b/src/main/resources/templates/users.html @@ -52,7 +52,7 @@ 1 - agent_hr + 이에이치알 E10234 HR Y @@ -106,7 +106,7 @@ - agent_hr + 이에이치알 HR_DEPT_ROLE
diff --git a/src/test/java/com/cloudhandson/vpdbackoffice/web/GuidedFlowTemplateTest.java b/src/test/java/com/cloudhandson/vpdbackoffice/web/GuidedFlowTemplateTest.java index 0204d92..c0b90e5 100644 --- a/src/test/java/com/cloudhandson/vpdbackoffice/web/GuidedFlowTemplateTest.java +++ b/src/test/java/com/cloudhandson/vpdbackoffice/web/GuidedFlowTemplateTest.java @@ -174,6 +174,20 @@ class GuidedFlowTemplateTest { .doesNotContain("SYS.ODCIVARCHAR2LIST('SPRING_BOOT', 'ORDS')"); } + @Test + void defaultVerificationUsersUseHumanNamesInsteadOfRoleLikeAgentNames() throws IOException { + String seed = Files.readString(Path.of("sql/adb/17_agent_ords_security_local_vpd_setup.sql")); + String users = template("users.html"); + String result = template("fragments/probe-result.html"); + + assertThat(seed) + .contains("(101, '이에이치알'") + .contains("(102, '박파이넨스'") + .contains("(103, '김어드민'"); + assertThat(users).contains("이에이치알").doesNotContain(">agent_hr<"); + assertThat(result).contains("김어드민").doesNotContain(">agent_all<"); + } + @Test void vectorOrdsHandlerReadsBodyStreamOnlyOnce() throws IOException { String sql = Files.readString(Path.of("sql/adb/29_agent_ords_vector_search_ords.sql"));