diff --git a/docs/adr/0001-dds-mcp-service-identity.md b/docs/adr/0001-dds-mcp-service-identity.md new file mode 100644 index 0000000..bd02184 --- /dev/null +++ b/docs/adr/0001-dds-mcp-service-identity.md @@ -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을 제공하지 못하므로 기각. diff --git a/docs/design/617-dds-mcp-end-user-context/README.md b/docs/design/617-dds-mcp-end-user-context/README.md new file mode 100644 index 0000000..2344451 --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/README.md @@ -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_`를 사용한다. 표시 이름·한글 로그인명과 분리한다. + +## 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_`를 사용하며 표시명과 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 스파이크가 승인되기 전까지 임의 구현으로 채우지 않는다. diff --git a/docs/design/617-dds-mcp-end-user-context/fn-authenticate-mcp-bearer.md b/docs/design/617-dds-mcp-end-user-context/fn-authenticate-mcp-bearer.md new file mode 100644 index 0000000..2130ae6 --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/fn-authenticate-mcp-bearer.md @@ -0,0 +1,28 @@ +# 함수 명세: `authenticateMcpBearer` (#617) + +> **상태**: Draft · **분류**: 복잡 — 인증 저장소 I/O 및 fail-closed 경계 + +## 책임 + +MCP Tool 호출의 Bearer를 기존 애플리케이션 사용자로 해석한다. DDS 역할·DDL을 결정하지 않는다. + +## 시그니처 + +`BearerToken -> AuthenticatedAppUser` + +## 입력과 출력 + +- 입력: `Authorization: Bearer `에서 추출한 원문 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을 호출하지 않는다. diff --git a/docs/design/617-dds-mcp-end-user-context/fn-compile-dds-publish-plan.md b/docs/design/617-dds-mcp-end-user-context/fn-compile-dds-publish-plan.md new file mode 100644 index 0000000..168f2d4 --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/fn-compile-dds-publish-plan.md @@ -0,0 +1,33 @@ +# 함수 명세: `compileDdsPublishPlan` (#617) + +> **상태**: Draft · **분류**: 복잡 — 권한 컴파일·DDL diff·안전 검증 + +## 책임 + +DDS 백오피스의 보호 객체, 접근 역할, 테이블 세부 권한, 사용자/그룹 할당을 실행 가능한 DDS DDL diff로 바꾼다. + +## 시그니처 + +`DdsAuthorizationDraft -> DdsPublishPlan` + +## 규칙 + +1. 그룹을 포함해 역할별 유효 사용자를 계산한다. +2. `CB_APP_USER.userId`마다 ASCII `DDS_U_` 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 규칙, 이름 충돌은 게시 전에 차단한다. diff --git a/docs/design/617-dds-mcp-end-user-context/fn-get-database-access-token.md b/docs/design/617-dds-mcp-end-user-context/fn-get-database-access-token.md new file mode 100644 index 0000000..f15e9dd --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/fn-get-database-access-token.md @@ -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한다. diff --git a/docs/design/617-dds-mcp-end-user-context/fn-publish-and-verify-dds-plan.md b/docs/design/617-dds-mcp-end-user-context/fn-publish-and-verify-dds-plan.md new file mode 100644 index 0000000..68b8d99 --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/fn-publish-and-verify-dds-plan.md @@ -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 요청 경로는 마지막 성공 게시 상태를 우회 권한으로 사용하지 않는다. diff --git a/docs/design/617-dds-mcp-end-user-context/fn-with-dds-end-user-context.md b/docs/design/617-dds-mcp-end-user-context/fn-with-dds-end-user-context.md new file mode 100644 index 0000000..f84500c --- /dev/null +++ b/docs/design/617-dds-mcp-end-user-context/fn-with-dds-end-user-context.md @@ -0,0 +1,36 @@ +# 함수 명세: `withDdsEndUserContext` (#617) + +> **상태**: Draft · **분류**: 복잡 — 연결 풀 보안 경계 + +## 책임 + +한 DB 작업을 정확히 하나의 local DDS END USER Context 안에서 실행하고, 어떤 종료 경로에서도 Context를 해제한다. + +## 시그니처 + +`(DdsPrincipal, SqlWork) -> T` + +## 입력 + +- `DdsPrincipal`: 게시된 DDS END USER 이름, lookup key 참조, 사용자 ID, 게시 version. +- `SqlWork`: 보호 객체를 읽거나 쓰는 제한된 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`, 행, 컬럼이 각각 맞는지 확인한다.