From eb74a99a5ac970d6118e0467451aab2eefb559b4 Mon Sep 17 00:00:00 2001
From: Rinjae
Date: Wed, 5 Aug 2026 17:47:37 +0900
Subject: [PATCH] =?UTF-8?q?-=20PersonalDataEncryptConverter:=20`isFakeMode?=
=?UTF-8?q?`=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20-=20Lega?=
=?UTF-8?q?cyEncryptionMigration:=20=ED=97=88=EC=9A=A9=20IP=20=EA=B2=80?=
=?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20-=20?=
=?UTF-8?q?=EC=8A=A4=ED=82=B5=20=EB=82=B4=EC=97=AD=20=EB=A1=9C=EA=B7=B8=20?=
=?UTF-8?q?=EA=B0=95=ED=99=94=20-=20=EC=95=94=ED=98=B8=ED=99=94=20?=
=?UTF-8?q?=EB=8C=80=EC=83=81=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20?=
=?UTF-8?q?=EB=B0=8F=20DamoMode=20=EA=B5=AC=EB=B6=84=20=EC=A7=80=EC=9B=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../LegacyEncryptionMigrationController.java | 104 ++++++++++++++----
1 file changed, 83 insertions(+), 21 deletions(-)
diff --git a/src/main/java/com/eactive/apim/portal/common/migration/LegacyEncryptionMigrationController.java b/src/main/java/com/eactive/apim/portal/common/migration/LegacyEncryptionMigrationController.java
index c539cbc..c9d4680 100644
--- a/src/main/java/com/eactive/apim/portal/common/migration/LegacyEncryptionMigrationController.java
+++ b/src/main/java/com/eactive/apim/portal/common/migration/LegacyEncryptionMigrationController.java
@@ -2,6 +2,7 @@ package com.eactive.apim.portal.common.migration;
import com.eactive.apim.portal.common.util.StringMaskingUtil;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
+import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
@@ -20,6 +21,8 @@ import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* [임시] 레거시 평문 데이터를 {@link PersonalDataEncryptConverter} 규칙으로 일괄 정규화(암호화)하는 운영 도구.
@@ -29,15 +32,19 @@ import java.util.Map;
* 쓰기에서 무조건 인코딩하므로, {@code convertToDatabaseColumn(convertToEntityAttribute(x))}는
* 평문→인코딩, 인코딩→동일값(멱등)으로 정규화된다. 이 값이 기존과 다를 때만 UPDATE 한다.
*
- * 보안: 오직 127.0.0.1(localhost)에서 직접 호출한 요청만 허용한다. 기본은 dry-run(미변경)이며,
- * 실제 실행은 {@code dryRun=false}를 명시해야 한다. 작업 완료 후 이 클래스는 제거한다.
+ * 보안: PTL_PROPERTY {@code Portal / migration.internal.allow-ips} 허용 IP 목록(콤마 구분,
+ * 기본 loopback)에 포함된 IP 의 직접 호출만 허용한다 ({@code MenuInternalController} 모델).
+ * 운영 서버는 bind IP 가 NIC IP 라 loopback 호출이 불가하므로, 실행 전 property 에 호출자 IP 를
+ * 추가하고 작업 완료 후 원복한다. 프록시 경유(X-Forwarded-For 존재) 요청은 거부한다.
+ * 기본은 dry-run(미변경)이며, 실제 실행은 {@code dryRun=false}를 명시해야 한다.
+ * 작업 완료 후 이 클래스는 제거한다.
*
*
* # 미리보기(변경 안 함)
* curl -X POST 'http://127.0.0.1:39130/internal/migration/encrypt-legacy'
* # 실제 실행 (PII 컬럼)
* curl -X POST 'http://127.0.0.1:39130/internal/migration/encrypt-legacy?dryRun=false'
- * # audit 컬럼(created_by/last_modified_by, 15개 테이블)까지 포함
+ * # audit 컬럼(created_by/last_modified_by, 19개 테이블)까지 포함
* curl -X POST 'http://127.0.0.1:39130/internal/migration/encrypt-legacy?dryRun=false&includeAudit=true'
*
*/
@@ -46,13 +53,14 @@ import java.util.Map;
@RequestMapping("/internal/migration")
public class LegacyEncryptionMigrationController {
- /** PII 직접 컬럼 (로그인/검색에 직접 영향) */
+ /** PII 직접 컬럼 (로그인/검색에 직접 영향). ofctelno 는 admin(UnifbwkManService)이 컨버터를 수동 호출해 암호화하는 컬럼 */
private static final List PII_TARGETS = Arrays.asList(
new TargetTable("PTL_USER", Arrays.asList("login_id", "email_addr", "phone_number", "mobile_number")),
new TargetTable("PTL_MESSAGE_REQUEST", Arrays.asList("email", "phone")),
- new TargetTable("tseairm02", Arrays.asList("cphnno", "emad")),
+ new TargetTable("tseairm02", Arrays.asList("cphnno", "emad", "ofctelno")),
new TargetTable("PTL_USER_LOG", Arrays.asList("login_id")),
- new TargetTable("PTL_TWO_FACTOR_AUTH", Arrays.asList("recipient"))
+ new TargetTable("PTL_TWO_FACTOR_AUTH", Arrays.asList("recipient")),
+ new TargetTable("PTL_USER_INVITATION", Arrays.asList("INVITATION_MOBILE"))
);
/** Auditable(@MappedSuperclass) 상속 테이블의 감사 컬럼 (옵션) */
@@ -61,16 +69,24 @@ public class LegacyEncryptionMigrationController {
"DJB_APISTATUS_INCIDENT", "DJB_APISTATUS_INCIDENT_TIMELINE", "DJB_APISTATUS_INCIDENT_API",
"ptl_file", "PTL_MESSAGE_TEMPLATE", "ptl_notice", "ptl_terms",
"ptl_user_privacy_policy_agreement", "ptl_approval_line",
- "PTL_INQUIRY_COMMENT", "ptl_inquiry", "ptl_partnership_application"
+ "PTL_INQUIRY_COMMENT", "ptl_inquiry", "ptl_partnership_application",
+ "PTL_MENU_ITEM", "PTL_MENU_PLACEMENT", "PTL_ROLE", "PTL_ROLE_AUTHORITY"
);
private static final List AUDIT_COLUMNS = Arrays.asList("created_by", "last_modified_by");
+ static final String PROP_GROUP = "Portal";
+ static final String PROP_ALLOW_IPS = "migration.internal.allow-ips";
+ static final String DEFAULT_ALLOW_IPS = "127.0.0.1,::1";
+
private final JdbcTemplate jdbcTemplate;
+ private final PortalPropertyService portalPropertyService;
private final PersonalDataEncryptConverter converter = new PersonalDataEncryptConverter();
- public LegacyEncryptionMigrationController(@Qualifier("portalDataSource") DataSource emsDataSource) {
+ public LegacyEncryptionMigrationController(@Qualifier("portalDataSource") DataSource emsDataSource,
+ PortalPropertyService portalPropertyService) {
// EMS(EMSAPP) 스키마 데이터소스. 컨버터 적용 테이블은 모두 EMS에 존재한다.
this.jdbcTemplate = new JdbcTemplate(emsDataSource);
+ this.portalPropertyService = portalPropertyService;
}
@PostMapping("/encrypt-legacy")
@@ -78,7 +94,7 @@ public class LegacyEncryptionMigrationController {
public Map encryptLegacy(HttpServletRequest request,
@RequestParam(defaultValue = "true") boolean dryRun,
@RequestParam(defaultValue = "false") boolean includeAudit) {
- assertLocalOnly(request);
+ assertAllowedIp(request);
assertNotBypass();
List targets = new ArrayList<>(PII_TARGETS);
@@ -90,24 +106,36 @@ public class LegacyEncryptionMigrationController {
List