[Architect] #617 design DDS MCP end-user context

This commit is contained in:
devmrko
2026-07-01 20:34:48 +09:00
parent ebcce66bf7
commit b7100cc62a
7 changed files with 399 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
# ADR-0001: DDS MCP Context에는 개인 IAM 사용자 대신 서비스 애플리케이션을 사용한다
> **상태**: Proposed
> **날짜**: 2026-07-01 · **결정자**: [AI] Architect · **관련 이슈**: #617
## 맥락 (Context)
MCP SSE 서비스는 자체 Bearer로 애플리케이션 사용자를 식별하고, local DDS END USER Context를 부착해 Tool SQL을 실행해야 한다. application-mediated DDS Context에는 database-access token이 필요하다.
개인 IAM 사용자 `joungmin.ko@oracle.com`의 장기 token을 서비스 설정에 넣으면 개인 계정 수명주기, 감사, 회전, 퇴사, 권한 범위가 MCP 서비스의 가용성과 보안 경계에 직접 결합된다.
## 결정 (Decision)
`mcp-dds-service`라는 OCI IAM 서비스 애플리케이션을 별도로 등록하고, client-credentials flow로 short-lived database-access token을 취득한다. MCP 사용자는 IAM 사용자가 아니라 기존 `CB_APP_USER` 및 로컬 DDS END USER로 관리한다.
이 결정은 P0 스파이크에서 실제 OCI tenancy/ADB 설정으로 Context attach가 증명되기 전까지 Proposed 상태다.
## 근거 (Rationale)
서비스 신뢰와 업무 사용자 신원을 분리한다. database-access token은 서비스의 Context 부착 권한이고, DDS END USER는 DB가 DATA ROLE/DATA GRANT를 집행할 실제 권한 주체다.
## 결과 (Consequences)
- **긍정**: 개인 계정과 서비스 실행 권한을 분리하고, 사용자별 IAM 계정 없이 DDS Context를 적용할 수 있다.
- **부정 / 비용**: OCI IAM 애플리케이션 등록, token cache/rotation, TLS/DB identity 구성, secret 관리가 필요하다.
- **후속 작업**: Free Identity Domain entitlement, client-credentials database token, ADB local END USER Context attach를 스파이크로 검증한다.
## 검토한 대안 (Alternatives Considered)
- **개인 IAM 사용자 token 고정** — 개인 수명주기·감사·권한 회전에 서비스가 종속되어 기각.
- **MCP 사용자마다 IAM 사용자 생성** — ERP/기존 사용자 관리 모델을 IAM으로 중복 이전해야 하므로 기각.
- **IAM 없이 자체 Bearer만 사용** — application-mediated DDS Context attach에 필요한 database-access token을 제공하지 못하므로 기각.

View File

@@ -0,0 +1,211 @@
# 설계서: DDS MCP 사용자별 END USER Context 및 권한 게시 (#617)
> **상태**: Draft — P0 스파이크 승인 전 구현 금지
> **작성**: [AI] Architect · **최종수정**: 2026-07-01
> **추적성** — Redmine: #617 · 관련 ADR: [ADR-0001](../../adr/0001-dds-mcp-service-identity.md)
> · 구현 파일: TBD · 테스트: TBD
## 1. 목적 (Why)
MCP SSE 서비스가 요청자의 권한을 대신 판단하거나 SQL에 조건을 덧붙이지 않고, 각 Tool의 DB 작업을 해당 요청자에 대응하는 Oracle Deep Data Security(DDS) END USER Context로 실행한다.
VPD 트랙은 기존 구현과 데이터 모델을 변경하지 않는다. DDS 트랙은 별도의 보호 객체, 별도의 권한 게시 모델, 별도의 런타임 Context 경로를 갖는다.
## 2. 범위 (Scope)
- **포함**
- MCP Bearer → `CB_APP_USER` → 로컬 DDS END USER의 명시적 매핑.
- MCP Tool 호출마다 DDS END USER Context를 부착·해제하는 실행 경계.
- 보호 데이터 객체, 접근 역할, 테이블 세부 권한, 사용자/그룹 역할 할당을 관리하는 DDS 전용 백오피스 모델.
- 역할·그룹의 유효 멤버십을 DDS `END USER` / `DATA ROLE` / `DATA GRANT` DDL로 게시·회수·검증하는 Publisher.
- OCI IAM 서비스 애플리케이션 database-access token과 local END USER lookup key의 기술 스파이크.
- **제외 (out of scope)**
- VPD 정책, VPD 백오피스, 기존 `CB_AGENT_CTX` 경로의 변경.
- MCP 사용자마다 OCI IAM 사용자를 생성·관리하는 기능.
- 사용자 요청 시점의 `CREATE END USER`, `GRANT DATA ROLE`, `CREATE DATA GRANT` 수행.
- 임의 SQL predicate 입력 및 기존 VPD `DENY 우선` 규칙의 자동 변환.
- OCI IAM 도메인/애플리케이션 등록의 실제 운영 배포. 스파이크 성공 후 별도 작업으로 분리한다.
## 3. 인수조건 (Acceptance Criteria)
- [ ] 활성 MCP Bearer는 정확히 하나의 활성 `CB_APP_USER`로 해석되며, DDS END USER 매핑이 없으면 Tool 실행 전에 권한 없음으로 종료된다.
- [ ] 동일한 공용 연결 풀에서 서로 다른 두 요청자가 연속·동시 Tool을 호출해도 각 요청은 자신의 DDS END USER Context와 DATA GRANT 결과만 받는다.
- [ ] 예외·취소·타임아웃을 포함한 모든 실행 경로에서 Context가 해제되어 다음 요청에 이전 권한이 남지 않는다.
- [ ] 백오피스 게시기는 사용자/그룹 역할 할당과 테이블 세부 권한을 idempotent DDS DDL로 반영하며, 게시 diff·사유·결과·드리프트를 보관한다.
- [ ] 그룹 멤버 추가/제거 후 게시하면 필요한 DDS END USER/DATA ROLE은 부여되고 불필요한 역할은 회수된다.
- [ ] MCP Tool SQL은 권한 `WHERE`를 직접 만들지 않으며, DDS DATA GRANT가 반환 행·컬럼을 제한한다.
- [ ] 미매핑·비활성·회수된 Bearer, 미게시 사용자, DATA GRANT 없는 사용자, Context attach 실패는 fail-closed로 처리한다.
- [ ] VPD 회귀 테스트가 통과하며 VPD 스키마·정책·UI에 변경이 없다.
## 4. 컨텍스트 & 제약
- 기존 `CB_BEARER_TOKEN`/`CB_APP_USER`는 애플리케이션 논리 사용자 식별 수단이다. DDS 내장 인증 객체가 아니다.
- 로컬 DDS END USER는 `CREATE END USER`로 별도 생성한다. ERP/MCP 사용자는 IAM 사용자가 아니어도 되지만, DDS END USER는 사용자별로 존재한다.
- MCP SSE 서비스는 공용 DB 연결을 사용한다. DDS END USER Context는 SSE 연결 전체가 아니라 **매 Tool DB 실행 경계**마다 부착·해제한다.
- application-mediated local END USER Context에는 서비스 애플리케이션의 database-access token이 필요하다. 개인 IAM 사용자 토큰을 장기 설정값으로 저장하지 않는다.
- 서비스 애플리케이션의 OCI IAM 등록, token 발급, TLS/DB 설정, lookup key 발급은 P0 스파이크로 검증되기 전에는 구현 사실로 간주하지 않는다.
- DDS DATA GRANT는 additive다. 여러 DATA ROLE의 권한은 합집합이므로 VPD의 `DENY 우선` 의미를 자동 보존할 수 없다.
- END USER 이름은 ASCII 안정 식별자 `DDS_U_<CB_APP_USER.user_id>`를 사용한다. 표시 이름·한글 로그인명과 분리한다.
## 5. 아키텍처 개요
### 5.1 두 평면
```text
권한 게시 평면
백오피스
→ 보호 데이터 객체 / 접근 역할 / 세부 권한 / 사용자·그룹 할당
→ 유효 멤버십 계산
→ DDS Publisher
→ END USER · DATA ROLE · DATA GRANT DDL
→ 게시 이력 · 검증 · 드리프트 상태
요청 실행 평면
MCP Client Bearer
→ MCP SSE 인증
→ CB_BEARER_TOKEN / CB_APP_USER 확인
→ DDS END USER 매핑 확인
→ 서비스 IAM database-access token + lookup key로 Context 부착
→ MCP Tool DB 작업
→ DDS DATA GRANT 집행
→ Context 해제
```
### 5.2 신원 분리
| 항목 | 의미 | 관리 주체 |
|---|---|---|
| MCP Bearer | MCP 요청자가 어느 애플리케이션 사용자 인지 식별 | 기존 `CB_BEARER_TOKEN` |
| 애플리케이션 사용자 | 업무 사용자·그룹·역할 할당의 기준 | `CB_APP_USER` 및 DDS 전용 할당 모델 |
| DDS END USER | DB가 DATA ROLE/DATA GRANT를 집행하는 보안 주체 | DDS Publisher |
| Database-access token | MCP SSE 서비스가 Context를 부착할 수 있다는 애플리케이션 신뢰 | OCI IAM 서비스 애플리케이션 |
### 5.3 실행 경계
`McpDdsContextExecutor`만 보호 객체용 연결을 획득한다. 이 경계는 다음 순서를 강제한다.
1. Bearer를 재검증하고 활성 애플리케이션 사용자를 찾는다.
2. 게시 완료된 DDS END USER 매핑과 lookup key 참조를 찾는다.
3. 서비스 database-access token을 획득/갱신한다.
4. 연결에 DDS END USER Context를 부착한다.
5. Tool의 제한된 DB 작업을 실행한다.
6. `finally`에서 Context를 해제한 뒤 연결을 반환한다.
Tool·Repository는 Context 설정 API, DATA GRANT DDL, 원시 Bearer를 직접 다루지 않는다.
## 6. 데이터 모델
기존 VPD 공통 권한 테이블은 읽거나 변경하지 않는다. DDS 게시용 모델은 별도 관리한다.
| 개념 | 잠정 저장 구조 | 핵심 값 |
|---|---|---|
| 보호 데이터 객체 | `CB_DDS_PROTECTED_OBJECT` | object owner/name, Tool 노출명, 활성 상태 |
| 접근 역할 | `CB_DDS_ACCESS_ROLE` | 표시명, `DATA ROLE` 이름, 상태 |
| 테이블 세부 권한 | `CB_DDS_OBJECT_GRANT` | 역할, 객체, action, 허용/제외 컬럼, 행 조건 DSL |
| 역할 할당 | `CB_DDS_ROLE_ASSIGNMENT` | 역할, 대상 유형(USER/GROUP), 대상 ID |
| DDS END USER 매핑 | `CB_DDS_END_USER_MAP` | `CB_APP_USER.user_id`, DDS END USER 이름, 게시 상태, lookup key 참조 |
| 게시 실행 | `CB_DDS_PUBLISH_RUN` / 항목 | diff, 사유, 실행 결과, DDL fingerprint, 검증 결과 |
행 조건은 자유 SQL 문자열이 아니다. 객체별 허용 컬럼, 비교 연산자, 리터럴 타입, `ORA_END_USER_CONTEXT` 허용 경로만으로 구성된 DSL을 Publisher가 검증된 SQL predicate로 컴파일한다.
## 7. 함수 명세 (Function Specs)
| 함수 | 책임(1줄) | 시그니처(잠정) | 입력 | 출력 | 에러/실패 | 복잡? |
|---|---|---|---|---|---|---|
| `authenticateMcpBearer` | Bearer를 활성 업무 사용자로 해석 | `Bearer → AppUser` | Bearer | `AppUser` | 미존재·만료·회수 → deny | **복잡** |
| `resolveDdsEndUser` | 업무 사용자의 게시된 DDS END USER를 찾음 | `AppUser → DdsPrincipal` | 사용자 ID | END USER/lookup key 참조 | 미매핑·미게시 → deny | 단순 |
| `getDatabaseAccessToken` | 서비스용 short-lived DB access token을 제공 | `() → Token` | 서비스 IAM 설정 | token | 발급 실패 → fail closed | **복잡** |
| `withDdsEndUserContext` | Context를 부착·DB 작업·해제를 원자적으로 수행 | `(DdsPrincipal, SqlWork) → T` | DDS principal, 작업 | 결과 | attach/query/clear 실패 | **복잡** |
| `compileDdsPublishPlan` | 백오피스 권한 모델을 DDL diff로 변환 | `Draft → PublishPlan` | 객체/역할/할당 | DDL plan | DSL/충돌/deny 규칙 오류 | **복잡** |
| `publishDdsPlan` | 승인된 plan을 멱등 게시하고 검증 | `PublishPlan → PublishRun` | plan, 사유 | 결과/증적 | 부분 실패·drift | **복잡** |
| `verifyDdsIsolation` | 대표 사용자별 Context·객체·행·컬럼 결과를 확인 | `PublishRun → Verification` | 사용자 matrix | 검증 결과 | context 누수·권한 불일치 | **복잡** |
복잡 함수의 세부 명세는 다음 문서로 분리한다.
- [Bearer 인증](fn-authenticate-mcp-bearer.md)
- [서비스 database-access token](fn-get-database-access-token.md)
- [DDS Context 실행 경계](fn-with-dds-end-user-context.md)
- [DDS 게시 계획 컴파일](fn-compile-dds-publish-plan.md)
- [DDS 게시·검증](fn-publish-and-verify-dds-plan.md)
## 8. 흐름 / 알고리즘
### 8.1 권한 게시
1. 운영자가 보호 객체, 접근 역할, 세부 권한, 사용자/그룹 할당을 수정한다.
2. Publisher는 그룹을 포함한 역할별 유효 사용자를 계산한다.
3. 누락된 사용자에 대해 비밀번호 없는 DDS END USER 생성 계획을 만든다.
4. 역할별 DATA ROLE과 객체별 DATA GRANT의 생성·교체·회수 diff를 만든다.
5. 승인된 plan만 실행한다. 요청 실행 경로에서는 DDL을 실행하지 않는다.
6. 게시 후 END USER/ROLE/GRANT dictionary와 대표 사용자 matrix를 확인한다.
### 8.2 MCP Tool 실행
1. MCP 요청의 Bearer를 검증한다.
2. 사용자·DDS END USER 매핑·게시 상태를 확인한다.
3. Context 부착 전에 미매핑/비활성/회수 상태면 권한 없음으로 중단한다.
4. Tool의 DB 작업을 DDS Context 안에서 실행한다.
5. 성공·실패와 상관없이 Context를 제거한다.
6. MCP 응답에는 Tool이 반환한 DDS 제한 결과만 포함한다. END USER 이름, lookup key, database-access token은 노출하지 않는다.
## 9. 엣지케이스 & 에러 처리
| 상황 | 처리 |
|---|---|
| Bearer가 없거나 회수됨 | 인증 실패. DDS END USER 조회/부착을 시도하지 않는다. |
| 사용자 매핑 없음 | 권한 없음. 요청 중 END USER를 생성하지 않는다. |
| 사용자 비활성/게시 실패/drift | 권한 없음. 마지막 성공 게시 결과를 사용하지 않는다. |
| Context attach 실패 | Tool SQL을 실행하지 않는다. correlation ID만 기록한다. |
| Tool SQL 예외/취소/시간초과 | `finally`에서 Context 해제 후 안전한 오류 응답. |
| 연결 풀 재사용 | 매 DB 작업에 새 Context를 부착하고 `finally`에서 해제한다. |
| 그룹에서 사용자 제거 | 다음 게시에서 DATA ROLE 회수, 이후 요청은 새 게시 결과만 사용한다. |
| 비ASCII 업무 사용자명 | `DDS_U_<id>`를 사용하며 표시명과 END USER 식별자를 분리한다. |
| VPD DENY 규칙 | DDS 1차 모델에서 미지원. 게시 전 오류로 차단한다. |
## 10. 테스트 계획
| 인수조건 | 검증 |
|---|---|
| Context 사용자 격리 | 같은 공용 풀에서 A → B → A 순서와 동시 실행. `ORA_END_USER_CONTEXT.username` 및 결과 행/컬럼이 각 사용자와 일치. |
| Context 해제 | Tool 예외·취소·타임아웃 뒤 다음 요청이 이전 사용자의 Context를 갖지 않음. |
| 토큰 fail-closed | 미존재·만료·회수 Bearer와 미매핑 사용자 모두 SQL 실행 전 거부. |
| 역할/그룹 게시 | 직접 사용자·그룹 추가/제거 후 예상 DATA ROLE grant/revoke와 결과 확인. |
| 세부 권한 | 역할별 객체 차단, 행 조건, 컬럼 제외, 다중 역할 합집합을 검증. |
| DDL 멱등성 | 동일 plan 재게시가 변경 없음으로 완료. |
| 드리프트 | dictionary의 END USER/DATA ROLE/DATA GRANT를 의도적으로 변경한 뒤 탐지. |
| VPD 회귀 | 기존 VPD Maven/SQL/HTTP 회귀 테스트 전체 통과. |
| P0 스파이크 | OCI IAM 서비스 token + local END USER name/lookup key로 Context attach 성공 및 두 사용자 격리 증명. |
## 11. 리스크 & 대안 검토
### 선택: DDS 선언 게시 + MCP Context 부착
권한을 백오피스에서 검토·승인·게시하고, MCP Tool은 게시된 DDS 권한을 집행한다. Agent가 SQL을 만들더라도 DB가 결과를 제한한다.
### 대안: 기존 VPD 공통 권한 테이블을 요청마다 동적 해석
기존 VPD 모델과 즉시 반영 특성은 유지하지만, DDS DATA ROLE/DATA GRANT를 권한의 단일 출처로 쓰려는 목표와 맞지 않는다. VPD 트랙에 남긴다.
### 대안: 사용자별 IAM 계정
IAM token claim에서 DATA ROLE을 직접 활성화할 수 있으나 ERP/MCP 사용자 관리 주체를 IAM으로 옮겨야 한다. 이번 범위에서는 제외한다.
### 주요 리스크
- local END USER application-mediated Context에는 service IAM database-access token이 필요하다.
- lookup key 생성/보관 방식이 미확정이다.
- DDS 권한은 additive이므로 VPD DENY 우선 모델을 그대로 이식할 수 없다.
- Publisher의 partial failure는 사용자별 권한 불일치를 만들 수 있으므로 실행 plan, compensation, drift 탐지가 필요하다.
OCI IAM 서비스 애플리케이션의 도메인·token flow·비밀 보관 방식은 되돌리기 어려우므로 ADR로 분리한다.
## 12. 미해결 질문 (Open Questions)
1. local END USER security-context lookup key의 정확한 생성 API/명령, 회전·보관 기준은 무엇인가?
2. 현재 OCI tenancy의 Free Identity Domain에서 DDS용 application registration 및 client-credentials database-access token을 발급할 수 있는가?
3. ADB의 TLS, identity provider, application identity, pool account에 필요한 정확한 설정·권한은 무엇인가?
4. Java 26ai JDBC artifact/API와 현재 `dds-backoffice`의 OJDBC 버전·Spring pool이 Context attach를 지원하는가?
5. VPD의 DENY 규칙을 DDS 1차 모델에서 명시적으로 제외할지, 제한된 조합 규칙으로 컴파일할지?
6. Data Grant의 다중 역할 컬럼/행 합집합 의미를 실제 ADB에서 어떤 matrix로 검증할지?
이 여섯 항목은 P0 스파이크가 승인되기 전까지 임의 구현으로 채우지 않는다.

View File

@@ -0,0 +1,28 @@
# 함수 명세: `authenticateMcpBearer` (#617)
> **상태**: Draft · **분류**: 복잡 — 인증 저장소 I/O 및 fail-closed 경계
## 책임
MCP Tool 호출의 Bearer를 기존 애플리케이션 사용자로 해석한다. DDS 역할·DDL을 결정하지 않는다.
## 시그니처
`BearerToken -> AuthenticatedAppUser`
## 입력과 출력
- 입력: `Authorization: Bearer <opaque-token>`에서 추출한 원문 token.
- 출력: 활성 `CB_APP_USER``userId`, 표시명, token 식별자, 검증 시각.
## 규칙
1. token 형식·길이를 먼저 검증한다.
2. 해시 비교, 만료, 회수, 활성 사용자 여부를 한 트랜잭션에서 검증한다.
3. token이 없거나 하나의 활성 사용자로 해석되지 않으면 동일한 권한 없음 결과를 반환한다.
4. 원문 token, 해시, 사용자 상세를 로그·MCP 응답에 넣지 않는다.
5. 각 Tool 호출마다 재검증한다. SSE 연결 생성 시점의 결과를 재사용하지 않는다.
## 실패
`AUTHORIZATION_DENIED`로 fail-closed한다. DDS Context 부착과 DB Tool SQL을 호출하지 않는다.

View File

@@ -0,0 +1,33 @@
# 함수 명세: `compileDdsPublishPlan` (#617)
> **상태**: Draft · **분류**: 복잡 — 권한 컴파일·DDL diff·안전 검증
## 책임
DDS 백오피스의 보호 객체, 접근 역할, 테이블 세부 권한, 사용자/그룹 할당을 실행 가능한 DDS DDL diff로 바꾼다.
## 시그니처
`DdsAuthorizationDraft -> DdsPublishPlan`
## 규칙
1. 그룹을 포함해 역할별 유효 사용자를 계산한다.
2. `CB_APP_USER.userId`마다 ASCII `DDS_U_<id>` END USER 이름을 결정한다.
3. 접근 역할마다 하나의 stable DATA ROLE 이름을 결정한다.
4. 사용자·역할·객체·세부 권한의 현재 게시 상태와 목표 상태를 비교한다.
5. 생성/교체/회수 DDL과 예상 영향 사용자·객체를 계산한다.
6. 행 조건 DSL은 whitelist 기반 compiler만 사용한다. raw SQL은 거부한다.
7. `DENY` 또는 지원하지 않는 권한 조합은 plan 생성 단계에서 오류로 끝낸다.
## 출력
- END USER 생성/활성화/비활성화 목록
- DATA ROLE 생성/역할 부여/회수 목록
- DATA GRANT 생성·교체·삭제 목록
- 예상 영향 사용자·객체·컬럼 matrix
- 이전/목표 fingerprint와 승인 대상 diff
## 실패
충돌, 비활성 객체, DSL 오류, DENY 규칙, 이름 충돌은 게시 전에 차단한다.

View File

@@ -0,0 +1,28 @@
# 함수 명세: `getDatabaseAccessToken` (#617)
> **상태**: Draft · **분류**: 복잡 — OCI IAM 외부 I/O·비밀·만료 관리
## 책임
MCP SSE 서비스 애플리케이션의 short-lived database-access token을 제공한다. MCP 사용자 Bearer와 혼동하거나 대체하지 않는다.
## 시그니처
`() -> DatabaseAccessToken`
## 규칙
1. 개인 IAM 사용자(`joungmin.ko@oracle.com`)의 장기 token을 사용하지 않는다.
2. `mcp-dds-service` 전용 OCI IAM 애플리케이션의 client-credentials flow를 사용한다.
3. access token은 만료 전 안전 여유를 두고 갱신하며, 원문을 로그·DB·Redmine에 저장하지 않는다.
4. 토큰 발급 실패 시 이전 만료 token을 재사용하지 않고 Tool DB 작업을 차단한다.
5. secret은 배포 환경의 secret store만 사용한다.
## 스파이크 확인
- OCI Identity Domain의 무료/현재 entitlement에서 database-access token 발급이 가능한지 확인한다.
- ADB의 identity provider, TLS, pool account, application identity 조건을 실제 연결로 확인한다.
## 실패
`DDS_CONTEXT_UNAVAILABLE`로 fail-closed한다.

View File

@@ -0,0 +1,31 @@
# 함수 명세: `publishDdsPlan` 및 `verifyDdsIsolation` (#617)
> **상태**: Draft · **분류**: 복잡 — 보안 DDL 변경·검증·드리프트
## 책임
승인된 DDL plan을 멱등 실행하고, dictionary와 representative END USER matrix로 실제 결과를 확인한다.
## 시그니처
- `DdsPublishPlan, Reason -> DdsPublishRun`
- `DdsPublishRun -> DdsVerificationResult`
## 게시 규칙
1. 승인 사유와 diff fingerprint가 없으면 게시하지 않는다.
2. Publisher lock으로 같은 보호 객체의 동시 게시를 막는다.
3. END USER/Data Role/Data Grant를 dependency 순서로 적용한다.
4. 각 DDL 결과와 Oracle 오류를 `DdsPublishRun`에 기록한다.
5. 중간 실패 시 성공으로 표시하지 않는다. 부분 적용 상태로 기록하고 drift로 승격한다.
## 검증 규칙
1. `DBA_END_USERS`, `DBA_DATA_ROLES`, `DBA_DATA_ROLE_GRANTS`, `DBA_DATA_GRANTS`를 목표 fingerprint와 비교한다.
2. 대표 사용자별 Context, 객체 접근, 행, 컬럼 결과를 검증한다.
3. 역할 제거와 객체 미권한 경로도 반드시 검증한다.
4. Context attach/clear 격리 검증이 실패하면 publish 결과를 정상으로 표시하지 않는다.
## 실패
게시·검증 실패는 `FAILED` 또는 `DRIFT` 상태다. MCP 요청 경로는 마지막 성공 게시 상태를 우회 권한으로 사용하지 않는다.

View File

@@ -0,0 +1,36 @@
# 함수 명세: `withDdsEndUserContext` (#617)
> **상태**: Draft · **분류**: 복잡 — 연결 풀 보안 경계
## 책임
한 DB 작업을 정확히 하나의 local DDS END USER Context 안에서 실행하고, 어떤 종료 경로에서도 Context를 해제한다.
## 시그니처
`(DdsPrincipal, SqlWork<T>) -> T`
## 입력
- `DdsPrincipal`: 게시된 DDS END USER 이름, lookup key 참조, 사용자 ID, 게시 version.
- `SqlWork<T>`: 보호 객체를 읽거나 쓰는 제한된 Tool DB 작업.
## 알고리즘
1. `DdsPrincipal`이 활성·게시 완료 상태인지 확인한다.
2. 공용 연결을 획득하고 서비스 database-access token을 얻는다.
3. local END USER 이름과 lookup key로 DDS Context를 부착한다.
4. `SqlWork`를 실행한다. Repository는 Context 설정/해제를 직접 호출할 수 없다.
5. 성공·실패·취소·시간초과와 무관하게 `finally`에서 Context를 해제한다.
6. 해제 실패는 연결을 풀에 반환하지 않고 폐기한다.
## 보안 불변식
- 하나의 Tool DB 작업에는 하나의 DDS END USER만 존재한다.
- 이전 Tool의 Context가 다음 Tool에 남아서는 안 된다.
- Context 부착 이전과 해제 이후에는 보호 SQL을 실행하지 않는다.
- local END USER의 DATA ROLE은 payload로 임의 추가하지 않고, 게시된 DDS DDL에서만 얻는다.
## 스파이크 확인
동일 풀 연결에서 사용자 A → B → A와 동시 요청을 실행해 `ORA_END_USER_CONTEXT.username`, 행, 컬럼이 각각 맞는지 확인한다.