# #740 HMM MCP VPD 실행 경계 복구

## 배경

HMM MCP의 `search_hr_data`는 현재 `ADMIN` JDBC 세션에서
`DBMS_CLOUD_AI_AGENT.RUN_TOOL`을 실행한다. 토큰으로
`HMM_ACCESS_CTX`를 설정해도 `ADMIN`에는 `EXEMPT ACCESS POLICY`가 있으므로
`ADMIN.HMM_LEAVE_BALANCES`와 `ADMIN.HMM_LEAVE_REQUESTS`의 VPD 정책이
실제 조회에 적용되지 않는다.

컨텍스트 값이 올바른 것과 VPD가 적용되는 것은 별개의 조건이다. 보호 테이블을
읽는 최종 SQL은 반드시 `EXEMPT ACCESS POLICY`가 없는 계정의 동일 DB 세션에서
컨텍스트 설정과 함께 실행해야 한다.

## 목표

- `ADMIN`은 Select AI `SHOWSQL` 생성만 담당한다.
- 비면제 런타임 스키마 `CB_ORDS`가 생성 SQL을 읽기 전용으로 실행한다.
- 실행 직전 같은 `CB_ORDS` 세션에서
  `CB_ORDS_HANDLER_PKG.SET_VPD_CONTEXT`를 호출한다.
- 실행 종료 시 성공·실패와 관계없이 `CLEAR_VPD_CONTEXT`를 호출하고 롤백한다.
- 런타임 계정이 `ADMIN`이거나 `EXEMPT ACCESS POLICY`를 가진 경우 fail-closed 한다.
- MCP `search_hr_data`만 새 경계로 전환하며 용어·정책 검색 도구는 기존 경로를 유지한다.

## 구조

```text
MCP tools/call
  └─ bearer token 해시 인증
      ├─ ADMIN / DBMS_CLOUD_AI.GENERATE(..., 'showsql')
      │    └─ 허용된 HMM HR 객체만 포함한 SELECT/WITH 생성
      └─ CB_ORDS JDBC session
           ├─ CB_ORDS_HANDLER_PKG.SET_VPD_CONTEXT('Bearer ...')
           │    └─ ADMIN.HMM_ACCESS_CTX_PKG.SET_USER_BY_BEARER(token)
           ├─ runtime principal/컨텍스트 일치 검증
           ├─ SET TRANSACTION READ ONLY
           ├─ 생성 SQL 실행 → HMM_LEAVE_SCOPE_POLICY 적용
           ├─ ROLLBACK
           └─ CB_ORDS_HANDLER_PKG.CLEAR_VPD_CONTEXT
```

## DB 설계

### 런타임 스키마

`CB_ORDS`에는 다음 최소 권한만 부여한다.

- `CREATE SESSION`
- `ADMIN.CB_ORDS_HANDLER_PKG` 실행
- Select AI 프로필의 승인 객체 6개에 대한 `SELECT`
  - `HMM_ORG_TEAMS`
  - `HMM_HR_EMPLOYEES`
  - `HMM_LEAVE_BALANCES`
  - `HMM_LEAVE_REQUESTS`
  - `HMM_ATTENDANCE_DAILY`
  - `HMM_HR_TERMS`

`EXEMPT ACCESS POLICY`, `SELECT ANY TABLE`, 객체 생성 `ANY` 권한은 부여하지 않는다.
생성 SQL의 스키마 한정 여부에 영향을 받지 않도록 승인 객체에 한해
`CB_ORDS` private synonym을 만든다.

### 컨텍스트 패키지

`ADMIN.CB_ORDS_HANDLER_PKG`는 HTTP Authorization 값에서 Bearer 토큰을
추출한 후 `ADMIN.HMM_ACCESS_CTX_PKG.SET_USER_BY_BEARER`를 호출한다.
토큰 원문은 테이블이나 로그에 저장하지 않는다.

`CB_ORDS`에는 동일 이름의 private synonym만 제공하여 런타임 SQL에서는
`CB_ORDS_HANDLER_PKG.SET_VPD_CONTEXT`로 호출한다. 컨텍스트 패키지 자체의
직접 실행 권한은 런타임 계정에 노출하지 않는다.

`CLEAR_VPD_CONTEXT`는 HMM 컨텍스트와 client identifier를 모두 정리한다.
유효하지 않은 인증 헤더는 컨텍스트를 먼저 지운 뒤 오류로 종료한다.

## 애플리케이션 설계

`backoffice.select-ai` 설정을 두 연결로 분리한다.

- 생성 연결: 기존 `db-url`, `db-username`, `db-password`, `profile`
- 실행 연결: `runtime-db-url`, `runtime-db-username`, `runtime-db-password`

생성 연결은 기존 `BACKOFFICE_DB_*`를 기본값으로 사용할 수 있다. 실행 연결은
명시적으로 설정해야 하며 생성 연결로 자동 폴백하지 않는다.

실행 전 다음을 검증한다.

1. `USER`가 `ADMIN`이 아니다.
2. `SESSION_PRIVS`에 `EXEMPT ACCESS POLICY`가 없다.
3. 패키지 호출 후 `HMM_ACCESS_CTX.EMPLOYEE_CODE`가 토큰 인증 결과와 일치한다.

하나라도 실패하면 SQL을 실행하지 않는다.

## 운영 설정

`search_hr_data`의 `executionType`을 `AGENT_TOOL`에서 `SELECT_AI`로 변경한다.
다른 두 도구는 그대로 유지한다.

필수 환경값:

```properties
BACKOFFICE_SELECT_AI_PROFILE=HMM_HR_DATA_GPT54_PROFILE
BACKOFFICE_SELECT_AI_RUNTIME_DB_URL=<ADB JDBC URL>
BACKOFFICE_SELECT_AI_RUNTIME_DB_USERNAME=CB_ORDS
BACKOFFICE_SELECT_AI_RUNTIME_DB_PASSWORD=<secret>
```

비밀번호는 Git·Redmine·로그에 기록하지 않고 서버 환경 파일에서만 관리한다.

## 검증 기준

1. `CB_ORDS`에 `EXEMPT ACCESS POLICY`가 없음을 확인한다.
2. E1006 토큰으로 `HMM_LEAVE_BALANCES` 조회 결과의 직원은 E1006 한 명뿐이다.
3. E1006 토큰으로 `HMM_LEAVE_REQUESTS` 조회 결과에 E1002가 없다.
4. E1006이 E1002 휴가를 직접 요청하면 0건을 반환한다.
5. 관리자 E1001은 권한 규칙에 따라 자기 자신과 직속 팀원 범위를 조회한다.
6. 무효 토큰, 컨텍스트 불일치, 런타임 권한 오설정은 fail-closed 한다.

## 롤백

- 애플리케이션 환경의 `search_hr_data`를 이전 `AGENT_TOOL` 정의로 되돌리고
  이전 JAR를 재기동한다.
- `CB_ORDS` 스키마는 즉시 삭제하지 않고 계정을 잠가 조사 가능 상태로 보존한다.
- VPD 정책 자체와 기존 `HMM_ACCESS_CTX_PKG`는 변경하지 않는다.
