Files
vpd-permission-poc/docs/design/617-dds-mcp-end-user-context/README.md

111 lines
7.9 KiB
Markdown

# 설계서: DDS MCP 사용자별 END USER Context 및 권한 게시 (#617)
> **상태**: Implemented — OCI IAM·ADB·SSE 실증 완료
> **최종수정**: 2026-07-02
> **추적성** — Redmine: #617 · 관련 ADR: [ADR-0001](../../adr/0001-dds-mcp-service-identity.md)
> · 구현: `DdsMcpBearerAuthenticator`, `DdsMcpEndUserResolver`, `DdsMcpContextExecutor`, `DdsMcpSseController`
> · DB 게시: `sql/adb/44_dds_mcp_local_end_user_setup.sql`
> · 검증: `sql/adb/45_dds_mcp_local_end_user_test.sql`, SSE `tools/call`
## 1. 결정과 목적
MCP Tool의 보호 SQL은 요청 Bearer가 지정한 업무 사용자의 local DDS `END USER` Context에서만 실행한다. DDS가 `DATA ROLE``DATA GRANT`로 행·컬럼 접근을 집행하며, Tool은 권한 predicate를 직접 만들지 않는다.
VPD 경로는 변경하지 않는다. 기존 DDS 관리의 권한 게시 경계는 MCP용 END USER/역할/grant도 함께 갱신한다.
## 2. 토큰과 사용자 — 반드시 구분할 것
| 값 | 현재 구현에서의 역할 | 사람 사용자인가 |
|---|---|---|
| MCP 요청 Bearer | `CB_AGENT_BEARER_KEY` 해시·만료·회수 검증 후 `CB_APP_USER`를 결정 | **예.** DB의 업무 사용자 매핑 기준 |
| local DDS END USER | `DDS_U_<CB_APP_USER.user_id>` | **예.** DDS가 집행하는 보안 주체 |
| OCI IAM database-access token | Confidential application의 client-credentials로 매 Tool Context attach를 승인 | 아니오. 서비스 애플리케이션 신원 |
| OCI IAM end-user token | 향후 authorization-code/OBO 전용 확장 | 현재 MCP 인증 입력으로 사용하지 않음 |
따라서 현재 MCP Bearer가 `CB_APP_USER`를 지정할 수 있으면 그 사용자별 DDS 권한은 적용된다. 반면 client-credentials 토큰의 `sub`/`client_id`는 서비스 애플리케이션이므로 사람 사용자를 판정하는 데 사용하면 안 된다.
OCI IAM JWT의 사람 사용자 claim을 그대로 DDS에 전달하려면 별도의 authorization-code 또는 OBO flow, 해당 end-user token 검증, IAM role↔DDS DATA ROLE 매핑으로 확장해야 한다. 이 경로는 현재 local END USER 모델과 혼용하지 않는다.
## 3. 실제 아키텍처
```text
권한 관리 / DDS 게시
CB_APP_USER + 직접/그룹 역할 + CB_PERMISSION 규칙
→ DDS Publisher
→ CB_DDS_END_USER_MAP
→ CREATE END USER DDS_U_<id>
→ CREATE/GRANT DATA ROLE DDS_U_<id>_ROLE
→ CREATE DATA GRANT (보호 벡터 객체)
MCP SSE Tool 호출
Authorization: Bearer <MCP bearer>
→ 매 메시지마다 bearer 검증
→ CB_APP_USER 확인
→ 게시된 DDS_U_<id> map 확인
→ OCI IAM database-access token 취득/캐시
→ EndUserSecurityContext(name, lookup key) attach
→ 보호 SQL 실행 (DATA GRANT 집행)
→ finally: context clear, 연결 반환
```
SSE 연결 자체는 인증을 보조할 뿐이다. `/dds/mcp/messages`**매 요청마다** Bearer와 사용자를 재검증하므로, 연결을 오래 유지해도 이전 사용자 권한을 신뢰하지 않는다.
## 4. 게시 모델과 운영 규칙
- 활성 `CB_APP_USER`마다 `CB_DDS_END_USER_MAP``DDS_U_<id>`, `DDS_U_<id>_ROLE`, grant 이름과 게시 상태를 기록한다.
- local END USER Context의 lookup key는 서비스 secret과 사용자 ID로 HMAC 파생한다. 원문/파생값은 DB·Git·Redmine·로그에 저장하지 않는다.
- 사용자의 직접 역할과 활성 그룹 역할에서 `CB_PERMISSION` / `CB_PERMISSION_RULE` / 허용 컬럼을 계산해 벡터 보호 객체의 `DATA GRANT`를 재생성한다.
- ALLOW가 없으면 grant를 제거하여 DDS 기본 거부 상태를 유지한다. 비활성 사용자는 data role과 grant를 회수한다.
- 사용자 활성화·직접 역할·그룹 멤버/역할·permission rule/허용 컬럼 변경은 같은 요청 안에서 활성 사용자 전체를 bulk 재발행한다. 수동 **DDS 관리의 게시 작업**은 전체 복구·재검증용으로도 제공한다.
- `withDataRoles(...)`는 local username+lookup-key Context에 사용하지 않는다. 역할은 게시된 local END USER grant에서만 활성화된다.
## 5. OCI IAM / ADB 전제조건
1. ADB external authentication을 `OCI_IAM`으로 등록하고 application ID와 domain URL을 설정한다.
2. `OCI_IAM_DOMAIN_DB_CRED$` credential에 Confidential application의 client ID/secret을 보관한다.
3. pool account에 `CREATE SESSION`, `CREATE END USER SECURITY CONTEXT`를 부여하고 TLS wallet 연결을 사용한다.
4. application identity를 `IAM_OAUTH_CLIENT_ID=<client id>`로 등록한다. 서비스 역할을 application identity에 부여할 경우 해당 역할만 활성화된다.
5. application은 database resource scope로 client-credentials token을 얻는다.
DB가 확인하는 token claim은 `resource_app_id`, `tenant_iss`, audience와 scope다. 이 값은 DB OCI IAM 설정 및 database resource registration과 일치해야 한다.
## 6. 구현 경계
| 컴포넌트 | 책임 | 실패 처리 |
|---|---|---|
| `DdsMcpBearerAuthenticator` | MCP Bearer → 활성 `CB_APP_USER` | 인증 실패, SQL 미실행 |
| `DdsMcpEndUserResolver` | 사용자 → 게시된 DDS principal | 미매핑/미게시면 거부 |
| `DdsMcpDatabaseAccessTokenProvider` | OCI IAM service token 발급·만료 전 갱신 | Context 실행 차단 |
| `DdsMcpContextExecutor` | attach → 제한된 SQL → clear | clear 실패 시 연결 폐기 |
| `DdsMcpVectorSearchService` | Context 내부 보호 벡터 SQL만 실행 | DDS 오류를 안전한 MCP 오류로 변환 |
| `DdsMcpAuthorizationChangeListener` | 권한 변경 event → 전체 local END USER grant 재발행 | 요청을 실패로 알리고 게시 상태를 확인하게 함 |
| DDS Publisher | 기존 권한 모델 → local END USER/DATA ROLE/DATA GRANT | 부분 실패를 게시 실패로 기록 |
Tool/Repository는 raw Bearer, client secret, lookup key 또는 `setEndUserSecurityContext`를 직접 다루지 않는다.
## 7. 검증 결과 (2026-07-02)
- [x] SQLcl로 ADB 접속 및 local `END USER`, `DATA ROLE`, `DATA GRANT` catalog 생성 검증.
- [x] OCI IAM database-access token 발급 성공. `resource_app_id`, `tenant_iss`, audience `DDSDB`, scope `DB_ACCESS_SCOPE`가 DB 설정과 일치.
- [x] DB credential `OCI_IAM_DOMAIN_DB_CRED$`, OCI IAM application identity mapping 확인.
- [x] `/dds/mcp/sse` endpoint event 및 `tools/list` HTTP 200 확인.
- [x] `dds_vector_search` tool call: Bearer → application user 101 → local DDS Context attach → 보호 query 3행 반환 → context clear, HTTP 200.
- [ ] 사용자 A→B→A 및 동시 요청의 full isolation regression을 자동화한다.
- [ ] DDS 관리 UI publish가 MCP END USER map/grant를 같은 트랜잭션 경계에서 갱신하도록 통합한다.
SSE stream은 연결을 유지하므로 client read timeout이 날 수 있다. 이는 실패 판정 기준이 아니며, Tool RPC의 HTTP 결과와 attach/query/clear 로그로 성공을 판정한다.
## 8. 보안 불변식
1. 유효한 MCP Bearer 하나는 정확히 하나의 활성 `CB_APP_USER`와 하나의 게시된 DDS END USER로만 해석된다.
2. Context attach 이전·clear 이후에는 보호 SQL을 실행하지 않는다.
3. attach/query/clear 어느 단계가 실패해도 fail-closed한다.
4. client-credentials token은 서비스 승인용이며 업무 사용자 권한 확대에 사용하지 않는다.
5. secret, raw bearer, database-access token, lookup key는 응답·로그·Git·Redmine에 기록하지 않는다.
## 9. 참고
- [Oracle: Configure the Database for IAM Integration](https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/configure-database-iam-integration.html)
- [Oracle: Prerequisites for Establishing a Local Security Context](https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/prerequisites-establishing-local-security-context.html)
- [Oracle: End-User Security Context Issues](https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/end-user-security-context-issues.html)