설계서: DDS MCP 사용자별 END USER Context 및 권한 게시 (#617)
상태: Implemented — OCI IAM·ADB·SSE 실증 완료 최종수정: 2026-07-02 추적성 — Redmine: #617 · 관련 ADR: ADR-0001 · 구현:
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, SSEtools/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. 실제 아키텍처
권한 관리 / 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 전제조건
- ADB external authentication을
OCI_IAM으로 등록하고 application ID와 domain URL을 설정한다. OCI_IAM_DOMAIN_DB_CRED$credential에 Confidential application의 client ID/secret을 보관한다.- pool account에
CREATE SESSION,CREATE END USER SECURITY CONTEXT를 부여하고 TLS wallet 연결을 사용한다. - application identity를
IAM_OAUTH_CLIENT_ID=<client id>로 등록한다. 서비스 역할을 application identity에 부여할 경우 해당 역할만 활성화된다. - 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)
- SQLcl로 ADB 접속 및 local
END USER,DATA ROLE,DATA GRANTcatalog 생성 검증. - OCI IAM database-access token 발급 성공.
resource_app_id,tenant_iss, audienceDDSDB, scopeDB_ACCESS_SCOPE가 DB 설정과 일치. - DB credential
OCI_IAM_DOMAIN_DB_CRED$, OCI IAM application identity mapping 확인. /dds/mcp/sseendpoint event 및tools/listHTTP 200 확인.dds_vector_searchtool 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. 보안 불변식
- 유효한 MCP Bearer 하나는 정확히 하나의 활성
CB_APP_USER와 하나의 게시된 DDS END USER로만 해석된다. - Context attach 이전·clear 이후에는 보호 SQL을 실행하지 않는다.
- attach/query/clear 어느 단계가 실패해도 fail-closed한다.
- client-credentials token은 서비스 승인용이며 업무 사용자 권한 확대에 사용하지 않는다.
- secret, raw bearer, database-access token, lookup key는 응답·로그·Git·Redmine에 기록하지 않는다.