refs #740: enforce carrier VPD by MCP user
This commit is contained in:
@@ -1,123 +1,52 @@
|
||||
# #740 HMM MCP VPD 실행 경계 복구
|
||||
# #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 정책이
|
||||
실제 조회에 적용되지 않는다.
|
||||
HMM MCP의 인증 사용자를 Oracle DB 세션 사용자 문맥으로 연결하고, HR 및 선사 배정 데이터를
|
||||
Oracle VPD로 제한한다. 팀원은 본인 행, 팀장은 본인과 직속 팀원 행을 조회한다.
|
||||
|
||||
컨텍스트 값이 올바른 것과 VPD가 적용되는 것은 별개의 조건이다. 보호 테이블을
|
||||
읽는 최종 SQL은 반드시 `EXEMPT ACCESS POLICY`가 없는 계정의 동일 DB 세션에서
|
||||
컨텍스트 설정과 함께 실행해야 한다.
|
||||
## 범위와 결정사항
|
||||
|
||||
## 목표
|
||||
- 포털의 사용자 preset은 각각 다른 Bearer token 환경변수를 사용한다.
|
||||
- 백오피스는 token hash로 직원을 식별하고 실제 SELECT와 같은 `CB_ORDS` 연결에
|
||||
`SET_VPD_CONTEXT`를 호출한다.
|
||||
- `ADMIN`은 `EXEMPT ACCESS POLICY`가 있으므로 Select AI `SHOWSQL` 생성만 담당한다.
|
||||
- `HMM_CARRIER_ASSIGNMENTS_V.EMPLOYEE_ID`에 `SELF`, `MANAGED_TEAM`, `ALL` 규칙을 적용한다.
|
||||
- 기존 MCP 이름 `search_carrier_performance`, 자연어 Select AI, 선사 `CARRIER_CODE` 조인과 HTML
|
||||
renderer는 유지한다. 고정 SQL이나 대체 조회 패키지를 만들지 않는다.
|
||||
|
||||
- `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
|
||||
포털 사용자 preset
|
||||
→ 사용자별 Bearer token
|
||||
→ token hash로 EMPLOYEE_ID 확인
|
||||
→ ADMIN Select AI SHOWSQL 생성
|
||||
→ CB_ORDS 연결에서 SET_VPD_CONTEXT
|
||||
→ 생성 SELECT 실행
|
||||
→ HMM_CARRIER_ASSIGNMENTS_V VPD
|
||||
├─ E1001 팀장: 직속 팀원 배정 8건
|
||||
└─ E1002 팀원: 본인 배정 C001·C002 2건
|
||||
→ 허용된 CARRIER_CODE만 원격 KPI와 조인
|
||||
→ HTML renderer
|
||||
```
|
||||
|
||||
## DB 설계
|
||||
핵심은 token 소유자, DB context의 직원, VPD predicate가 동일한 요청 안에서 이어지는 것이다.
|
||||
`SET_VPD_CONTEXT`를 호출해도 `EXEMPT ACCESS POLICY`를 가진 연결에서 SELECT하면 정책이 우회된다.
|
||||
|
||||
### 런타임 스키마
|
||||
## 현재 상태
|
||||
|
||||
`CB_ORDS`에는 다음 최소 권한만 부여한다.
|
||||
2026-08-10 운영 적용 및 MCP 호출 검증을 완료했다.
|
||||
|
||||
- `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`
|
||||
- 사용자별 portal token 5개가 서로 다른 hash로 직원에게 연결됨
|
||||
- `HMM_CARRIER_SCOPE_POLICY` 활성화
|
||||
- MCP E1001: `vpdEnforced=true`, 8건
|
||||
- MCP E1002: `vpdEnforced=true`, 2건, 직원 범위 E1002만 포함
|
||||
- E1002 renderer: 입력 2건, HTML에 C001·C002 포함, E1003 미포함
|
||||
|
||||
`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`는 변경하지 않는다.
|
||||
- [아키텍처와 신뢰 경계](architecture.md)
|
||||
- [적용 및 검증 절차](cookbook.md)
|
||||
- [문제 해결](troubleshooting.md)
|
||||
- [HTML 리포트 후속 처리](../hmm-html-report-mcp/README.md)
|
||||
|
||||
41
docs/design/740-hmm-mcp-vpd-runtime/architecture.md
Normal file
41
docs/design/740-hmm-mcp-vpd-runtime/architecture.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# HMM MCP VPD 아키텍처
|
||||
|
||||
[개요](README.md) · [적용 절차](cookbook.md) · [문제 해결](troubleshooting.md)
|
||||
|
||||
## 권한 흐름
|
||||
|
||||
```text
|
||||
포털 사용자 preset
|
||||
→ 사용자별 Bearer token
|
||||
→ token hash로 EMPLOYEE_ID 식별
|
||||
→ HMM_ACCESS_CTX.EMPLOYEE_ID 설정
|
||||
→ Select AI가 만든 SELECT를 CB_ORDS에서 실행
|
||||
→ HMM_CARRIER_ASSIGNMENTS_V VPD
|
||||
├─ VIEWER: 본인 EMPLOYEE_ID
|
||||
└─ MANAGER: 본인과 직속 팀원 EMPLOYEE_ID
|
||||
→ 허용된 CARRIER_CODE만 원격 선사 실적과 조인
|
||||
→ HTML renderer
|
||||
```
|
||||
|
||||
선사 조회의 자연어 처리와 `CARRIER_CODE` 조인은 기존 Select AI 프로필을 유지한다. 권한 조건을
|
||||
질문이나 고정 SQL에 넣지 않고 Oracle VPD가 세션 사용자 ID로 자동 적용한다.
|
||||
|
||||
## 신뢰 경계
|
||||
|
||||
- 화면에 표시된 사용자 코드는 권한 근거가 아니다. 선택 preset의 전용 Bearer token이 근거다.
|
||||
- 토큰 원문은 서버 환경에만 저장하고 DB에는 SHA-256 hash만 저장한다.
|
||||
- `ADMIN`은 `EXEMPT ACCESS POLICY`가 있으므로 SQL 생성만 수행한다.
|
||||
- 실제 SELECT는 비면제 계정 `CB_ORDS`가 같은 요청의 VPD context를 설정한 뒤 실행한다.
|
||||
- renderer는 이미 필터링된 행만 표현하며 사용자나 권한을 다시 판단하지 않는다.
|
||||
|
||||
## 선사 VPD 규칙
|
||||
|
||||
보호 객체는 `ADMIN.HMM_CARRIER_ASSIGNMENTS_V`, 기준 컬럼은 `EMPLOYEE_ID`다.
|
||||
|
||||
| 역할 | 규칙 | 적용 범위 |
|
||||
|---|---|---|
|
||||
| `HMM_HR_VIEWER` | `SELF` | 현재 인증 사용자 |
|
||||
| `HMM_HR_MANAGER` | `MANAGED_TEAM` | 현재 사용자와 직속 팀원 |
|
||||
| `HMM_HR_ADMIN` | `ALL` | 명시적 관리자 전체 |
|
||||
|
||||
컨텍스트, 활성 역할 또는 허용 규칙이 없으면 `1=0`으로 차단한다.
|
||||
25
docs/design/740-hmm-mcp-vpd-runtime/cookbook.md
Normal file
25
docs/design/740-hmm-mcp-vpd-runtime/cookbook.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# HMM MCP VPD 적용 절차
|
||||
|
||||
[개요](README.md) · [아키텍처](architecture.md) · [문제 해결](troubleshooting.md)
|
||||
|
||||
1. `ADMIN`이 VPD 우회 권한을 가지고 `CB_ORDS`는 가지지 않는지 확인한다.
|
||||
2. `database/adb/84_hmm_carrier_team_vpd.sql`을 ADMIN으로 실행한다.
|
||||
3. 포털 preset마다 `HMM_MCP_BEARER_TOKEN_<EMPLOYEE_CODE>` 전용 토큰을 발급한다.
|
||||
4. 선사 조회 MCP는 기존 `HMM_RDS_FEDERATION_PROFILE`로 `SHOWSQL`을 생성하고 `CB_ORDS`에서 실행한다.
|
||||
운영 도구 정의는 이름과 입력 계약을 유지하고 `executionType`만 `SELECT_AI`로 설정한다.
|
||||
5. 서비스를 재시작하고 동일 질문을 E1001과 E1002로 각각 호출한다.
|
||||
|
||||
성공 기준:
|
||||
|
||||
| 사용자 | 허용된 선사 배정 | 예상 건수 |
|
||||
|---|---|---:|
|
||||
| E1001 팀장 | 직속 팀원 C001~C008 | 8 |
|
||||
| E1002 팀원 | 본인 C001, C002 | 2 |
|
||||
|
||||
첫 조회 결과, renderer 입력, HTML 표의 행 수와 직원 범위가 모두 같아야 한다. 토큰 원문과 DB
|
||||
비밀번호는 명령·로그·문서에 출력하지 않는다.
|
||||
|
||||
MCP 응답에서 `vpdEnforced=true`, `scopeEmployeeCode`가 선택 사용자와 같은지도 확인한다.
|
||||
|
||||
롤백할 때는 VPD 정책을 삭제하지 말고 disable하여 조사 가능 상태로 보존한다. 사용자별 token
|
||||
환경변수도 공용 token으로 되돌리지 않고 사용자 선택 기능을 일시 비활성화한다.
|
||||
27
docs/design/740-hmm-mcp-vpd-runtime/troubleshooting.md
Normal file
27
docs/design/740-hmm-mcp-vpd-runtime/troubleshooting.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# HMM MCP VPD 문제 해결
|
||||
|
||||
[개요](README.md) · [아키텍처](architecture.md) · [적용 절차](cookbook.md)
|
||||
|
||||
## 사용자를 바꿔도 같은 결과
|
||||
|
||||
- 원인: preset들이 같은 token 환경변수를 참조하거나 선사 View에 VPD가 없다.
|
||||
- 확인: token 원문을 출력하지 않고 preset 환경변수 이름과 token hash의 employee mapping을 확인한다.
|
||||
- 해결: 사용자별 token을 발급하고 `HMM_CARRIER_SCOPE_POLICY`를 활성화한다.
|
||||
|
||||
## 컨텍스트는 다른데 전체 행이 보임
|
||||
|
||||
- 원인: 최종 SELECT가 `EXEMPT ACCESS POLICY`를 가진 ADMIN에서 실행됐다.
|
||||
- 확인: 실행 계정의 `SESSION_PRIVS`와 MCP 응답의 `scopeEmployeeCode`를 확인한다.
|
||||
- 해결: ADMIN은 Select AI `SHOWSQL`만 수행하고 실제 SELECT는 `CB_ORDS`에서 실행한다.
|
||||
|
||||
## 팀원 결과가 0건
|
||||
|
||||
- 원인: token→직원, 직원→역할, 직원→선사 access group 중 하나가 누락됐다.
|
||||
- 확인: 위 순서로 원장 연결을 확인한다.
|
||||
- 해결: 누락된 원장만 보강한다. 조회 함수에 직원 코드나 결과 행을 하드코딩하지 않는다.
|
||||
|
||||
## 원격 선사 KPI가 과다 노출됨
|
||||
|
||||
- 원인: 생성 SQL이 로컬 `HMM_CARRIER_ASSIGNMENTS_V` 없이 원격 KPI View만 읽었다.
|
||||
- 해결: Select AI profile metadata에서 직원·팀 질의는 배정 View와 KPI View를 `CARRIER_CODE`로
|
||||
조인하도록 유지한다. SQL 자체는 고정하지 않는다.
|
||||
Reference in New Issue
Block a user