# HMM 휴가 원장 팀 계층 VPD 설계 (#707)

> 상태: 구현·ADB 적용·배포·검증 완료
> 대상: HMMAIPOC `ADMIN`, `hmm-backoffice.cloud-handson.com`
> Git 기준: `hmm-backoffice`

## 목적

HMM 백오피스의 사용자·역할·보호 객체·행 접근 규칙을 실제 Oracle VPD와 연결한다.
팀장 E1001은 자신과 직접 보고 팀원의 휴가 잔여 및 휴가 신청을 조회하고, 일반 팀원은 자기 행만
조회한다. 유효한 사용자 컨텍스트나 허용 규칙이 없으면 모든 행을 차단한다.

## 조사 결과

- 직원 7명은 `HMM_HR_EMPLOYEES`에 있으며 E1001의 직접 보고 관계가
  `MANAGER_EMPLOYEE_ID = 1`로 저장되어 있다.
- `HMM_LEAVE_BALANCES`는 직원별 1행씩 총 7행이고, `HMM_LEAVE_REQUESTS`는 팀원 6명의 총 8행이다.
- HMM 역할 `HMM_HR_VIEWER`, `HMM_HR_MANAGER`, `HMM_HR_ADMIN`은 존재한다.
- 적용 전 사용자 역할, HMM 토큰, 보호 객체, 행 접근 규칙, VPD 정책은 0건이었다.
- `ADMIN`은 `EXEMPT ACCESS POLICY` 권한이 있어 정책 적용 여부를 검증할 때는 별도의 비면제
  검증 사용자가 필요하다.

## 접근 정책

| 사용자 유형 | 역할 | 저장 규칙 | 결과 |
|---|---|---|---|
| 일반 팀원 | `HMM_HR_VIEWER` | `SELF` | 현재 직원 ID와 같은 `EMPLOYEE_ID` 행 |
| 팀장 | `HMM_HR_MANAGER` | `MANAGED_TEAM` | 현재 직원 본인과 `MANAGER_EMPLOYEE_ID`가 현재 직원인 직접 보고자 행 |
| HR 관리자 | `HMM_HR_ADMIN` | `ALL` | 전체 행. 역할이 실제로 부여된 경우에만 적용 |
| 미인증/무권한 | 없음 | 없음 | `1 = 0` |

E1001에는 `HMM_HR_MANAGER`, E1002~E1007에는 `HMM_HR_VIEWER`를 부여한다. HR 관리자 역할은
미리 규칙만 준비하고 현재 데모 사용자에게 자동 부여하지 않는다.

## DB 구성

### 컨텍스트

`HMM_ACCESS_CTX`는 `HMM_ACCESS_CTX_PKG.SET_USER_BY_BEARER`만이 설정한다.
패키지는 전달된 토큰의 SHA-256 해시를 `HMM_ACCESS_BEARER_TOKENS`와 비교하고 다음 조건을 모두
만족한 경우에만 직원 ID, 사번, 팀 ID를 세션 컨텍스트에 기록한다.

- 토큰이 회수되지 않음
- 만료 시각이 현재보다 이후
- 직원이 `ACTIVE`

오류 또는 누락 토큰은 컨텍스트를 먼저 지우고 실패한다.

### 정책 함수

`HMM_LEAVE_VPD_FILTER`는 현재 컨텍스트의 직원에게 직접 또는 접근 그룹으로 부여된 활성 역할을
구한다. 역할과 `CB_PERMISSION`, `CB_PERMISSION_RULE`에 저장된 대상별 규칙을 결합해
`HMM_LEAVE_BALANCES`와 `HMM_LEAVE_REQUESTS`의 `EMPLOYEE_ID` 조건을 반환한다.

- 한 권한 안의 규칙은 `AND`
- 서로 다른 허용 권한은 `OR`
- 거부 규칙은 허용 결과에서 제외
- 알 수 없는 규칙, 존재하지 않는 컬럼, 빈 컨텍스트는 fail-closed

### 정책 연결

두 휴가 테이블에 `HMM_LEAVE_SCOPE_POLICY`를 `SELECT`, `CONTEXT_SENSITIVE`로 연결한다.
정책 메타데이터와 설명은 백오피스의 보호 객체 및 VPD 화면에서 조회한다.

## 백오피스 변경

- `/permissions`가 HMM 역할과 `CB_PERMISSION*` 호환 저장소의 실제 규칙을 표시한다.
- HMM 행 규칙 선택지는 `ALL`, `MANAGED_TEAM`, `SELF`, `STATIC_SQL`을 우선 제공한다.
- 미리보기는 `HMM_ACCESS_CTX.EMPLOYEE_ID`와 직접 보고 관계를 보여준다.
- 휴가 테이블의 기본 정책 연결은 `HMM_LEAVE_VPD_FILTER`를 선택한다.
- 보호 객체·역할·권한 수정은 기존 감사 및 외부 권한 변경 알림 흐름을 유지한다.

## 배포 순서

1. 애플리케이션 테스트와 SQL 구문 검사를 수행한다.
2. 멱등 SQL `sql/adb/72_hmm_leave_team_vpd.sql`을 `ADMIN`으로 실행한다.
3. VPD 정책·컴파일 상태·백오피스 화면을 확인한다.
4. 백오피스와 같은 SHA-256 저장 규칙으로 E1001과 E1002 임시 검증 토큰을 만든다.
5. 비면제 검증 사용자에서 토큰별 조회 결과를 확인하고 검증 토큰과 사용자를 제거한다.
6. 애플리케이션 JAR를 배포한 뒤 전 메뉴 회귀검사를 수행한다.

## 완료 기준

- E1001: 휴가 잔여 7행, 휴가 신청 8행
- E1002: 휴가 잔여 1행, 휴가 신청 1행
- E1002가 E1003의 행을 조건에 추가 조회해도 0행
- 무컨텍스트: 두 테이블 모두 0행
- 두 정책과 컨텍스트/패키지/함수가 `VALID` 및 `ENABLE=YES`
- `/users`, `/roles`, `/objects`, `/permissions`, `/vpd-policies`, `/vpd-filter-runtime`,
  `/tokens`, `/probe`를 포함한 백오피스 메뉴에 데이터 처리 오류가 없음

## 검증 결과

2026-07-23 배포에서 모든 완료 기준을 충족했다.

- `HMM_ACCESS_CTX_PKG`와 `HMM_LEAVE_VPD_FILTER`는 `VALID`
- 두 `HMM_LEAVE_SCOPE_POLICY`는 `CONTEXT_SENSITIVE`, `ENABLE=YES`
- 비면제 임시 DB 사용자와 원문을 저장하지 않는 임시 토큰으로 확인:
  - 무컨텍스트: 잔여 0행, 신청 0행
  - E1001 팀장: 잔여 7행, 신청 8행
  - E1002 팀원: 잔여 1행, 신청 1행
  - E1002가 E1003 신청을 조건 조회: 0행
  - 무효 토큰: 0행
- 검증용 DB 사용자와 임시 토큰은 즉시 제거했다.
- 자동 테스트 105건이 통과했다.
- 실제 브라우저로 관리 화면 26개와 HMM 정형 원장 6개를 전수 확인했으며 HTTP/화면/브라우저
  오류는 0건이었다.

상세 증거는 `docs/reports/2026-07-23-hmm-leave-team-vpd-verification.md`에 기록한다.

## 보안 및 롤백

- 토큰 원문과 DB 비밀번호를 SQL, Git, 로그에 기록하지 않는다.
- 검증용 DB 사용자와 검증 토큰은 테스트가 끝나면 제거 또는 회수한다.
- 롤백은 두 `HMM_LEAVE_SCOPE_POLICY`를 비활성화하거나 제거한 뒤 사용자 역할·권한 메타데이터를
  유지하는 방식으로 수행한다. 휴가 원장 데이터는 수정하거나 삭제하지 않는다.
