From 462e55bcd50532f7247e29037e7b680b9b7e9164 Mon Sep 17 00:00:00 2001 From: infraurp Date: Mon, 27 Jul 2026 14:52:16 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=8B=A0=EC=B2=AD=C2=B7?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=B0=8F=202=EB=8B=A8=EA=B3=84=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20UX=20=EA=B0=9C=EC=84=A0=20(SFR-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 관리자 메뉴에 권한 신청·승인 화면(#46)을 노출하고 신청/승인 처리 로직을 보강했으며, 2단계 인증(OTP) 등록·인증 화면의 사용성을 개선했다. Co-Authored-By: Claude Sonnet 5 --- .../author/AuthorWorkflowController.java | 52 ++++- .../cmm/privacy/author/AuthorWorkflowDAO.java | 11 + .../privacy/author/AuthorWorkflowService.java | 6 + .../author/AuthorWorkflowServiceImpl.java | 13 ++ .../PrivacyAuthorWorkflow_SQL_Mysql.xml | 23 +++ .../PrivacyAuthorWorkflow_SQL_Oracle.xml | 24 +++ .../com/uat/uia/EgovSecondFactor.jsp | 122 +++++++---- .../com/uat/uia/EgovSecondFactorEnroll.jsp | 94 +++++---- src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp | 11 +- .../jsp/site/mnt/sec/authorRequest/list.jsp | 195 ++++++++++++++++++ .../jsp/site/mnt/sec/authorRequest/regist.jsp | 146 +++++++++++++ ...ivacy.menu_author_request_insert_mysql.sql | 73 +++++++ 개인정보보호_개발진행현황.md | 16 +- 13 files changed, 694 insertions(+), 92 deletions(-) create mode 100644 src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/list.jsp create mode 100644 src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/regist.jsp create mode 100644 src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql diff --git a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowController.java b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowController.java index bf4407b..f96ce42 100644 --- a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowController.java +++ b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowController.java @@ -7,10 +7,12 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import egovframework.com.cmm.LoginVO; import egovframework.com.cmm.privacy.auditlog.ClientIpResolver; +import egovframework.rte.ptl.mvc.tags.ui.pagination.PaginationInfo; /** * 권한 신청·승인 워크플로 + 권한이력 조회·다운로드 컨트롤러 @@ -35,7 +37,7 @@ public class AuthorWorkflowController { return (o instanceof LoginVO) ? (LoginVO) o : null; } - /** 권한 변경 신청 목록 */ + /** 권한 변경 신청 목록 (AJAX/JSON) */ @RequestMapping(value = "/mnt/sec/authorRequest/list.do") public String requestList(AuthorChangeRequestVO searchVO, ModelMap model) throws Exception { List list = authorWorkflowService.selectRequestList(searchVO); @@ -44,6 +46,54 @@ public class AuthorWorkflowController { return JSON; } + /** + * 권한 변경 신청 목록·승인·반려 화면(서버 렌더링). + * 뷰: /WEB-INF/jsp/site/mnt/sec/authorRequest/list.jsp + */ + @RequestMapping(value = "/mnt/sec/authorRequest/listView.do") + public String requestListView(@ModelAttribute("authorChangeRequestVO") AuthorChangeRequestVO searchVO, + ModelMap model) throws Exception { + PaginationInfo paginationInfo = new PaginationInfo(); + paginationInfo.setCurrentPageNo(searchVO.getPageIndex()); + paginationInfo.setRecordCountPerPage(searchVO.getRecordCountPerPage() < 1 ? 10 : searchVO.getRecordCountPerPage()); + paginationInfo.setPageSize(10); + + // selectRequestList 내부에서 firstIndex/totalRecordCount 를 세팅한다. + List list = authorWorkflowService.selectRequestList(searchVO); + paginationInfo.setTotalRecordCount(searchVO.getTotalRecordCount()); + + model.addAttribute("resultList", list); + model.addAttribute("paginationInfo", paginationInfo); + return "site/mnt/sec/authorRequest/list"; + } + + /** + * 권한 변경 신청 등록 화면(서버 렌더링). + * 뷰: /WEB-INF/jsp/site/mnt/sec/authorRequest/regist.jsp + */ + @RequestMapping(value = "/mnt/sec/authorRequest/registView.do") + public String requestRegistView(@ModelAttribute("authorChangeRequestVO") AuthorChangeRequestVO vo, + ModelMap model) throws Exception { + // 목표권한 드롭다운 목록 + model.addAttribute("authorityList", authorWorkflowService.selectAuthorityList()); + return "site/mnt/sec/authorRequest/regist"; + } + + /** 대상자 계정ID → 고유식별자(ESNTL_ID)·성명 자동조회 (AJAX, jsonView) */ + @RequestMapping(value = "/mnt/sec/authorRequest/findTarget.do") + public String findTarget(@org.springframework.web.bind.annotation.RequestParam("trgetId") String trgetId, + ModelMap model) throws Exception { + egovframework.rte.psl.dataaccess.util.EgovMap m = authorWorkflowService.selectTargetByLoginId(trgetId); + if (m == null) { + model.addAttribute("result", "fail"); + model.addAttribute("message", "해당 계정ID의 취급자를 찾을 수 없습니다."); + } else { + model.addAttribute("result", "success"); + model.addAttribute("data", m); + } + return JSON; + } + /** 권한 변경 신청 등록 */ @RequestMapping(value = "/mnt/sec/authorRequest/submit.do") public String submit(AuthorChangeRequestVO vo, HttpServletRequest request, ModelMap model) throws Exception { diff --git a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowDAO.java b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowDAO.java index 879e83f..0d93db3 100644 --- a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowDAO.java +++ b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowDAO.java @@ -66,4 +66,15 @@ public class AuthorWorkflowDAO extends EgovComAbstractDAO { public void insertChangeHistory(AuthorChangeHistoryVO vo) { insert("AuthorWorkflowDAO.insertChangeHistory", vo); } + + /** 목표권한 드롭다운용 권한코드 목록 */ + @SuppressWarnings("unchecked") + public List selectAuthorityList() { + return list("AuthorWorkflowDAO.selectAuthorityList", null); + } + + /** 대상자 계정ID → 고유식별자·성명 자동조회 */ + public egovframework.rte.psl.dataaccess.util.EgovMap selectTargetByLoginId(String trgetId) { + return (egovframework.rte.psl.dataaccess.util.EgovMap) selectByPk("AuthorWorkflowDAO.selectTargetByLoginId", trgetId); + } } diff --git a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowService.java b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowService.java index d752f5e..b7952fd 100644 --- a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowService.java +++ b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowService.java @@ -28,4 +28,10 @@ public interface AuthorWorkflowService { /** 반려(J) — 반려 사유 기록 */ void rejectRequest(long requestId, String confmId, String confmResn) throws Exception; + + /** 목표권한 드롭다운용 권한코드 목록 */ + List selectAuthorityList() throws Exception; + + /** 대상자 계정ID → 고유식별자·성명 자동조회 (없으면 null) */ + egovframework.rte.psl.dataaccess.util.EgovMap selectTargetByLoginId(String trgetId) throws Exception; } diff --git a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowServiceImpl.java b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowServiceImpl.java index b6dfd93..1b8bc40 100644 --- a/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowServiceImpl.java +++ b/src/main/java/egovframework/com/cmm/privacy/author/AuthorWorkflowServiceImpl.java @@ -106,6 +106,19 @@ public class AuthorWorkflowServiceImpl implements AuthorWorkflowService { authorWorkflowDAO.updateRequestStatus(req); } + @Override + public List selectAuthorityList() throws Exception { + return authorWorkflowDAO.selectAuthorityList(); + } + + @Override + public egovframework.rte.psl.dataaccess.util.EgovMap selectTargetByLoginId(String trgetId) throws Exception { + if (trgetId == null || trgetId.trim().length() == 0) { + return null; + } + return authorWorkflowDAO.selectTargetByLoginId(trgetId.trim()); + } + /** 신청구분(N/C/R) → 이력 변경구분(G:부여/M:변경/R:회수) */ private String mapChgSeCode(String requestSe) { if ("R".equals(requestSe)) { diff --git a/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAuthorWorkflow_SQL_Mysql.xml b/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAuthorWorkflow_SQL_Mysql.xml index a97303f..8d099d5 100644 --- a/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAuthorWorkflow_SQL_Mysql.xml +++ b/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAuthorWorkflow_SQL_Mysql.xml @@ -10,6 +10,29 @@ --> + + + + + + + + + + + + + + + + @@ -14,32 +16,46 @@ 2단계 인증 - 문화품앗이 관리자 -
-
2단계 인증
-
+
+

2단계 인증

+
${errorMsg}
@@ -54,29 +70,21 @@ OTP(일회용 비밀번호) 앱(Google Authenticator, OTP 등)을 이용해 등록할 수 있습니다. 등록 전까지는 취급자 화면 접근이 제한될 수 있습니다.
- - - + <%-- 시도횟수 초과 --%>

인증 시도 횟수를 초과했습니다.
보안을 위해 다시 로그인해 주세요.

- - - +
<%-- 정상 코드 입력 --%> -
- - 휴대폰(SMS) 인증 - 공동인증서(GPKI) 인증 - OTP(일회용 비밀번호) 인증 - -
-

등록된 휴대폰 @@ -96,21 +104,45 @@

- - - + + + + + + + + + + + + + +
2단계 인증코드 입력
인증방식 + + + 휴대폰(SMS) 인증 + 공동인증서(GPKI) 인증 + OTP(일회용 비밀번호) 인증 + + +
+ +
+
+ +
-

+

인증수단을 변경/재등록하려면 - 여기를 누르세요. + 여기를 누르세요.

-
한국문화예술위원회 문화품앗이 · 개인정보취급자 보호
+
한국문화예술위원회 문화품앗이 · 개인정보취급자 보호
diff --git a/src/main/webapp/WEB-INF/jsp/egovframework/com/uat/uia/EgovSecondFactorEnroll.jsp b/src/main/webapp/WEB-INF/jsp/egovframework/com/uat/uia/EgovSecondFactorEnroll.jsp index debc940..19b3d5a 100644 --- a/src/main/webapp/WEB-INF/jsp/egovframework/com/uat/uia/EgovSecondFactorEnroll.jsp +++ b/src/main/webapp/WEB-INF/jsp/egovframework/com/uat/uia/EgovSecondFactorEnroll.jsp @@ -7,6 +7,7 @@ · 비밀키(secret)는 등록 1회 표기용이며 저장·로깅하지 않는다. QR 스캔 등록 + 수동 입력(보조) 병행. · QR(otpQrDataUri)은 서버측 ZXing 으로 생성한 자립형 data URI PNG — 외부 리소스/네트워크 없음(폐쇄망). · 저장소(DaoOtpSecretStore)는 OTP_DEVICE 테이블에 비밀키 암호문을 영구 저장 — 재기동에도 유지. + · 자립형 게이트라 head.do 미사용, 인라인 스타일을 mnt 관리자 스킨 토큰(board.css/admin.css)으로 통일. --%> @@ -15,37 +16,48 @@ OTP 인증수단 등록 - 문화품앗이 관리자 -
-
OTP 인증수단 등록
-
+
+

OTP 인증수단 등록

+
${enrollError}
@@ -67,28 +79,40 @@
-
QR 스캔이 어려우면 수동 입력
+

QR 스캔이 어려우면 수동 입력

${fn:escapeXml(otpSecret)}
otpauth URI: ${fn:escapeXml(otpauthUri)}
- - - + + + + + + + + + +
앱 표시 6자리 코드 입력
+ +
+
+ +
- - - +
비밀키는 이 화면에서만 표시됩니다(저장·재조회 불가). 등록을 완료하기 전에 창을 닫으면 처음부터 다시 발급해야 합니다.
-
한국문화예술위원회 문화품앗이 · 개인정보취급자 보호
+
한국문화예술위원회 문화품앗이 · 개인정보취급자 보호
diff --git a/src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp b/src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp index 4dbe5e6..138d86a 100644 --- a/src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp +++ b/src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp @@ -48,13 +48,13 @@ || param.page eq '9100022' || param.page eq '9100031' || param.page eq '9100034' || param.page eq '9100041' || param.page eq '9100042'|| param.page eq '9100043' || param.page eq '9100071' || param.page eq '9100072' || param.page eq '9100080' - || param.page eq '9100091' || param.page eq '9100092' || param.page eq '9100093' || param.page eq ''}">class="on">시스템관리 + || param.page eq '9100091' || param.page eq '9100092' || param.page eq '9100093' || param.page eq '9100035' || param.page eq ''}">class="on">시스템관리
    style="display: block;"> + || param.page eq '9100091' || param.page eq '9100092' || param.page eq '9100093' || param.page eq '9100035' || param.page eq ''}">style="display: block;">
  • class="on">${menuUpperVo2.menuNm} @@ -94,10 +94,10 @@
  • -
  • class="on">${menuUpperVo2.menuNm} +
  • class="on">${menuUpperVo2.menuNm} -
      style="display: block;"> + diff --git a/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/list.jsp b/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/list.jsp new file mode 100644 index 0000000..0c676f7 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/list.jsp @@ -0,0 +1,195 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- Head --%> +<%--// Head --%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> +<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + + + + + + + + + + + + + +
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      권한변경 신청 목록
      NO.신청일시대상자ID신청구분신청사유신청자처리상태처리
      + + 권한신설 + 권한변경 + 권한회수 + + + + + 신청 + 승인 + 반려 + + + + + ');" class="btn2">승인 + ');" class="btn1">반려 + +
      + +
      + +
      + +
      + + +
      +
      +

      반려 사유 입력

      + + +
      + 반려 + 취소 +
      +
      +
      + +
+ +
+ +
+ + + diff --git a/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/regist.jsp b/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/regist.jsp new file mode 100644 index 0000000..aba0470 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/site/mnt/sec/authorRequest/regist.jsp @@ -0,0 +1,146 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- Head --%> +<%--// Head --%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> +<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
권한변경 신청 등록
* + + 조회 + +
* + + (계정ID [조회] 시 자동 입력) +
* + +
+ + (권한회수 시 선택 불필요) +
* + +
+ + +
+
+ +
+ + +
+ +
+ + + + + + + + + + diff --git a/src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql b/src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql new file mode 100644 index 0000000..7ca559b --- /dev/null +++ b/src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql @@ -0,0 +1,73 @@ +-- ===================================================================== +-- 관리자 메뉴 등록 : 권한 신청·승인 워크플로 화면(#46) +-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-001 / 개발항목 #3 화면 #46) +-- 대상 DB : MySQL 5.7 (스키마 csv) 작성일: 2026-07-23 +-- --------------------------------------------------------------------- +-- [목적] /mnt/sec/authorRequest/listView.do (권한 신청·승인 목록)을 +-- 관리자 좌측/상단 메뉴에 노출. +-- +-- [배치] 운영 csv DB 실측(2026-07-23) 기준 확정 배치: +-- 통합관리시스템(9000000) > 시스템관리(9100000) > 권한관리(9100030) +-- ├ 권한생성 (9100031, EgovAuthorList) +-- ├ 롤관리 (9100034, EgovRoleList) +-- └ 권한 신청·승인 (9100035, AuthorRequestList) ← 신규 +-- +-- [노출권한] IS_AUTHENTICATED_FULLY, ROLE_ADMIN +-- (형제 '권한생성'(9100031)·'권한관리'그룹(9100030)과 동일) +-- +-- [메뉴 렌더 경로] LayoutMntController.left() → MainMenu.selectMainMenuLeft +-- : COMTNMENUCREATDTLS(권한↔메뉴) 없으면 화면에 안 뜸. +-- +-- ★★ 이 DML만으로는 좌측메뉴에 안 뜬다 — left.jsp 수정 필수 ★★ +-- src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp 는 메뉴번호 하드코딩 JSP라 +-- 권한관리(9100030) 블록에 9100035 분기를 추가해야 렌더된다. +-- (2026-07-23 반영 완료: 9100035 + 시스템관리 하이라이트 조건 추가) +-- +-- [재실행 안전] 모든 INSERT NOT EXISTS 가드 → 반복 실행 무해. +-- [Oracle] 대상 운영 DB가 MySQL(csv)이라 Oracle 쌍 미작성. +-- [롤백] 맨 아래 주석 참조. +-- ===================================================================== + +-- ───────────────────────────────────────────────────────────── +-- [1] 프로그램(URL) 등록 ※ 메뉴보다 먼저(FK: COMTNMENUINFO.PROGRM_FILE_NM → COMTNPROGRMLIST) +-- URL 관례: '/mnt/...do?page=' (좌측메뉴 활성표시용 page 파라미터) +-- ───────────────────────────────────────────────────────────── +INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL) +SELECT 'AuthorRequestList', '/mnt/sec/authorRequest/', '권한 신청·승인', + '권한 변경 신청·승인 워크플로(SFR-001 #46)', '/mnt/sec/authorRequest/listView.do?page=9100035' + FROM DUAL + WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST WHERE PROGRM_FILE_NM = 'AuthorRequestList'); + +-- ───────────────────────────────────────────────────────────── +-- [2] 메뉴 등록 : 권한관리(9100030) 하위, 롤관리(ord 4) 다음(ord 5) +-- ───────────────────────────────────────────────────────────── +INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM) +SELECT '권한 신청·승인', 'AuthorRequestList', 9100035, 9100030, 5, '권한 변경 신청·승인 워크플로', '/', '/' + FROM DUAL + WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO WHERE MENU_NO = 9100035); + +-- ───────────────────────────────────────────────────────────── +-- [3] 권한↔메뉴 노출 매핑 (형제 '권한생성'과 동일한 2개 권한) +-- ───────────────────────────────────────────────────────────── +INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE) +SELECT 9100035, g.ac + FROM (SELECT 'IS_AUTHENTICATED_FULLY' AS ac UNION ALL SELECT 'ROLE_ADMIN') g + WHERE g.ac IN (SELECT AUTHOR_CODE FROM COMTNAUTHORINFO) -- FK 안전 + AND NOT EXISTS (SELECT 1 FROM COMTNMENUCREATDTLS x + WHERE x.MENU_NO = 9100035 AND x.AUTHOR_CODE = g.ac); + +-- ───────────────────────────────────────────────────────────── +-- [검증] +-- SELECT m.MENU_NO,m.UPPER_MENU_NO,m.MENU_NM,p.URL, +-- (SELECT COUNT(*) FROM COMTNMENUCREATDTLS d WHERE d.MENU_NO=m.MENU_NO) grants +-- FROM COMTNMENUINFO m JOIN COMTNPROGRMLIST p ON m.PROGRM_FILE_NM=p.PROGRM_FILE_NM +-- WHERE m.MENU_NO=9100035; +-- +-- [롤백] +-- DELETE FROM COMTNMENUCREATDTLS WHERE MENU_NO=9100035; +-- DELETE FROM COMTNMENUINFO WHERE MENU_NO=9100035; +-- DELETE FROM COMTNPROGRMLIST WHERE PROGRM_FILE_NM='AuthorRequestList'; +-- +-- [향후 확장] 권한이력(#6)·접속기록 조회(#41/#47) 화면도 동일 패턴으로 +-- 9100036~ 부여하여 같은 권한관리/시스템관리 그룹에 추가. +-- ===================================================================== diff --git a/개인정보보호_개발진행현황.md b/개인정보보호_개발진행현황.md index 6412069..b3109ac 100644 --- a/개인정보보호_개발진행현황.md +++ b/개인정보보호_개발진행현황.md @@ -2,7 +2,7 @@ > 발주기관: 한국문화원연합회 | 사업기간: 계약일~2026.12.18 | 대상: 문화품앗이(csv.culture.go.kr) > 근거: 개인정보보호위원회 「고유식별정보 안전조치 관리실태 점검」 결과통보(2025.11.21.) -> 작성일: **2026-07-21** | 기준: `개인정보보호_개발항목.docx`(61개 개발항목) 대비 소스 실측 +> 작성일: **2026-07-21** | 갱신: **2026-07-22**(URL 접근제어·2차 인증 진짜완료 재확인) | 기준: `개인정보보호_개발항목.docx`(61개 개발항목) 대비 소스 실측 --- @@ -21,6 +21,8 @@ - 분석·설계 및 하네스는 완료되어 **바로 구현 단계 진입 가능**한 상태. - 현재 워킹트리의 소스 변경(`EgovSpringSecurityLoginFilter.java`, `pom.xml`)은 **로컬 개발 IP 허용(127.0.0.1) 추가**로, 본 사업 산출물이 아님 — 진행률에서 제외. +> **[2026-07-22 갱신] URL 접근 제어·2차 인증 "진짜 완료" 재확인** — #1·#2(URL 접근제어), #14·#15·#16(2차 인증) 5개 항목의 **코드·로직을 재확인**해 "✅ 진짜완료"로 표시함(구현 완결 확인). ⚠단, 두 인터셉터의 **운영 강제(AuthorityInterceptor `urlRoleMapping` 등록 / SecondFactorInterceptor `enforce=true`)는 배포 단계에서 별도 활성 필요** — "코드 완결(진짜완료)"과 "운영 강제 활성"은 구분해 기록함. 운영 강제 활성은 화면(#52)·인증수단(#18~20) 완료 후 진행 예정. + --- ## 1-1. 오늘의 작업 (2026-07-21, ~17:00 목표) @@ -182,9 +184,9 @@ fork 3개를 병렬로 투입해 **자체 완결적·파일 충돌 없는 기반 | # | 개발항목 | 상태 | 소스 실측 / 비고 | |:--:|----------|:----:|------------------| -| 1 | `getAuthorities()` 구현 | ✅ 완료 | [Fork A] `EgovUserDetailsSessionServiceImpl` 세션 LoginVO.uniqId→`EgovUserAuthorityDAO.selectAuthoritiesByUniqId`→`PrivacyAuthority_SQL_Mysql.xml`(COMTNEMPLYRSCRTYESTBS 조회). sql-map-config 등록 + 빈 DAO 주입 배선 완료. 미인증 시 빈 목록(fail-safe) | -| 2 | AuthorityInterceptor 신규 등록 | ✅ 완료 | [Fork A] `cmm/privacy/interceptor/AuthorityInterceptor.java` + `egov-com-servlet.xml` 등록. `/mnt/` 한정, `EgovUserDetailsHelper.getAuthorities()`(#1) 대조 미허용 403. **urlRoleMapping 기본 비어있음=fail-open(전경로 통과)** → 운영에서 매핑 점진 등록 | -| 3 | 권한 신청·승인 워크플로 구현 | ✅ 완료 | [Fork A] `AUTHOR_CHANGE_REQUEST`(DDL)+5계층(`AuthorWorkflow*`). `approveRequest()` 승인 시 스냅샷→권한반영(COMTNEMPLYRSCRTYESTBS)→상태A→#4이력을 **단일TX**(context-transaction AOP, 예외 시 롤백). 화면 #46 후속 | +| 1 | `getAuthorities()` 구현 | ✅ 진짜완료 | **[2026-07-22 진짜완료 — 코드·로직 재확인]** [Fork A] `EgovUserDetailsSessionServiceImpl` 세션 LoginVO.uniqId→`EgovUserAuthorityDAO.selectAuthoritiesByUniqId`→`PrivacyAuthority_SQL_Mysql.xml`(COMTNEMPLYRSCRTYESTBS 조회). sql-map-config 등록 + 빈 DAO 주입 배선 완료. 미인증 시 빈 목록(fail-safe) | +| 2 | AuthorityInterceptor 신규 등록 | ✅ 진짜완료 | **[2026-07-22 진짜완료 — 코드·로직 재확인. ⚠운영 강제(`urlRoleMapping` 등록)는 배포 시 별도 활성]** [Fork A] `cmm/privacy/interceptor/AuthorityInterceptor.java` + `egov-com-servlet.xml` 등록. `/mnt/` 한정, `EgovUserDetailsHelper.getAuthorities()`(#1) 대조 미허용 403. **urlRoleMapping 기본 비어있음=fail-open(전경로 통과)** → 운영에서 매핑 점진 등록 | +| 3 | 권한 신청·승인 워크플로 구현 | ✅ 완료(화면 #46 포함) | [Fork A] `AUTHOR_CHANGE_REQUEST`(DDL)+5계층(`AuthorWorkflow*`). `approveRequest()` 승인 시 스냅샷→권한반영(COMTNEMPLYRSCRTYESTBS)→상태A→#4이력을 **단일TX**(context-transaction AOP, 예외 시 롤백). **화면 #46 구현: `AuthorWorkflowController`에 `listView.do`/`registView.do`(JSP 뷰명 반환·`@ModelAttribute authorChangeRequestVO`·`PaginationInfo`) 페이지진입 추가 + `site/mnt/sec/authorRequest/{list,regist}.jsp`(JstlView, 검색·목록·`ui:pagination`, 승인/반려 jQuery AJAX(approve/reject.do)·반려사유 모달, 신청등록 폼). 기존 jsonView 엔드포인트 병존. **메뉴 등록: `src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql`(COMTNPROGRMLIST+COMTNMENUINFO'개인정보보호'그룹+COMTNMENUCREATDTLS 권한매핑, NOT EXISTS 재실행안전·기존 권한관리 노출권한 상속). ⚠운영 DB 적용 필요(메뉴번호 충돌·관리자 AUTHOR_CODE 배포 시 확인).**| | 4 | AUTHOR_CHANGE_HISTORY 이력 테이블 신설 | ✅ 완료 | `src/script/mysql/ddl/privacy.author_change_history_create_mysql.sql`. 변경자/변경일시/변경전후권한(BEFORE/AFTER_AUTHOR)/변경구분/승인자/IP + 3년(1,095일) 보관 전제. Fork A 중단→오케스트레이터 완료 | | 5 | 권한 이력 파기 배치 구현 | ✅ 완료 | [Fork A] `cmm/privacy/batch/`(Service/Impl/PurgeDAO) + `PrivacyAuthorHistoryPurge_SQL`(DELETE WHERE CHG_DT