# 설계서: 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_` | **예.** 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_ → CREATE/GRANT DATA ROLE DDS_U__ROLE → CREATE DATA GRANT (보호 벡터 객체) MCP SSE Tool 호출 Authorization: Bearer → 매 메시지마다 bearer 검증 → CB_APP_USER 확인 → 게시된 DDS_U_ 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_`, `DDS_U__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=`로 등록한다. 서비스 역할을 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)