arko_renual/src/script/mysql/privacy.install_mysql.sql
infraurp 363138eef5 장기 미사용 계정 현황 화면명에 "관리자" 명시 + 행별 조치 버튼 축소
미사용계정 현황(#11, 메뉴 9100037)이 대상이 개인정보취급자(EMP) 계정임을
화면·메뉴명에서 알 수 없어 오해 소지가 있었다. "장기 미사용 계정" →
"장기 미사용 관리자 계정"으로 명시(요약표/목록 캡션, 메뉴명, 프로그램 설명).
이미 배포된 환경을 위해 재실행 안전한 UPDATE 문을 DML에 추가.

목록 행별 "말소"/"잠금" 버튼은 시각적으로 과도하게 커서 축소(.btn3.mini).
상단 "선택 말소" 일괄 버튼은 그대로 둠.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 16:59:37 +09:00

1156 lines
82 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- #####################################################################
-- arko_renual 설치용 통합 SQL (개인정보보호 기능개선 사업 · arko 대비 변동분)
-- 대상 DB : MySQL 5.7 / MariaDB (스키마 csv)
-- 구성 : [PART 1] DDL(테이블 생성·변경) → [PART 2] DML(기준/시드 데이터)
-- → [PART 3] 관리자 메뉴 등록 DML(좌측메뉴 노출, COMTNPROGRMLIST/
-- COMTNMENUINFO/COMTNMENUCREATDTLS, 9100035~9100039 + 9100044 + 9100045)
-- 실행순서: 반드시 PART 1 → PART 2 → PART 3 순서로 실행
-- (테이블 생성 → 시드 적재 → 메뉴 등록. 메뉴는 FK로 프로그램 등록을 먼저 요구)
-- 재실행 : **전 구간 재실행 안전(멱등)** — PART 1 은 CREATE TABLE IF NOT EXISTS 와
-- information_schema 존재 확인 후 실행되는 ALTER(컬럼/인덱스), PART 2·3 은
-- NOT EXISTS 가드를 쓴다. 이미 적용된 항목은 'SKIP: ...' 메시지만 출력하고
-- 넘어가므로, 기존 환경에도 이 파일 하나만 그대로 실행하면 된다.
-- (2026-08-05 이전 버전은 재실행 시 1050/1060/1061 오류가 났다)
-- ★★ PART 3 적용만으로는 좌측메뉴에 안 뜬다 — left.jsp 하드코딩 <c:when> 분기가
-- 반드시 함께 배포돼야 한다(소스에는 이미 반영됨). 상세는 PART 3 헤더 참조. ★★
-- #####################################################################
-- =====================================================================
-- PART 1. DDL (테이블 생성 / 컬럼 확장) ※ 시드 INSERT는 PART 2로 분리
-- =====================================================================
-- ─────────────────────────────────────────────────────────────
-- [privacy.account_mng_policy_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 계정 관리 정책 : ACCOUNT_MNG_POLICY (단일행 정책 테이블)
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-002, 장기 미사용 계정 관리)
-- 개발항목 #13(정책 설정) + #9(휴면 기준일수 정책값화) - 담당: arko-privacy-authctrl (Fork A)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [용도] 휴면 기준일수·사전알림 시점·삭제 보존기간·배치주기 등 계정 관리 정책을
-- 하드코딩이 아닌 관리자 설정값으로 운영하기 위한 단일행 정책 테이블.
-- [#9] 휴면 select(EgovLoginUsr_SQL_Mysql.xml)의 365 하드코딩을
-- (SELECT DORMANT_DAY FROM ACCOUNT_MNG_POLICY) 로 대체(미설정 시 365 fallback).
-- [#10] ALERT_D1/D2/D3(기본 30/7/0) = 휴면예정 D-30/D-7/D-Day 사전알림 시점.
-- ADMIN_EMAIL = 사전알림 관리자 수신 주소.
-- =====================================================================
CREATE TABLE IF NOT EXISTS ACCOUNT_MNG_POLICY (
POLICY_ID INT NOT NULL DEFAULT 1 COMMENT '정책 식별자(단일행=1)',
DORMANT_DAY INT NOT NULL DEFAULT 365 COMMENT '휴면 전환 기준 미접속 일수(#9)',
ALERT_D1 INT NOT NULL DEFAULT 30 COMMENT '사전알림 1차(휴면 D-일수)',
ALERT_D2 INT NOT NULL DEFAULT 7 COMMENT '사전알림 2차(휴면 D-일수)',
ALERT_D3 INT NOT NULL DEFAULT 0 COMMENT '사전알림 3차(D-Day, 0=당일)',
DELETE_PRESERVE_DAY INT NOT NULL DEFAULT 365 COMMENT '휴면/탈퇴 후 삭제 보존기간(일)',
BATCH_CYCLE VARCHAR(20) NOT NULL DEFAULT 'DAILY' COMMENT '배치 주기(DAILY/WEEKLY/MONTHLY)',
ADMIN_EMAIL VARCHAR(200) NULL COMMENT '사전알림 관리자 수신 이메일',
UPD_ID VARCHAR(20) NULL COMMENT '최종 수정자',
UPD_DT DATETIME NULL COMMENT '최종 수정일시',
PRIMARY KEY (POLICY_ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='계정 관리 정책(개인정보보호 SFR-002, 단일행)';
-- 기본 정책 시드(없을 때만). 기준 365일·알림 D-30/7/0·보존 365일.
-- ─────────────────────────────────────────────────────────────
-- [privacy.account_unlock_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 계정 잠금 해제 이력 : ACCOUNT_UNLOCK_HISTORY
-- 문화품앗이 개인정보보호 기능개선 사업 (SER-001, 계정 잠금 정책 정비)
-- 개발항목 #24 - 담당: arko-privacy-authctrl (Fork A)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 잠긴 계정 해제를 DB 직접조작이 아니라 관리자 화면(EgovAccountUnlockManage)
-- 에서 처리하고, 해제자·해제일시·대상·사유를 이력으로 남긴다.
-- [무결성] 애플리케이션 계층에 UPDATE/DELETE statement 미정의(INSERT/SELECT 한정).
-- =====================================================================
CREATE TABLE IF NOT EXISTS ACCOUNT_UNLOCK_HISTORY (
UNLOCK_HIST_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '해제 이력 일련번호(PK)',
TRGET_ID VARCHAR(50) NULL COMMENT '해제 대상 계정 ID(전체해제 시 NULL 가능)',
TRGET_SE VARCHAR(10) NULL COMMENT '대상 회원구분 GNR/GRO/ENT/USR',
UNLOCK_TY CHAR(1) NOT NULL DEFAULT 'I' COMMENT '해제 유형 I:개별 A:전체',
UNLOCK_USER_ID VARCHAR(50) NULL COMMENT '해제 처리자(관리자) ID',
UNLOCK_RESN VARCHAR(500) NULL COMMENT '해제 사유',
UNLOCK_IP VARCHAR(45) NULL COMMENT '해제자 접속 IP(IPv6 대응)',
UNLOCK_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '해제 일시',
PRIMARY KEY (UNLOCK_HIST_ID),
KEY IX_AUH_TRGET (TRGET_ID, UNLOCK_DT),
KEY IX_AUH_USER (UNLOCK_USER_ID, UNLOCK_DT),
KEY IX_AUH_DT (UNLOCK_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='계정 잠금 해제 이력(개인정보보호 SER-001 #24)';
-- =====================================================================
-- 계정 잠금 임계값 10회 → 5회 (#24)
-- 임계값은 하드코딩이 아니라 LOGIN_LOCK_COUNT.LOCK_COUNT(단일행) 데이터에서 조회됨
-- (loginDAO.selectFailTotalCount). 아래 UPDATE 로 운영 정책값을 5회로 조정한다.
-- ※ 소스(EgovLoginController)에는 값 부재 시 기본 5회 fallback 이 반영되어 있음.
-- =====================================================================
UPDATE LOGIN_LOCK_COUNT SET LOCK_COUNT = 5;
-- 행이 없을 경우 대비(최초 세팅):
-- INSERT INTO LOGIN_LOCK_COUNT (LOCK_COUNT) SELECT 5 FROM DUAL
-- WHERE NOT EXISTS (SELECT 1 FROM LOGIN_LOCK_COUNT);
-- ─────────────────────────────────────────────────────────────
-- [privacy.author_change_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 권한 변경 이력 테이블 : AUTHOR_CHANGE_HISTORY
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-001, 역할별 차등 접근/권한 변경 추적)
-- 개발항목 #4 - 담당: arko-privacy-authctrl (Fork A → 오케스트레이터 완료)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 권한 신설·변경·회수(#1 getAuthorities 대상 COMTNEMPLYRSCRTYESTBS 등)의
-- 모든 변경을 변경자·변경일시·변경전/후 권한과 함께 이력으로 적재한다.
-- 권한 신청·승인 워크플로(#3)의 승인 반영 시점에 권한반영과 단일 트랜잭션으로 적재.
--
-- [보관·파기] 3년(1,095일) 이상 보관 전제. 월 1회 @Scheduled 파기 배치(#5)가
-- 보관기간(1,095일) 경과 이력만 선별 파기한다. 개별 삭제 API 미제공.
-- 다운로드 시 사유 입력 필수 + 다운로드 이력 적재(#6)는 별도 처리.
--
-- [무결성] 이력 위·변조 억제를 위해 변경전/후 권한을 함께 보관(사후 대사 가능).
-- 애플리케이션 계층에 UPDATE statement 미정의(INSERT/SELECT/파기 DELETE 한정).
-- =====================================================================
CREATE TABLE IF NOT EXISTS AUTHOR_CHANGE_HISTORY (
HIST_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '이력 일련번호(PK)',
TRGET_UNIQ_ID VARCHAR(20) NOT NULL COMMENT '변경 대상자 고유식별자(ESNTL_ID)',
TRGET_ID VARCHAR(20) NULL COMMENT '변경 대상자 계정ID(참고용)',
CHG_SE_CODE CHAR(1) NOT NULL COMMENT '변경구분 G:부여(grant) R:회수(revoke) M:변경(modify)',
BEFORE_AUTHOR VARCHAR(1000) NULL COMMENT '변경 전 권한(코드 CSV 또는 스냅샷)',
AFTER_AUTHOR VARCHAR(1000) NULL COMMENT '변경 후 권한(코드 CSV 또는 스냅샷)',
CHG_RESN VARCHAR(500) NULL COMMENT '변경 사유(신청/승인 워크플로 연계 #3)',
APROVL_ID VARCHAR(20) NULL COMMENT '승인자 계정ID(승인 워크플로 #3)',
CHANGER_UNIQ_ID VARCHAR(20) NOT NULL COMMENT '변경자(처리자) 고유식별자',
CHANGER_ID VARCHAR(20) NULL COMMENT '변경자 계정ID(참고용)',
CHANGER_IP VARCHAR(45) NULL COMMENT '변경자 접속 IP(IPv6 대응)',
CHG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '변경 일시',
PRIMARY KEY (HIST_ID),
KEY IX_ACH_TRGET (TRGET_UNIQ_ID, CHG_DT),
KEY IX_ACH_CHANGER (CHANGER_UNIQ_ID, CHG_DT),
KEY IX_ACH_CHGDT (CHG_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='권한 변경 이력(개인정보보호 SFR-001, 3년 이상 보관)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.author_workflow_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 권한 신청·승인 워크플로 + 권한이력 다운로드 이력
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-001, 권한 신설·변경·회수 승인통제)
-- 개발항목 #3(신청·승인 워크플로) / #6(권한이력 조회·다운로드) - 담당: arko-privacy-authctrl (Fork A 6차)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [#3] AUTHOR_CHANGE_REQUEST : 권한 변경 신청→승인/반려 워크플로.
-- 승인(A) 시 권한 반영(COMTNEMPLYRSCRTYESTBS)과 이력 적재(AUTHOR_CHANGE_HISTORY)를
-- 단일 트랜잭션으로 처리한다(egovframework.com..*Impl AOP tx advisor).
-- [#6] AUTHOR_HIST_DOWNLOAD_HISTORY : 권한이력 다운로드 증빙(사유 필수).
-- =====================================================================
CREATE TABLE IF NOT EXISTS AUTHOR_CHANGE_REQUEST (
REQUEST_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '신청 일련번호(PK)',
TRGET_UNIQ_ID VARCHAR(20) NOT NULL COMMENT '권한변경 대상자 고유식별자(ESNTL_ID)',
TRGET_ID VARCHAR(20) NULL COMMENT '권한변경 대상자 계정ID(참고용)',
REQUEST_SE CHAR(1) NOT NULL COMMENT '신청구분 N:신설(부여) C:변경 R:회수',
BEFORE_AUTHOR VARCHAR(1000) NULL COMMENT '신청 시점 현재 권한(스냅샷)',
AFTER_AUTHOR VARCHAR(1000) NULL COMMENT '신청 목표 권한(코드/CSV)',
REQUEST_RESN VARCHAR(500) NOT NULL COMMENT '신청 사유(필수)',
REQSTER_ID VARCHAR(20) NOT NULL COMMENT '신청자 계정ID',
REQSTER_UNIQ_ID VARCHAR(20) NULL COMMENT '신청자 고유식별자',
REQUEST_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '신청 일시',
CONFM_STTUS CHAR(1) NOT NULL DEFAULT 'R' COMMENT '처리상태 R:신청 A:승인 J:반려',
CONFM_ID VARCHAR(20) NULL COMMENT '승인/반려 처리자 계정ID',
CONFM_UNIQ_ID VARCHAR(20) NULL COMMENT '승인/반려 처리자 고유식별자',
CONFM_DT DATETIME NULL COMMENT '승인/반려 일시',
CONFM_RESN VARCHAR(500) NULL COMMENT '반려 사유',
PRIMARY KEY (REQUEST_ID),
KEY IX_ACR_TRGET (TRGET_UNIQ_ID, REQUEST_DT),
KEY IX_ACR_STTUS (CONFM_STTUS, REQUEST_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='권한 변경 신청·승인 워크플로(개인정보보호 SFR-001 #3)';
CREATE TABLE IF NOT EXISTS AUTHOR_HIST_DOWNLOAD_HISTORY (
DWLD_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '다운로드 일련번호(PK)',
DOWNLOADER_ID VARCHAR(20) NOT NULL COMMENT '다운로드자 계정ID',
DOWNLOADER_UNIQ_ID VARCHAR(20) NULL COMMENT '다운로드자 고유식별자',
DOWNLOADER_IP VARCHAR(45) NULL COMMENT '다운로드자 접속 IP(IPv6 대응)',
DWLD_RESN VARCHAR(500) NOT NULL COMMENT '다운로드 사유(필수·미입력 차단)',
SEARCH_COND VARCHAR(1000) NULL COMMENT '다운로드 시 검색조건 스냅샷',
DWLD_CNT INT NOT NULL DEFAULT 0 COMMENT '다운로드 건수',
DWLD_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '다운로드 일시',
PRIMARY KEY (DWLD_ID),
KEY IX_AHD_DWLD (DOWNLOADER_ID, DWLD_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='권한이력 다운로드 증빙 이력(개인정보보호 SFR-001 #6)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.job_mapping_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 개인정보 처리 업무유형 매핑 테이블 : PDAL_JOB_MAPPING
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-004 / SER-002)
-- 개발항목 #32 - 담당: arko-privacy-auditlog (Fork B)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 접속기록 수집(#31 PersonalDataLogAspect)이 가로챈 서비스 메서드를
-- 업무코드(JOB_SE_CODE)·업무명(JOB_SE_NM)으로 매핑한다.
-- AOP pointcut 범위(개인정보 처리 서비스 패키지) 안에서 신규 화면이 추가되면
-- 이 테이블에 행만 등록하면 수집되며, Aspect/pointcut 소스 수정이 불요하다.
--
-- [매칭 규칙] Aspect가 (대상클래스 단순명, 메서드명)으로 조회.
-- - TARGET_CLASS : 서비스 impl 단순 클래스명(예: EgovMberManageServiceImpl) 또는 FQN
-- - METHOD_PTTRN : 메서드명 접두 매칭(예: 'select','insertMber') 또는 '*'(전체)
-- - DML_GUBUN : 지정 시 우선, NULL이면 메서드명 접두로 유도(insert*→I/update*→U/delete*→D/그외 S)
-- - SORT_ORDR : 다중 매칭 시 낮은 값 우선(구체 규칙을 위로)
--
-- ※ URL 패턴 기반 수집이 필요하면(서블릿 필터형) 동일 테이블에 URL_PTTRN 컬럼을 추가해 확장.
-- =====================================================================
CREATE TABLE IF NOT EXISTS PDAL_JOB_MAPPING (
MAPPING_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '매핑 일련번호(PK)',
TARGET_CLASS VARCHAR(200) NOT NULL COMMENT '대상 서비스 impl 단순 클래스명 또는 FQN',
METHOD_PTTRN VARCHAR(100) NOT NULL DEFAULT '*' COMMENT '메서드명 접두 매칭(또는 * 전체)',
JOB_SE_CODE VARCHAR(30) NOT NULL COMMENT '수행업무 유형 코드',
JOB_SE_NM VARCHAR(100) NOT NULL COMMENT '수행업무 유형(화면·기능명)',
DML_GUBUN CHAR(1) NULL COMMENT '처리구분 강제값 S/I/U/D (NULL=메서드명 유도)',
SORT_ORDR INT NOT NULL DEFAULT 100 COMMENT '매칭 우선순위(낮을수록 우선)',
USE_AT CHAR(1) NOT NULL DEFAULT 'Y' COMMENT '사용여부 Y/N',
RM VARCHAR(300) NULL COMMENT '비고',
PRIMARY KEY (MAPPING_ID),
KEY IX_PJM_LOOKUP (USE_AT, TARGET_CLASS, SORT_ORDR)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='개인정보 처리 업무유형 매핑(접속기록 #31/#32)';
-- ---------------------------------------------------------------------
-- 초기 매핑 시드 : 현행 개인정보 처리 서비스(#31 pointcut 대상과 일치)
-- 신규 개인정보 화면 추가 시 아래 형식으로 행만 추가하면 수집된다.
-- ※ 시드 INSERT 본문은 PART 2 로 분리되어 있다.
-- ---------------------------------------------------------------------
-- ─────────────────────────────────────────────────────────────
-- [privacy.job_mapping_audit_cols_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 업무유형 매핑 테이블 확장 : PDAL_JOB_MAPPING 에 변경자·변경일시 컬럼 추가
-- 개발항목 #32-1 (관리 화면) - 2026-08-07
-- ---------------------------------------------------------------------
-- [사유] 관리 화면에서 취급자가 매핑을 등록·수정·사용중지할 수 있게 되면
-- "누가 접속기록 분류를 바꿨는가"가 감사 대상이 된다.
-- [멱등] information_schema 존재 확인 후 ALTER (재실행 안전).
-- ★ 관리 화면 sqlMap(PrivacyJobMapping_SQL_Mysql.xml)이 이 4개 컬럼을 SELECT/INSERT 하므로
-- 소스 배포보다 이 DDL 적용이 반드시 선행돼야 한다(미적용 시 1054 Unknown column).
-- =====================================================================
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'PDAL_JOB_MAPPING' AND COLUMN_NAME = 'REGISTER_ID') > 0,
'SELECT ''SKIP: PDAL_JOB_MAPPING.REGISTER_ID already exists'' AS msg',
'ALTER TABLE PDAL_JOB_MAPPING
ADD COLUMN REGISTER_ID VARCHAR(20) NULL COMMENT ''등록자 계정ID(시드행은 NULL)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'PDAL_JOB_MAPPING' AND COLUMN_NAME = 'REG_DT') > 0,
'SELECT ''SKIP: PDAL_JOB_MAPPING.REG_DT already exists'' AS msg',
'ALTER TABLE PDAL_JOB_MAPPING
ADD COLUMN REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''등록 일시'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'PDAL_JOB_MAPPING' AND COLUMN_NAME = 'UPDUSR_ID') > 0,
'SELECT ''SKIP: PDAL_JOB_MAPPING.UPDUSR_ID already exists'' AS msg',
'ALTER TABLE PDAL_JOB_MAPPING
ADD COLUMN UPDUSR_ID VARCHAR(20) NULL COMMENT ''최종 변경자 계정ID'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'PDAL_JOB_MAPPING' AND COLUMN_NAME = 'UPDT_DT') > 0,
'SELECT ''SKIP: PDAL_JOB_MAPPING.UPDT_DT already exists'' AS msg',
'ALTER TABLE PDAL_JOB_MAPPING
ADD COLUMN UPDT_DT DATETIME NULL COMMENT ''최종 변경 일시'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- ─────────────────────────────────────────────────────────────
-- [privacy.download_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 개인정보 접속기록 다운로드 이력 : PDAL_DOWNLOAD_HISTORY
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-004 / SER-002)
-- 개발항목 #42 - 담당: arko-privacy-auditlog (Fork B)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 개인정보 접속기록(PERSONAL_DATA_ACCESS_LOG) 조회결과 다운로드 시,
-- 다운로드 사유(필수)·다운로드자·일시·IP·건수를 이력으로 남긴다.
-- 사유 미입력 시 다운로드는 서비스 계층에서 차단(본 테이블 미적재).
-- [원칙] 개인정보 본문 미적재 — 다운로드 검색조건 요약(SEARCH_COND)만 보관.
-- 이 테이블은 INSERT/SELECT 만 수행(위·변조 방지, UPDATE/DELETE 미정의).
-- =====================================================================
CREATE TABLE IF NOT EXISTS PDAL_DOWNLOAD_HISTORY (
DL_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '다운로드 이력 일련번호(PK)',
DOWNLOADER_ID VARCHAR(20) NOT NULL COMMENT '다운로드자 계정ID',
DOWNLOAD_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '다운로드 일시',
DOWNLOADER_IP VARCHAR(45) NULL COMMENT '다운로드자 접속 IP(IPv6 대응)',
DL_REASON VARCHAR(500) NOT NULL COMMENT '다운로드 사유(필수 — 미입력 시 차단)',
SEARCH_COND VARCHAR(1000) NULL COMMENT '다운로드 검색조건 요약(개인정보 본문 미포함)',
RESULT_CNT INT NOT NULL DEFAULT 0 COMMENT '다운로드 건수',
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록 일시',
PRIMARY KEY (DL_ID),
KEY IX_PDDH_DT (DOWNLOAD_DT),
KEY IX_PDDH_USER (DOWNLOADER_ID, DOWNLOAD_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='개인정보 접속기록 다운로드 이력(#42, SER-002)';
-- [Oracle] 시퀀스 필요:
-- CREATE SEQUENCE PDAL_DOWNLOAD_HISTORY_SEQ START WITH 1 INCREMENT BY 1 NOCACHE;
-- ─────────────────────────────────────────────────────────────
-- [privacy.inspection_report_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 개인정보 접속기록 정기점검 리포트 : PDAL_INSPECTION_REPORT
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-004 / SER-002)
-- 개발항목 #43 - 담당: arko-privacy-auditlog (Fork B)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 월 1회 정기점검 배치가 전월분 접속기록을 집계해 1행으로 적재한다.
-- "월 1회 이상 점검 내역"을 화면 출력으로 제출(개인정보보호위 점검 대응).
--
-- [집계원] PERSONAL_DATA_ACCESS_LOG(#30) + 해시체인 무결성 검증(#40) 재사용.
-- [자리표시] 이상징후 5유형(#35)·다운로드 이력(#42)은 미구현 → 컬럼만 두고 배치가 0/요약만 적재.
-- [화면입력] INSPECTOR_ID/INSPECT_RESULT/ACTN_MATTER 는 배치가 아닌 점검자가 화면(#48)에서 입력.
-- =====================================================================
CREATE TABLE IF NOT EXISTS PDAL_INSPECTION_REPORT (
RPT_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '리포트 일련번호(PK)',
RPT_YM CHAR(6) NOT NULL COMMENT '점검 대상 연월(전월, yyyyMM)',
PERIOD_FROM DATETIME NOT NULL COMMENT '집계 시작일시',
PERIOD_TO DATETIME NOT NULL COMMENT '집계 종료일시',
TOTAL_LOG_CNT INT NOT NULL DEFAULT 0 COMMENT '총 접속기록 건수',
FAIL_CNT INT NOT NULL DEFAULT 0 COMMENT '처리결과 F(실패/차단) 건수 = 이상징후 일부',
ABNORMAL_SUMMARY VARCHAR(2000) NULL COMMENT '이상징후 5유형 요약(#35 연계 전 자리표시)',
INTEGRITY_RESULT VARCHAR(20) NOT NULL DEFAULT 'OK' COMMENT '해시체인 무결성: OK / VIOLATION',
INTEGRITY_MISMATCH_ID BIGINT NULL COMMENT '최초 불일치 LOG_ID(위반 시)',
INTEGRITY_TOTAL_CNT INT NOT NULL DEFAULT 0 COMMENT '무결성 검증 대상 건수',
HANDLER_SUMMARY VARCHAR(4000) NULL COMMENT '취급자별 처리현황 요약',
DOWNLOAD_CNT INT NOT NULL DEFAULT 0 COMMENT '다운로드 이력 건수(#42 연계 전 0)',
INSPECTOR_ID VARCHAR(20) NULL COMMENT '점검자 계정ID(화면 입력, #44)',
INSPECT_RESULT VARCHAR(500) NULL COMMENT '점검결과(화면 입력, #44)',
ACTN_MATTER VARCHAR(1000) NULL COMMENT '조치사항(화면 입력, #44)',
INSPECT_DT DATETIME NULL COMMENT '점검일시(점검결과 입력 시각, #44 감사증빙)',
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '리포트 생성일시',
PRIMARY KEY (RPT_ID),
UNIQUE KEY UX_PIR_YM (RPT_YM),
KEY IX_PIR_REGDT (REG_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='개인정보 접속기록 정기점검 리포트(#43, 월 1회)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.pdal_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 개인정보 접속기록 표준 테이블 : PERSONAL_DATA_ACCESS_LOG
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-004 / DBR-001 / SER-002)
-- 개발항목 #30 - 담당: arko-privacy-auditlog (Fork B)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [설계 원칙] 신규 스키마 설계가 아니라 기존 표준 PERSONAL_DATA_DML_HISTORY 를
-- 표준 접속기록 테이블로 "승격" + 보관/무결성 컬럼 확장이다.
--
-- [기존 PERSONAL_DATA_DML_HISTORY → PERSONAL_DATA_ACCESS_LOG 컬럼 매핑]
-- CHG_USER_ID → CONECT_ID (① 사용자 식별자)
-- CREATE_DATE → CONECT_DT (② 접속일시, SYSDATE())
-- CHG_CONECT_IP → CONECT_IP (③ 접속지 IP) * VARCHAR(45) IPv6 대응
-- UI_GUBUN → JOB_SE_CODE/NM (④ 수행업무 유형) * 업무유형 매핑 테이블로 코드/명 분리
-- ESNTL_ID → TRGET_ID (⑤ 처리대상 식별자)
-- DML_GUBUN → DML_GUBUN (처리구분 S/I/U/D 그대로 계승)
-- CHG_USER_SE → USER_SE (처리자 구분)
-- [신규 확장 컬럼]
-- TRGET_CN - 처리대상 건수/범위 (⑤ 보강)
-- PROCESS_RESULT - 처리결과 S:성공 F:실패/차단 (비정상접근 탐지 5유형 적재 겸용)
-- HASH_VAL - 위·변조 방지 해시 (SHA-256, 개발항목 #39)
-- PREV_HASH_VAL - 직전 레코드 해시 (해시체인)
--
-- [보관·파기] 730일 보관 전제. 월 단위 RANGE COLUMNS 파티셔닝으로
-- 파기 배치가 만료 파티션을 ALTER TABLE ... DROP PARTITION 으로 제거
-- (개별 DELETE 미사용). 애플리케이션 계층에 UPDATE/DELETE statement 미정의.
-- [분리 원칙] COMTNSYSLOG 의 7일 삭제정책은 본 테이블에 절대 적용하지 않는다.
-- =====================================================================
CREATE TABLE IF NOT EXISTS PERSONAL_DATA_ACCESS_LOG
(
LOG_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '로그 일련번호',
CONECT_ID VARCHAR(20) NOT NULL COMMENT '① 사용자 식별자(ID)',
CONECT_DT DATETIME NOT NULL COMMENT '② 접속일시(파티션 키)',
CONECT_IP VARCHAR(45) NOT NULL COMMENT '③ 접속지 정보(IP, IPv6 대응)',
JOB_SE_CODE VARCHAR(30) NOT NULL COMMENT '④ 수행업무 유형 코드',
JOB_SE_NM VARCHAR(100) NOT NULL COMMENT '④ 수행업무 유형 명(화면/기능명)',
TRGET_ID VARCHAR(50) NULL COMMENT '⑤ 처리대상 식별자',
TRGET_CN VARCHAR(200) NULL COMMENT '⑤ 처리대상 건수/범위',
DML_GUBUN CHAR(1) NOT NULL COMMENT '처리구분 S:조회 I:등록 U:수정 D:삭제',
PROCESS_RESULT CHAR(1) NOT NULL DEFAULT 'S' COMMENT '처리결과 S:성공 F:실패/차단',
USER_SE VARCHAR(10) NULL COMMENT '처리자 구분',
HASH_VAL VARCHAR(64) NOT NULL COMMENT '위·변조 방지 해시(SHA-256 Hex)',
PREV_HASH_VAL VARCHAR(64) NULL COMMENT '직전 레코드 해시(해시체인)',
PRIMARY KEY (LOG_ID, CONECT_DT),
KEY IDX_PDAL_01 (CONECT_ID, CONECT_DT), -- 접속자 ID 검색 (현행 CONECT_MTHD LIKE 결함 해소)
KEY IDX_PDAL_02 (CONECT_DT), -- 기간 검색 / 파티션 프루닝
KEY IDX_PDAL_03 (TRGET_ID, CONECT_DT), -- 처리대상 추적
KEY IDX_PDAL_04 (JOB_SE_CODE, CONECT_DT) -- 업무유형/이상탐지유형 집계
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
COMMENT='개인정보 접속기록 표준 테이블(PERSONAL_DATA_DML_HISTORY 승격, 730일 보관)'
PARTITION BY RANGE COLUMNS(CONECT_DT)
(
-- 파티션 키(CONECT_DT)를 PK에 포함해야 RANGE COLUMNS 파티셔닝 성립 (MySQL/MariaDB 제약)
-- 월 단위 사전 생성. 유지보수 배치가 매월 다음 달 파티션을 ADD PARTITION 으로 선행 생성하고,
-- 파기 배치(월 1회)가 보관기간(기본 730일) 경과 파티션을 DROP PARTITION 으로 제거한다.
-- ※ MAXVALUE 파티션은 두지 않는다 — 만료 데이터가 catch-all 에 갇혀 DROP 불가해지는 것을 방지.
PARTITION p202607 VALUES LESS THAN ('2026-08-01'),
PARTITION p202608 VALUES LESS THAN ('2026-09-01'),
PARTITION p202609 VALUES LESS THAN ('2026-10-01'),
PARTITION p202610 VALUES LESS THAN ('2026-11-01'),
PARTITION p202611 VALUES LESS THAN ('2026-12-01'),
PARTITION p202612 VALUES LESS THAN ('2027-01-01'),
PARTITION p202701 VALUES LESS THAN ('2027-02-01'),
PARTITION p202702 VALUES LESS THAN ('2027-03-01')
)
;
-- ---------------------------------------------------------------------
-- [유지보수 참고] 월별 파티션 추가/삭제 예시 (배치가 동적으로 수행)
--
-- -- 다음 달 파티션 선행 생성
-- ALTER TABLE PERSONAL_DATA_ACCESS_LOG
-- ADD PARTITION (PARTITION p202703 VALUES LESS THAN ('2027-04-01'));
--
-- -- 보관기간(730일) 경과 파티션 파기
-- ALTER TABLE PERSONAL_DATA_ACCESS_LOG DROP PARTITION p202607;
-- ---------------------------------------------------------------------
-- ─────────────────────────────────────────────────────────────
-- [privacy.pdal_purge_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 개인정보 접속기록 파기·증설 이력 : PDAL_PURGE_HISTORY
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-004 / DBR-001)
-- 개발항목 #37 - 담당: arko-privacy-auditlog (Fork B)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] PERSONAL_DATA_ACCESS_LOG 의 보관정책(730일) 파기 배치가 수행한
-- 파티션 DROP(파기)·ADD(선행 증설) 내역을 증빙으로 기록한다.
-- 개인정보 접속기록의 유일한 삭제 경로가 "파티션 DROP"임을 감사에서 입증하는 근거.
--
-- [원칙] 접속기록 본문(PERSONAL_DATA_ACCESS_LOG)에는 UPDATE/DELETE 를 정의하지 않으며,
-- 파기는 개별 DELETE 가 아니라 파티션 단위 DROP 으로만 수행한다(개발항목 #37).
-- =====================================================================
CREATE TABLE IF NOT EXISTS PDAL_PURGE_HISTORY
(
PURGE_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '이력 일련번호',
PARTITION_NAME VARCHAR(64) NOT NULL COMMENT '대상 파티션명(예: p202607)',
BOUNDARY_DT VARCHAR(30) NULL COMMENT '파티션 상한 경계값(LESS THAN, 예: 2026-08-01)',
PURGE_TYPE VARCHAR(10) NOT NULL COMMENT '유형 DROP:파기 ADD:선행증설',
PURGE_DT DATETIME NOT NULL COMMENT '수행 일시',
NOTE VARCHAR(300) NULL COMMENT '비고(보관기간·사유)',
PRIMARY KEY (PURGE_ID),
KEY IDX_PDAL_PURGE_01 (PURGE_DT),
KEY IDX_PDAL_PURGE_02 (PARTITION_NAME)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
COMMENT='개인정보 접속기록 파티션 파기·증설 이력(개발항목 #37, 파기 증빙)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.otp_device_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- OTP(2단계 인증) 비밀키 영구 저장 테이블
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-003 / IAR-001, 추가 인증수단 1차 OTP)
-- 개발항목 No.18 후속 - 담당: arko-privacy-authctrl
-- 작성일: 2026-07-22 DB: MySQL 5.7/8 / MariaDB
-- ---------------------------------------------------------------------
-- [배경] OtpSecretStore 기본 구현이 InMemory(재기동 시 유실)라 이를 대체하는
-- DB 영속 저장소(DaoOtpSecretStore)가 사용하는 테이블.
-- [보안] SECRET_ENC 는 OtpSecretCipher(AES-GCM) 암호문(Base64)만 저장한다.
-- 평문 비밀키/OTP 코드는 저장하지 않는다(SER-003).
-- [키] UNIQ_ID = 개인정보취급자 고유식별자(ESNTL_ID), 취급자당 1건(PK).
-- =====================================================================
CREATE TABLE IF NOT EXISTS OTP_DEVICE (
UNIQ_ID VARCHAR(20) NOT NULL COMMENT '개인정보취급자 고유식별자(ESNTL_ID, PK)',
SECRET_ENC VARCHAR(255) NOT NULL COMMENT 'OTP 비밀키 AES-GCM 암호문(Base64) - 평문 금지',
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록(발급) 일시',
LAST_VERIFY_DT DATETIME NULL COMMENT '최종 검증 성공 일시(참고용)',
USE_CNT INT NOT NULL DEFAULT 0 COMMENT '검증 성공 누적 횟수(참고용)',
DEVICE_LABEL VARCHAR(100) NULL COMMENT '기기/계정 라벨(참고용)',
USE_YN CHAR(1) NOT NULL DEFAULT 'Y' COMMENT '사용여부 Y:사용 N:해지',
PRIMARY KEY (UNIQ_ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='OTP 2단계 인증 비밀키 영구 저장(개인정보보호 SFR-003 No.18)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.diapause_emplyr_extend_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 휴면(장기 미사용) 대상 확장 : COMTNDIAPAUSEMBER 회원유형 구분 컬럼 추가
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-002, 장기 미사용 계정 관리)
-- 개발항목 #7 - 담당: arko-privacy-authctrl (Fork A)
-- 작성일: 2026-07-21 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [배경] 기존 휴면 대상은 일반회원(COMTNGNRLMBER)뿐이며, 휴면 테이블
-- COMTNDIAPAUSEMBER 에는 회원유형 구분 컬럼이 없다. 개인정보취급자
-- (COMTNEMPLYRINFO)를 휴면 대상에 포함(#7)하면서 일반회원 휴면/해제
-- 흐름과 혼선되지 않도록 회원유형 구분 컬럼을 신설한다.
--
-- [안전] DEFAULT 'GNR'(일반회원)로 신설 → 기존 적재 행은 자동 'GNR' 처리되어
-- 기존 selectDiapauseList(일반회원 join COMTNGNRLMBER) 흐름 무영향.
-- 취급자 휴면 행은 'EMP'로 적재하여 별도 select/notify 경로로 처리한다.
--
-- [멱등] MySQL 8 은 ADD COLUMN/INDEX IF NOT EXISTS 미지원 → information_schema 로
-- 존재를 확인해 없을 때만 실행한다(재실행 안전, MySQL 8 / MariaDB 공통).
-- =====================================================================
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNDIAPAUSEMBER' AND COLUMN_NAME = 'MBER_TY_CODE') > 0,
'SELECT ''SKIP: COMTNDIAPAUSEMBER.MBER_TY_CODE already exists'' AS msg',
'ALTER TABLE COMTNDIAPAUSEMBER
ADD COLUMN MBER_TY_CODE CHAR(3) NOT NULL DEFAULT ''GNR''
COMMENT ''회원유형 GNR:일반회원 EMP:개인정보취급자'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- 조회 성능(취급자/일반 분리 조회) 보조 인덱스
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNDIAPAUSEMBER' AND INDEX_NAME = 'IX_DIAPAUSE_MBERTY') > 0,
'SELECT ''SKIP: IX_DIAPAUSE_MBERTY already exists'' AS msg',
'ALTER TABLE COMTNDIAPAUSEMBER
ADD INDEX IX_DIAPAUSE_MBERTY (MBER_TY_CODE, DIAPAUSE_STAT)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- ─────────────────────────────────────────────────────────────
-- [privacy.account_mng_policy_purge_useyn_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 계정 관리 정책 확장 : ACCOUNT_MNG_POLICY 에 파기 배치 사용여부 컬럼 추가
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-002, 개발항목 #12)
-- 작성일: 2026-07-28
-- ---------------------------------------------------------------------
-- 파기(#12)는 되돌릴 수 없는 처리라 배치를 항상 등록해 두되 실제 수행 여부를
-- 이 플래그로 제어한다('N'이면 서비스가 즉시 0건 반환). 기본 'N'이므로 설치
-- 직후에는 아무것도 파기되지 않으며, 관리자가 계정 관리 정책 화면(#13)에서
-- '사용'으로 바꿔야 시작된다. 파기 기준일은 기존 DELETE_PRESERVE_DAY 를 사용.
--
-- ※ 신규 설치(위 PART 1 에서 ACCOUNT_MNG_POLICY 를 막 생성한 경우)에는 컬럼이
-- 없으므로 추가되고, 기존 환경 재실행 시에는 존재 확인 후 건너뛴다(재실행 안전).
-- =====================================================================
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ACCOUNT_MNG_POLICY' AND COLUMN_NAME = 'PURGE_USE_YN') > 0,
'SELECT ''SKIP: ACCOUNT_MNG_POLICY.PURGE_USE_YN already exists'' AS msg',
'ALTER TABLE ACCOUNT_MNG_POLICY
ADD COLUMN PURGE_USE_YN CHAR(1) NOT NULL DEFAULT ''N''
COMMENT ''장기 미사용 계정 파기 배치 사용여부 Y:사용 N:미사용(#12)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- ─────────────────────────────────────────────────────────────
-- [privacy.account_delete_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 장기 미사용 계정 개인정보 파기 이력 : ACCOUNT_DELETE_HISTORY
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-002, 개발항목 #12 계정 삭제 기능)
-- 작성일: 2026-07-28 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [목적] 휴면 확정(COMTNDIAPAUSEMBER.DIAPAUSE_STAT='P') 후 정책상 보존기간
-- (ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY)이 경과한 계정의 개인정보를
-- 파기(마스킹)한 이력을 남긴다. 실제 파기 방식은 행 DELETE가 아니라
-- PII 컬럼 마스킹(기존 회원탈퇴 MBER_STTUS='D' 컨벤션과 동일) — 다른
-- 테이블(게시글·마일리지·활동확인 등)이 MBER_ID/EMPLYR_ID를 참조하는
-- 소프트 조인이 많아 행 자체를 지우면 그쪽이 깨지기 때문.
--
-- [보존기간] ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY 값을 실행 시점에 그대로
-- 조회해 사용(하드코딩 없음). 이 테이블의 PRESERVE_DAY 컬럼은
-- 그 시점에 적용된 값의 스냅샷(정책이 나중에 바뀌어도 이력은 불변).
--
-- [처리량 제한] 배치가 오래 정지돼 있다가 재가동되는 날 백로그가 한꺼번에
-- 몰리는 것을 막기 위해 1회 처리건수를 상한(LIMIT 200)으로 제한.
--
-- ★ 배치(BatchScheduler.purgeExpiredAccountScheduler)는 파기가 되돌릴 수 없는
-- 처리라 기본 비활성(@Scheduled 주석) 상태다. 보존기간 확정·운영협의 후 활성화.
-- =====================================================================
CREATE TABLE IF NOT EXISTS ACCOUNT_DELETE_HISTORY (
DEL_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '파기 일련번호(PK)',
MBER_ID VARCHAR(32) NOT NULL COMMENT '파기 대상 계정ID(MBER_ID/EMPLYR_ID)',
MBER_TY_CODE CHAR(3) NOT NULL COMMENT '회원유형 GNR:일반회원 EMP:개인정보취급자',
ESNTL_ID VARCHAR(20) NULL COMMENT '고유식별자(파기 시점 스냅샷, 참고용)',
DIAPAUSE_DT DATETIME NULL COMMENT '휴면 확정 기준일시(파기 판단 기준)',
PRESERVE_DAY INT NOT NULL COMMENT '파기 시점 적용된 보존기간(정책값 스냅샷, 일)',
DEL_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '파기 처리 일시',
PRIMARY KEY (DEL_ID),
KEY IX_ADH_MBER (MBER_ID),
KEY IX_ADH_DELDT (DEL_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='장기 미사용 계정 개인정보 파기 이력(개인정보보호 SFR-002 #12)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.login_history_extend_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 인증 실패 이력 확장 : LOGIN_HISTORY (IP 기록 · 보관기간)
-- 문화품앗이 개인정보보호 기능개선 사업 (SER-001, 개발항목 #22-1)
-- 작성일: 2026-08-05
-- ---------------------------------------------------------------------
-- [목적] #22 로 살아난 실패이력 적재에 ① 접속 IP ② 보존(성공 시 DELETE→소프트
-- 리셋) ③ 보관기간 정책을 추가한다. 잠금 카운트는 RESET_YN='N' 인
-- 당일 행만 세므로 계정잠금 동작은 종전과 동일하다.
--
-- ★ 이 ALTER 는 sqlmap 동반 배포 필수 ★
-- loginDAO.insertFailHistory 가 컬럼목록 없는 `VALUES(...)` 3값 INSERT 라
-- 컬럼 추가 후 ERROR 1136(Column count doesn't match)로 실패한다.
-- 컬럼 명시 INSERT 로 교체된 EgovLoginUsr_SQL_Mysql.xml 과 함께 배포할 것.
--
-- [재실행] MySQL 8 은 ADD COLUMN/KEY IF NOT EXISTS 미지원 → 항목마다 information_schema
-- 로 존재를 확인해 없을 때만 실행한다(일부만 적용된 환경도 나머지가 채워짐).
-- =====================================================================
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'LOGIN_HISTORY' AND COLUMN_NAME = 'CONECT_IP') > 0,
'SELECT ''SKIP: LOGIN_HISTORY.CONECT_IP already exists'' AS msg',
'ALTER TABLE LOGIN_HISTORY
ADD COLUMN CONECT_IP VARCHAR(45) NULL COMMENT ''접속 IP(#22-1, ClientIpResolver 취득)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'LOGIN_HISTORY' AND COLUMN_NAME = 'RESET_YN') > 0,
'SELECT ''SKIP: LOGIN_HISTORY.RESET_YN already exists'' AS msg',
'ALTER TABLE LOGIN_HISTORY
ADD COLUMN RESET_YN CHAR(1) NOT NULL DEFAULT ''N''
COMMENT ''잠금카운터 리셋여부 Y:성공으로 리셋됨 N:유효(#22-1)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'LOGIN_HISTORY' AND INDEX_NAME = 'IX_LOGIN_HISTORY_01') > 0,
'SELECT ''SKIP: IX_LOGIN_HISTORY_01 already exists'' AS msg',
'ALTER TABLE LOGIN_HISTORY ADD KEY IX_LOGIN_HISTORY_01 (CREATE_DATE)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'LOGIN_HISTORY' AND INDEX_NAME = 'IX_LOGIN_HISTORY_02') > 0,
'SELECT ''SKIP: IX_LOGIN_HISTORY_02 already exists'' AS msg',
'ALTER TABLE LOGIN_HISTORY ADD KEY IX_LOGIN_HISTORY_02 (ID, GUBUN, CREATE_DATE)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ACCOUNT_MNG_POLICY' AND COLUMN_NAME = 'LOGIN_HIST_KEEP_DAY') > 0,
'SELECT ''SKIP: ACCOUNT_MNG_POLICY.LOGIN_HIST_KEEP_DAY already exists'' AS msg',
'ALTER TABLE ACCOUNT_MNG_POLICY
ADD COLUMN LOGIN_HIST_KEEP_DAY INT NOT NULL DEFAULT 730
COMMENT ''인증 실패 이력 보관기간(일, 기본 730=2년, #22-1)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- ─────────────────────────────────────────────────────────────
-- [privacy.conect_log_extend_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 로그인·로그아웃 접속기록 확장 : COMTNLOGINLOG + CONECT_IP_LOCATION
-- 문화품앗이 개인정보보호 기능개선 사업 (SER-001 / SER-002)
-- 개발항목 62 - 관리자 로그인·로그아웃 접속기록 조회·다운로드
-- 작성일: 2026-08-07 DB: MySQL 8 / MariaDB
-- ---------------------------------------------------------------------
-- [AS-IS 실측 2026-08-07] COMTNLOGINLOG 는 이미 살아 있으나(373,959행)
-- CONECT_IP 가 전 건 NULL 이고 접속위치 컬럼이 없다. 인덱스도 PK 하나뿐.
-- 또 CONECT_ID(고유식별자) 적재값이 16자리 hex 라 회원 테이블 4종 어디와도 0건 매칭된다(실측).
-- → 접속 계정ID·사용자구분을 별도 컬럼(CONECT_ACNT_ID / CONECT_USER_SE)에 직접 적재한다.
-- ★★ sqlmap(EgovLoginLog_SQL_Mysql.xml) 동반 배포 필수 ★★
-- logInsertLoginLog 가 CONECT_LC / CONECT_ACNT_ID / CONECT_USER_SE 를 컬럼 목록에 넣도록 바뀐다.
-- DDL 없이 sqlmap 만 올리면 Unknown column 으로 접속기록 적재가 깨진다.
-- =====================================================================
SET @ddl := IF(
(SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND COLUMN_NAME = 'CONECT_IP') >= 45,
'SELECT ''SKIP: COMTNLOGINLOG.CONECT_IP already >= 45'' AS msg',
'ALTER TABLE COMTNLOGINLOG
MODIFY COLUMN CONECT_IP VARCHAR(45) NULL COMMENT ''접속 IP(IPv6 대응)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND COLUMN_NAME = 'CONECT_LC') > 0,
'SELECT ''SKIP: COMTNLOGINLOG.CONECT_LC already exists'' AS msg',
'ALTER TABLE COMTNLOGINLOG
ADD COLUMN CONECT_LC VARCHAR(100) NULL COMMENT ''접속위치명(적재시점 스냅샷)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND COLUMN_NAME = 'CONECT_ACNT_ID') > 0,
'SELECT ''SKIP: COMTNLOGINLOG.CONECT_ACNT_ID already exists'' AS msg',
'ALTER TABLE COMTNLOGINLOG
ADD COLUMN CONECT_ACNT_ID VARCHAR(20) NULL COMMENT ''접속 계정ID(로그인 ID)'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND COLUMN_NAME = 'CONECT_USER_SE') > 0,
'SELECT ''SKIP: COMTNLOGINLOG.CONECT_USER_SE already exists'' AS msg',
'ALTER TABLE COMTNLOGINLOG
ADD COLUMN CONECT_USER_SE VARCHAR(3) NULL COMMENT ''사용자구분 USR/GNR/GRO/ENT'''
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND INDEX_NAME = 'IX_CLG_ACNT_DT') > 0,
'SELECT ''SKIP: IX_CLG_ACNT_DT already exists'' AS msg',
'CREATE INDEX IX_CLG_ACNT_DT ON COMTNLOGINLOG (CONECT_ACNT_ID, CREAT_DT)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND INDEX_NAME = 'IX_CLG_DT') > 0,
'SELECT ''SKIP: IX_CLG_DT already exists'' AS msg',
'CREATE INDEX IX_CLG_DT ON COMTNLOGINLOG (CREAT_DT)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND INDEX_NAME = 'IX_CLG_ID_DT') > 0,
'SELECT ''SKIP: IX_CLG_ID_DT already exists'' AS msg',
'CREATE INDEX IX_CLG_ID_DT ON COMTNLOGINLOG (CONECT_ID, CREAT_DT)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND INDEX_NAME = 'IX_CLG_MTHD_DT') > 0,
'SELECT ''SKIP: IX_CLG_MTHD_DT already exists'' AS msg',
'CREATE INDEX IX_CLG_MTHD_DT ON COMTNLOGINLOG (CONECT_MTHD, CREAT_DT)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'COMTNLOGINLOG' AND INDEX_NAME = 'IX_CLG_LC') > 0,
'SELECT ''SKIP: IX_CLG_LC already exists'' AS msg',
'CREATE INDEX IX_CLG_LC ON COMTNLOGINLOG (CONECT_LC)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS CONECT_IP_LOCATION (
LOC_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '일련번호(PK)',
IP_CIDR VARCHAR(45) NOT NULL COMMENT 'IP 대역(CIDR 표기)',
LOC_NM VARCHAR(100) NOT NULL COMMENT '접속위치명',
LOC_SE CHAR(1) NOT NULL DEFAULT 'E' COMMENT '내외부 구분 I:내부망 E:외부',
SORT_ORDR INT NOT NULL DEFAULT 0 COMMENT '정렬순서',
USE_AT CHAR(1) NOT NULL DEFAULT 'Y' COMMENT '사용여부 Y/N',
REMARK VARCHAR(200) NULL COMMENT '비고',
REGISTER_ID VARCHAR(20) NULL COMMENT '등록자 계정ID',
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',
UPDUSR_ID VARCHAR(20) NULL COMMENT '최종 변경자 계정ID',
UPDT_DT DATETIME NULL COMMENT '최종 변경일시',
PRIMARY KEY (LOC_ID),
UNIQUE KEY UK_CIL_CIDR (IP_CIDR)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='접속위치 매핑(IP 대역 → 위치명) — 로그인·로그아웃 접속기록 62';
-- ─────────────────────────────────────────────────────────────
-- [privacy.author_hist_download_job_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 권한변경 이력 CSV 비동기 다운로드 작업 : AUTHOR_HIST_DOWNLOAD_JOB
-- 문화품앗이 개인정보보호 기능개선 사업 (SFR-001 / 개발항목 6)
-- ---------------------------------------------------------------------
-- 종전 동기 다운로드는 조건 전체를 메모리에 올려 응답에 실었다. 이 테이블은
-- '다운로드 요청'을 작업으로 등록해 두고 백그라운드가 파일을 만든 뒤, 완료된 것만
-- 내려받게 하기 위한 작업 대장이다. 상태 전이: W(대기) -> R(생성중) -> S(완료) -> X(파기), 실패는 F.
-- FILE_PATH 는 반드시 웹 루트 밖(Globals.privacyDownloadPath). 파일은 암호화 저장하고
-- EXPIRE_DT 경과분은 파기 배치가 삭제한다. 내려받기 증빙은 AUTHOR_HIST_DOWNLOAD_HISTORY 가 담당.
-- =====================================================================
CREATE TABLE IF NOT EXISTS AUTHOR_HIST_DOWNLOAD_JOB (
JOB_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '작업 일련번호(PK)',
REQUESTER_ID VARCHAR(20) NOT NULL COMMENT '요청자 계정ID(취급자)',
REQUESTER_IP VARCHAR(45) NULL COMMENT '요청 IP(IPv6 대응 45)',
DL_REASON VARCHAR(500) NOT NULL COMMENT '다운로드 사유(필수)',
SEARCH_COND VARCHAR(1000) NULL COMMENT '검색조건 요약(표시용)',
F_TRGET_ID VARCHAR(50) NULL COMMENT '조건: 대상자ID(계정ID 또는 고유식별자)',
F_CHANGER_ID VARCHAR(50) NULL COMMENT '조건: 변경자 계정ID',
F_CHG_SE_CODE CHAR(1) NULL COMMENT '조건: 변경구분 G/M/R',
F_FROM_DT VARCHAR(19) NULL COMMENT '조건: 기간 시작',
F_TO_DT VARCHAR(19) NULL COMMENT '조건: 기간 종료',
STATUS CHAR(1) NOT NULL DEFAULT 'W' COMMENT '상태 W:대기 R:생성중 S:완료 F:실패 X:파기',
TOTAL_CNT INT NOT NULL DEFAULT 0 COMMENT '대상 총건수',
DONE_CNT INT NOT NULL DEFAULT 0 COMMENT '기록 완료 건수(진행률)',
FILE_NM VARCHAR(200) NULL COMMENT '내려받을 때 보여줄 파일명',
FILE_PATH VARCHAR(300) NULL COMMENT '서버 저장 절대경로(웹 루트 밖, 암호화 저장)',
FILE_SIZE BIGINT NOT NULL DEFAULT 0 COMMENT '암호화 파일 크기(byte)',
ERR_MSG VARCHAR(500) NULL COMMENT '실패 사유',
DOWNLOAD_CNT INT NOT NULL DEFAULT 0 COMMENT '내려받은 횟수',
LAST_DL_DT DATETIME NULL COMMENT '마지막 내려받은 일시',
REG_DT DATETIME NOT NULL COMMENT '요청 일시',
START_DT DATETIME NULL COMMENT '생성 시작 일시',
END_DT DATETIME NULL COMMENT '생성 종료 일시',
EXPIRE_DT DATETIME NULL COMMENT '파기 예정 일시(경과 시 파일 삭제)',
PRIMARY KEY (JOB_ID),
KEY IX_AHDJ_REQUESTER (REQUESTER_ID, REG_DT),
KEY IX_AHDJ_STATUS (STATUS, REG_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='권한변경 이력 CSV 비동기 다운로드 작업';
-- ─────────────────────────────────────────────────────────────
-- [privacy.account_action_history_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 장기 미사용 계정 조치 이력 : ACCOUNT_ACTION_HISTORY
-- 개발항목 #11 — 미사용계정 현황 화면의 '선택 잠금 / 선택 말소' 조치 기록.
-- 잠금 '해제' 이력은 ACCOUNT_UNLOCK_HISTORY(SER-001 #24)가 담당하며 역할이 다르다.
-- 애플리케이션 계층은 INSERT 만 정의(UPDATE/DELETE statement 없음).
-- =====================================================================
CREATE TABLE IF NOT EXISTS ACCOUNT_ACTION_HISTORY (
ACTION_HIST_ID BIGINT NOT NULL AUTO_INCREMENT COMMENT '조치 이력 일련번호(PK)',
TRGET_ID VARCHAR(50) NOT NULL COMMENT '조치 대상 계정 ID(EMPLYR_ID)',
TRGET_SE VARCHAR(10) NULL DEFAULT 'EMP' COMMENT '대상 회원구분(취급자 EMP)',
ACTION_TY CHAR(1) NOT NULL COMMENT '조치 유형 L:잠금(휴면확정) D:말소',
ACTION_USER_ID VARCHAR(50) NULL COMMENT '조치 처리자(관리자) ID',
ACTION_RESN VARCHAR(500) NULL COMMENT '조치 사유(화면 입력 필수)',
ACTION_IP VARCHAR(45) NULL COMMENT '조치자 접속 IP(IPv6 대응, X-Forwarded-For 보정값)',
NO_CONECT_DAY INT NULL DEFAULT 0 COMMENT '조치 시점의 미접속 일수(사후 검증용 스냅샷)',
ACTION_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '조치 일시',
PRIMARY KEY (ACTION_HIST_ID),
KEY IX_AAH_TRGET (TRGET_ID, ACTION_DT),
KEY IX_AAH_USER (ACTION_USER_ID, ACTION_DT),
KEY IX_AAH_DT (ACTION_DT)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='장기 미사용 계정 조치(잠금/말소) 이력(개인정보보호 SFR-002 #11)';
-- ─────────────────────────────────────────────────────────────
-- [privacy.login_lock_exempt_create_mysql.sql]
-- ─────────────────────────────────────────────────────────────
-- =====================================================================
-- 로그인 실패 계정잠금 예외 : LOGIN_LOCK_EXEMPT (화이트리스트, SER-001 24)
-- ---------------------------------------------------------------------
-- 인증 실패 5회 잠금은 userSe 구분 없이 전 계정에 적용되고, 잠금 해제 화면도
-- 관리자 로그인이 있어야 들어갈 수 있어 최고관리자가 잠기면 아무도 해제할 수 없다.
-- 이 테이블은 그 break-glass 통로다. 잠금 '판정'만 면제하며, 실패 이력(LOGIN_HISTORY)
-- 적재와 2차 인증(OTP)은 예외 계정에도 그대로 적용된다.
-- 시드 없음 — 예외 대상 ID는 관리자 화면(접근통제 정책)에서 운영팀이 직접 등록한다.
-- =====================================================================
CREATE TABLE IF NOT EXISTS LOGIN_LOCK_EXEMPT (
EXEMPT_ID VARCHAR(20) NOT NULL COMMENT '잠금 예외 대상 계정ID(COMTNEMPLYRINFO.EMPLYR_ID)',
EXEMPT_RESN VARCHAR(500) NOT NULL COMMENT '예외 등록 사유(필수 - 감사 대응)',
REG_ID VARCHAR(20) NULL COMMENT '등록한 관리자 계정ID',
REG_DT DATETIME NULL COMMENT '등록일시',
PRIMARY KEY (EXEMPT_ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='로그인 실패 계정잠금 예외 계정(개인정보보호 SER-001 24, 최고관리자 잠김 방지)';
-- =====================================================================
-- PART 2. DML (기준/시드 데이터) ※ 멱등: NOT EXISTS 가드로 중복 적재 방지
-- 수록: PDAL_JOB_MAPPING(업무유형 매핑 13행), ACCOUNT_MNG_POLICY(기본정책 1행)
-- 미수록: OTP_DEVICE / PERSONAL_DATA_ACCESS_LOG / *_HISTORY = 런타임 적재(시드 없음)
-- =====================================================================
-- 1) PDAL_JOB_MAPPING — 개인정보 처리 업무유형 매핑(접속기록 AOP 분류 기준, 필수)
INSERT INTO PDAL_JOB_MAPPING (TARGET_CLASS, METHOD_PTTRN, JOB_SE_CODE, JOB_SE_NM, DML_GUBUN, SORT_ORDR, RM)
SELECT * FROM (
SELECT 'EgovMberManageServiceImpl' AS TARGET_CLASS, 'select' AS METHOD_PTTRN, 'MBER_VIEW' AS JOB_SE_CODE, '회원관리-조회' AS JOB_SE_NM, 'S' AS DML_GUBUN, 10 AS SORT_ORDR, '회원 개인정보 조회' AS RM UNION ALL
SELECT 'EgovMberManageServiceImpl', 'insert', 'MBER_REG', '회원관리-등록', 'I', 10, NULL UNION ALL
SELECT 'EgovMberManageServiceImpl', 'update', 'MBER_MOD', '회원관리-수정', 'U', 10, NULL UNION ALL
SELECT 'EgovMberManageServiceImpl', 'delete', 'MBER_DEL', '회원관리-삭제', 'D', 10, NULL UNION ALL
SELECT 'EgovMberManageServiceImpl', '*', 'MBER_ETC', '회원관리-기타', NULL, 90, '미분류 폴백' UNION ALL
SELECT 'EgovEntrprsManageServiceImpl', 'select', 'ENTRPRS_VIEW', '업체관리-조회', 'S', 10, '업체 담당자 개인정보 조회' UNION ALL
SELECT 'EgovEntrprsManageServiceImpl', 'insert', 'ENTRPRS_REG', '업체관리-등록', 'I', 10, NULL UNION ALL
SELECT 'EgovEntrprsManageServiceImpl', 'update', 'ENTRPRS_MOD', '업체관리-수정', 'U', 10, NULL UNION ALL
SELECT 'EgovEntrprsManageServiceImpl', 'delete', 'ENTRPRS_DEL', '업체관리-삭제', 'D', 10, NULL UNION ALL
SELECT 'EgovEntrprsManageServiceImpl', '*', 'ENTRPRS_ETC', '업체관리-기타', NULL, 90, '미분류 폴백' UNION ALL
SELECT 'MberSsnServiceImpl', '*', 'MBER_SSN', '회원 고유식별정보 처리', NULL, 20, '주민등록번호 등 고유식별정보' UNION ALL
SELECT 'BizMberMergeServiceImpl', '*', 'MBER_MERGE', '회원 통합/이관', NULL, 30, NULL UNION ALL
SELECT 'BizTalentMberManageServiceImpl', '*', 'TALENT_MBER', '재능나눔 회원관리', NULL, 30, NULL
) seed
WHERE NOT EXISTS (SELECT 1 FROM PDAL_JOB_MAPPING pjm WHERE pjm.JOB_SE_CODE = seed.JOB_SE_CODE);
-- 2) ACCOUNT_MNG_POLICY — 장기 미사용 계정 관리 기본정책(단일행 POLICY_ID=1)
INSERT INTO ACCOUNT_MNG_POLICY (POLICY_ID, DORMANT_DAY, ALERT_D1, ALERT_D2, ALERT_D3, DELETE_PRESERVE_DAY, BATCH_CYCLE, UPD_ID, UPD_DT)
SELECT 1, 365, 30, 7, 0, 365, 'DAILY', 'SYSTEM', NOW()
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM ACCOUNT_MNG_POLICY WHERE POLICY_ID = 1);
-- (부록) LOGIN_LOCK_COUNT — 기존 arko 테이블(변동분 아님). 미존재 신규 환경에서만 사용(기본 5):
-- INSERT INTO LOGIN_LOCK_COUNT (LOCK_COUNT)
-- SELECT 5 FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM LOGIN_LOCK_COUNT);
-- 3) CONECT_IP_LOCATION — 접속위치 매핑 기본 시드(개발항목 62)
-- 운영 대역(본부·전산실·지역문화원 등)은 확정 후 같은 형식으로 INSERT 만 추가한다.
-- 매칭이 없으면 소스 fallback(루프백/사설/외부)이 적용되므로 비어 있어도 동작한다.
INSERT INTO CONECT_IP_LOCATION (IP_CIDR, LOC_NM, LOC_SE, SORT_ORDR, USE_AT, REMARK, REGISTER_ID)
SELECT * FROM (
SELECT '127.0.0.0/8' AS IP_CIDR, '서버 로컬' AS LOC_NM, 'I' AS LOC_SE, 1 AS SORT_ORDR,
'Y' AS USE_AT, '루프백 — WAS 자체 호출' AS REMARK, 'SYSTEM' AS REGISTER_ID
) seed
WHERE NOT EXISTS (SELECT 1 FROM CONECT_IP_LOCATION x WHERE x.IP_CIDR = seed.IP_CIDR);
INSERT INTO CONECT_IP_LOCATION (IP_CIDR, LOC_NM, LOC_SE, SORT_ORDR, USE_AT, REMARK, REGISTER_ID)
SELECT * FROM (
SELECT '10.0.0.0/8' AS IP_CIDR, '내부망' AS LOC_NM, 'I' AS LOC_SE, 2 AS SORT_ORDR, 'Y' AS USE_AT, '사설 대역 — 세부 위치는 더 긴 CIDR 로 덮어쓰기' AS REMARK, 'SYSTEM' AS REGISTER_ID
) seed
WHERE NOT EXISTS (SELECT 1 FROM CONECT_IP_LOCATION x WHERE x.IP_CIDR = seed.IP_CIDR);
INSERT INTO CONECT_IP_LOCATION (IP_CIDR, LOC_NM, LOC_SE, SORT_ORDR, USE_AT, REMARK, REGISTER_ID)
SELECT * FROM (
SELECT '172.16.0.0/12' AS IP_CIDR, '내부망' AS LOC_NM, 'I' AS LOC_SE, 3 AS SORT_ORDR, 'Y' AS USE_AT, '사설 대역' AS REMARK, 'SYSTEM' AS REGISTER_ID
) seed
WHERE NOT EXISTS (SELECT 1 FROM CONECT_IP_LOCATION x WHERE x.IP_CIDR = seed.IP_CIDR);
INSERT INTO CONECT_IP_LOCATION (IP_CIDR, LOC_NM, LOC_SE, SORT_ORDR, USE_AT, REMARK, REGISTER_ID)
SELECT * FROM (
SELECT '192.168.0.0/16' AS IP_CIDR, '내부망' AS LOC_NM, 'I' AS LOC_SE, 4 AS SORT_ORDR, 'Y' AS USE_AT, '사설 대역' AS REMARK, 'SYSTEM' AS REGISTER_ID
) seed
WHERE NOT EXISTS (SELECT 1 FROM CONECT_IP_LOCATION x WHERE x.IP_CIDR = seed.IP_CIDR);
-- =====================================================================
-- PART 3. 관리자 메뉴 등록 DML (좌측/상단 메뉴 노출)
-- 구성: 권한관리(9100030) 하위에 순서대로 9100035~9100039 5건 등록
-- 권한 신청·승인 / 권한 변경 이력 / 미사용계정 현황 / 계정 관리 정책 / 인증 실패 이력
-- 원본: src/script/mysql/dml/privacy.menu_author_request_insert_mysql.sql
-- src/script/mysql/dml/privacy.menu_author_history_insert_mysql.sql
-- src/script/mysql/dml/privacy.menu_dormant_status_insert_mysql.sql
-- src/script/mysql/dml/privacy.menu_account_policy_insert_mysql.sql
-- (개별 파일에 화면별 배치 근거·롤백 SQL이 더 상세히 있음 — 여기서는 통합 실행용)
--
-- ★★ 이 DML만으로는 좌측메뉴에 안 뜬다 — left.jsp 수정 배포 필수 ★★
-- src/main/webapp/WEB-INF/jsp/site/mnt/left.jsp 는 메뉴번호 하드코딩 JSP라
-- 권한관리(9100030) 블록에 9100035~9100038 각각의 <c:when> 분기 +
-- 시스템관리/권한관리 상위 하이라이트 조건(param.page eq '910003x')이
-- 반드시 함께 배포돼야 렌더된다. (arko_renual 소스에는 4건 모두 이미 반영 완료 —
-- 운영 배포 시 WAR/배포본에 이 JSP가 포함됐는지만 확인하면 됨)
--
-- [재실행 안전] 모든 INSERT NOT EXISTS 가드 → 반복 실행 무해.
-- [노출권한] 4건 모두 IS_AUTHENTICATED_FULLY, ROLE_ADMIN 동일
-- [Oracle] 대상 운영 DB가 MySQL(csv)이라 Oracle 쌍 미작성
-- =====================================================================
-- ── [1] 프로그램(URL) 등록 (COMTNMENUINFO.PROGRM_FILE_NM → COMTNPROGRMLIST FK, 메뉴보다 먼저) ──
INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL)
SELECT * FROM (
SELECT 'AuthorRequestList' AS PROGRM_FILE_NM, '/mnt/sec/authorRequest/' AS PROGRM_STRE_PATH, '개인정보취급자 권한 관리' AS PROGRM_KOREAN_NM, '개인정보취급자 권한 관리(SFR-001 #46)' AS PROGRM_DC, '/mnt/sec/authorRequest/listView.do?page=9100035' AS URL UNION ALL
SELECT 'AuthorHistoryList', '/mnt/sec/authorHistory/', '권한변경 이력조회', '권한변경 이력조회·검색·다운로드(SFR-001 #6)', '/mnt/sec/authorHistory/listView.do?page=9100036' UNION ALL
SELECT 'DormantStatusList', '/mnt/sec/dormantStatus/', '장기 미사용 관리자 계정 현황', '장기 미사용 관리자 계정 현황 조회(SFR-002 #11)', '/mnt/sec/dormantStatus/listView.do?page=9100037' UNION ALL
SELECT 'AccountPolicySet', '/mnt/sec/accountPolicy/', '계정 관리 정책 설정', '장기 미사용 계정 관리 정책 설정(SFR-002 #13)', '/mnt/sec/accountPolicy/policyView.do?page=9100038'
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST p WHERE p.PROGRM_FILE_NM = seed.PROGRM_FILE_NM);
-- ── [2] 메뉴 등록 : 권한관리(9100030) 하위, MENU_ORDR 5~8 순서 ──
INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM)
SELECT * FROM (
SELECT '개인정보취급자 권한 관리' AS MENU_NM, 'AuthorRequestList' AS PROGRM_FILE_NM, 9100035 AS MENU_NO, 9100030 AS UPPER_MENU_NO, 5 AS MENU_ORDR, '개인정보취급자 권한 부여·변경·말소 현황 및 승인 처리' AS MENU_DC, '/' AS RELATE_IMAGE_PATH, '/' AS RELATE_IMAGE_NM UNION ALL
SELECT '권한변경 이력조회', 'AuthorHistoryList', 9100036, 9100030, 6, '권한변경 이력조회·검색·다운로드', '/', '/' UNION ALL
SELECT '미사용 관리자 계정 현황', 'DormantStatusList', 9100037, 9100030, 7, '장기 미사용 관리자 계정 현황 조회', '/', '/' UNION ALL
SELECT '계정 관리 정책', 'AccountPolicySet', 9100038, 9100030, 8, '장기 미사용 계정 관리 정책 설정', '/', '/'
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO m WHERE m.MENU_NO = seed.MENU_NO);
-- ── [2-1] 명칭 정정(2026-08-10, 재실행 안전/멱등) — 9100037 이 관리자(EMP) 전용 화면임을 명시.
-- 이미 9100037 이 위 INSERT 로 적재된 환경은 NOT EXISTS 가드 때문에 INSERT 값이
-- 반영되지 않으므로 이 UPDATE 가 실제 적용 지점이다. 한글 리터럴은 UNHEX 우회 권장
-- (python3 -c "print('미사용 관리자 계정 현황'.encode('utf-8').hex().upper())").
UPDATE COMTNMENUINFO SET MENU_NM = '미사용 관리자 계정 현황' WHERE MENU_NO = 9100037;
UPDATE COMTNPROGRMLIST SET PROGRM_KOREAN_NM = '장기 미사용 관리자 계정 현황',
PROGRM_DC = '장기 미사용 관리자 계정 현황 조회(SFR-002 #11)'
WHERE PROGRM_FILE_NM = 'DormantStatusList';
-- ── [3] 권한↔메뉴 노출 매핑 (4건 × 2권한, FK 안전) ──
INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE)
SELECT seed.menu_no, g.ac
FROM (SELECT 9100035 AS menu_no UNION ALL SELECT 9100036 UNION ALL SELECT 9100037 UNION ALL SELECT 9100038) seed
CROSS JOIN (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 = seed.menu_no AND x.AUTHOR_CODE = g.ac);
-- ── [4] 인증 실패 이력 조회 메뉴(9100039) — SER-001 #22-1, 2026-08-05 추가 ──
-- 원본: src/script/mysql/dml/privacy.menu_login_fail_history_insert_mysql.sql
-- ★ left.jsp 9100039 <c:when> 분기 동반 배포 필수(위와 동일 제약)
INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL)
SELECT * FROM (
SELECT 'LoginFailHistoryList' AS PROGRM_FILE_NM, '/mnt/sec/loginFailHistory/' AS PROGRM_STRE_PATH,
'인증 실패 이력' AS PROGRM_KOREAN_NM, '로그인 인증 실패 이력 조회(SER-001 #22-1)' AS PROGRM_DC,
'/mnt/sec/loginFailHistory/listView.do?page=9100039' AS URL
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST p WHERE p.PROGRM_FILE_NM = seed.PROGRM_FILE_NM);
INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM)
SELECT * FROM (
SELECT '인증 실패 이력' AS MENU_NM, 'LoginFailHistoryList' AS PROGRM_FILE_NM, 9100039 AS MENU_NO,
9100030 AS UPPER_MENU_NO, 9 AS MENU_ORDR, '로그인 인증 실패 이력 조회' AS MENU_DC,
'/' AS RELATE_IMAGE_PATH, '/' AS RELATE_IMAGE_NM
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO m WHERE m.MENU_NO = seed.MENU_NO);
INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE)
SELECT 9100039, g.ac
FROM (SELECT 'IS_AUTHENTICATED_FULLY' AS ac UNION ALL SELECT 'ROLE_ADMIN') g
WHERE g.ac IN (SELECT AUTHOR_CODE FROM COMTNAUTHORINFO)
AND NOT EXISTS (SELECT 1 FROM COMTNMENUCREATDTLS x
WHERE x.MENU_NO = 9100039 AND x.AUTHOR_CODE = g.ac);
-- ── [5] 개인정보 접속기록 조회·다운로드 메뉴(9100044) — SFR-004 #47, 2026-08-06 추가 ──
-- 원본: src/script/mysql/dml/privacy.menu_access_log_insert_mysql.sql
-- ★ left.jsp 9100044 <c:when> 분기 + 하이라이트 조건 4곳 동반 배포 필수(위와 동일 제약)
-- ※ 9100040~9100043 은 '게시판관리' 그룹이 선점 → 미사용 번호 9100044 사용(MENU_ORDR 10)
INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL)
SELECT * FROM (
SELECT 'AccessLogList' AS PROGRM_FILE_NM, '/mnt/sec/accessLog/' AS PROGRM_STRE_PATH,
'개인정보 접속기록' AS PROGRM_KOREAN_NM, '개인정보 접속기록 조회·다운로드(SFR-004 #47)' AS PROGRM_DC,
'/mnt/sec/accessLog/listView.do?page=9100044' AS URL
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST p WHERE p.PROGRM_FILE_NM = seed.PROGRM_FILE_NM);
INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM)
SELECT * FROM (
SELECT '개인정보 접속기록' AS MENU_NM, 'AccessLogList' AS PROGRM_FILE_NM, 9100044 AS MENU_NO,
9100030 AS UPPER_MENU_NO, 10 AS MENU_ORDR, '개인정보 접속기록 조회·다운로드' AS MENU_DC,
'/' AS RELATE_IMAGE_PATH, '/' AS RELATE_IMAGE_NM
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO m WHERE m.MENU_NO = seed.MENU_NO);
INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE)
SELECT 9100044, g.ac
FROM (SELECT 'IS_AUTHENTICATED_FULLY' AS ac UNION ALL SELECT 'ROLE_ADMIN') g
WHERE g.ac IN (SELECT AUTHOR_CODE FROM COMTNAUTHORINFO)
AND NOT EXISTS (SELECT 1 FROM COMTNMENUCREATDTLS x
WHERE x.MENU_NO = 9100044 AND x.AUTHOR_CODE = g.ac);
-- ── [6] 업무유형 매핑 관리 메뉴(9100045) — SFR-004 #32-1, 2026-08-07 추가 ──
-- 원본: src/script/mysql/dml/privacy.menu_job_mapping_insert_mysql.sql
-- ★ left.jsp 9100045 <c:when> 분기 + 하이라이트 조건 4곳 동반 배포 필수(위와 동일 제약)
-- ※ 접속기록(9100044)의 '수행업무' 분류 기준을 편집하는 화면이라 바로 다음(MENU_ORDR 11)에 배치
INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL)
SELECT * FROM (
SELECT 'JobMappingList' AS PROGRM_FILE_NM, '/mnt/sec/jobMapping/' AS PROGRM_STRE_PATH,
'업무유형 매핑 관리' AS PROGRM_KOREAN_NM, '개인정보 접속기록 업무유형 매핑 관리(SFR-004 #32-1)' AS PROGRM_DC,
'/mnt/sec/jobMapping/listView.do?page=9100045' AS URL
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST p WHERE p.PROGRM_FILE_NM = seed.PROGRM_FILE_NM);
INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM)
SELECT * FROM (
SELECT '업무유형 매핑 관리' AS MENU_NM, 'JobMappingList' AS PROGRM_FILE_NM, 9100045 AS MENU_NO,
9100030 AS UPPER_MENU_NO, 11 AS MENU_ORDR, '접속기록 업무유형 매핑 등록·수정' AS MENU_DC,
'/' AS RELATE_IMAGE_PATH, '/' AS RELATE_IMAGE_NM
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO m WHERE m.MENU_NO = seed.MENU_NO);
INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE)
SELECT 9100045, g.ac
FROM (SELECT 'IS_AUTHENTICATED_FULLY' AS ac UNION ALL SELECT 'ROLE_ADMIN') g
WHERE g.ac IN (SELECT AUTHOR_CODE FROM COMTNAUTHORINFO)
AND NOT EXISTS (SELECT 1 FROM COMTNMENUCREATDTLS x
WHERE x.MENU_NO = 9100045 AND x.AUTHOR_CODE = g.ac);
-- ── [7] 로그인·로그아웃 접속기록 메뉴(9100046) — 개발항목 62, 2026-08-07 추가 ──
-- 원본: src/script/mysql/dml/privacy.menu_conect_log_insert_mysql.sql
-- ★ left.jsp 9100046 <c:when> 분기 + 하이라이트 조건 4곳 동반 배포 필수(위와 동일 제약)
-- ※ 인증 실패 이력(9100039)·개인정보 접속기록(9100044)과 같은 보안 로그 묶음(MENU_ORDR 12)
INSERT INTO COMTNPROGRMLIST (PROGRM_FILE_NM, PROGRM_STRE_PATH, PROGRM_KOREAN_NM, PROGRM_DC, URL)
SELECT * FROM (
SELECT 'ConectLogList' AS PROGRM_FILE_NM, '/mnt/sec/conectLog/' AS PROGRM_STRE_PATH,
'로그인 접속기록' AS PROGRM_KOREAN_NM,
'로그인·로그아웃 접속기록 조회·다운로드(접속ID·접속일시·IP·접속위치, 개발항목 62)' AS PROGRM_DC,
'/mnt/sec/conectLog/listView.do?page=9100046' AS URL
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNPROGRMLIST p WHERE p.PROGRM_FILE_NM = seed.PROGRM_FILE_NM);
INSERT INTO COMTNMENUINFO (MENU_NM, PROGRM_FILE_NM, MENU_NO, UPPER_MENU_NO, MENU_ORDR, MENU_DC, RELATE_IMAGE_PATH, RELATE_IMAGE_NM)
SELECT * FROM (
SELECT '로그인 접속기록' AS MENU_NM, 'ConectLogList' AS PROGRM_FILE_NM, 9100046 AS MENU_NO,
9100030 AS UPPER_MENU_NO, 12 AS MENU_ORDR, '로그인·로그아웃 접속기록 조회' AS MENU_DC,
'/' AS RELATE_IMAGE_PATH, '/' AS RELATE_IMAGE_NM
) seed
WHERE NOT EXISTS (SELECT 1 FROM COMTNMENUINFO m WHERE m.MENU_NO = seed.MENU_NO);
INSERT INTO COMTNMENUCREATDTLS (MENU_NO, AUTHOR_CODE)
SELECT 9100046, g.ac
FROM (SELECT 'IS_AUTHENTICATED_FULLY' AS ac UNION ALL SELECT 'ROLE_ADMIN') g
WHERE g.ac IN (SELECT AUTHOR_CODE FROM COMTNAUTHORINFO)
AND NOT EXISTS (SELECT 1 FROM COMTNMENUCREATDTLS x
WHERE x.MENU_NO = 9100046 AND x.AUTHOR_CODE = g.ac);
-- ── [검증] ──
-- SELECT m.MENU_NO,m.UPPER_MENU_NO,m.MENU_NM,m.MENU_ORDR,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 IN (9100035,9100036,9100037,9100038,9100039,9100044,9100045,9100046) ORDER BY m.MENU_ORDR;
-- ── [롤백] (역순 권장, 4건 일괄) ──
-- DELETE FROM COMTNMENUCREATDTLS WHERE MENU_NO BETWEEN 9100035 AND 9100038;
-- DELETE FROM COMTNMENUINFO WHERE MENU_NO BETWEEN 9100035 AND 9100038;
-- DELETE FROM COMTNPROGRMLIST WHERE PROGRM_FILE_NM IN
-- ('AuthorRequestList','AuthorHistoryList','DormantStatusList','AccountPolicySet');
-- ############################## END ##############################