[Developer] #567 expose VPD effective SQL trace
This commit is contained in:
137
docs/design/567-ords-vpd-sql-trace/README.md
Normal file
137
docs/design/567-ords-vpd-sql-trace/README.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 설계서: ORDS/VPD 권한 적용 SQL trace (#567)
|
||||
|
||||
> **상태**: Approved
|
||||
> **작성**: [AI] Architect · **최종수정**: 2026-06-30
|
||||
> **추적성** — Redmine: #567 · 관련 이슈: #561 · 관련 ADR: 없음
|
||||
> · 구현 파일: `ProbeResult`, `OrdsProbeService`, `OrdsMetadataService`, `probe-result.html`, `ords-handlers.html`, `sql/adb/17_*`, `22_*`, `26_*`, `29_*`, `33_*`
|
||||
> · 테스트: `ProbeResultTest`, `OrdsMetadataServiceTest`, `GuidedFlowTemplateTest`, `mvn test`
|
||||
|
||||
## 1. 목적 (Why)
|
||||
|
||||
권한 결과 확인에서 `set_vpd_context`가 토큰 사용자를 DB 컨텍스트로 설정한 뒤, VPD 정책 함수가 반환한 행 조건이 기본 SELECT에 어떻게 결합되는지 운영자가 직접 확인할 수 있게 한다. 이 기능은 #561의 Handler 흐름 설명을 실행 증거까지 확장한다.
|
||||
|
||||
## 2. 범위 (Scope)
|
||||
|
||||
- **포함**:
|
||||
- ORDS Handler 성공 응답의 `vpd_predicate`, `effective_sql` trace 필드.
|
||||
- 기존 trace 미적용 Handler를 위한 동일 DB fallback predicate 조회.
|
||||
- `/probe` 결과의 “토큰 적용 후 SQL” 영역과 trace 미지원 안내.
|
||||
- 신규/갱신 Handler SQL 및 기존 설치용 `GRANT` 패치 스크립트.
|
||||
- 토큰 원문 마스킹과 trace 관련 단위/템플릿/소스 테스트.
|
||||
- **제외 (out of scope)**:
|
||||
- Oracle optimizer 실행계획, `V$SQL` 조회, bind 값 치환 결과의 수집.
|
||||
- custom VPD Filter의 임의 SQL 해석 또는 자동 수정.
|
||||
- 권한 모델/토큰 저장 구조 변경.
|
||||
- 원문 Bearer Token의 응답·로그·DB 저장.
|
||||
|
||||
## 3. 인수조건 (Acceptance Criteria)
|
||||
|
||||
- [ ] 유효 토큰으로 권한 결과를 조회하면 VPD predicate와 기본 SELECT에 결합한 effective SQL을 확인할 수 있다.
|
||||
- [ ] 0행 결과도 성공적인 VPD 차단 결과로 trace 표시 대상이 된다.
|
||||
- [ ] 신규/갱신 object Handler와 기본 VPD/벡터 Handler는 진단 필드를 응답에 포함한다.
|
||||
- [ ] 기존 Handler가 진단 필드를 반환하지 않아도 같은 DB의 `set_user_by_bearer` 컨텍스트에서 기본 VPD predicate를 재조회한다.
|
||||
- [ ] trace를 얻을 수 없는 DB 계정·custom Filter·구버전 Handler는 조회 결과를 실패로 바꾸지 않고 적용 방법을 안내한다.
|
||||
- [ ] trace에는 predicate 표현식과 SQL 구조만 포함하며 Bearer 원문은 포함하지 않는다.
|
||||
- [ ] 초기화 및 기존 설치용 SQL에 `CB_AGENT_DOC_VPD_FILTER` 실행 권한과 갱신 절차가 명시된다.
|
||||
- [ ] 관련 단위/템플릿/Handler source 테스트와 전체 Maven 테스트가 통과한다.
|
||||
|
||||
## 4. 컨텍스트 & 제약
|
||||
|
||||
- ORDS는 공통 `CB_ORDS` 계정으로 접속하고 `cb_ords_handler_pkg.set_vpd_context`가 Bearer key를 `CB_AGENT_CTX`에 매핑한다.
|
||||
- 기본 VPD 함수 `ADMIN.CB_AGENT_DOC_VPD_FILTER`가 현재 컨텍스트의 권한 규칙으로 predicate를 만든다. 정책 함수의 반환값을 직접 표시하는 것이 행 결과를 다시 계산하는 별도 Java 권한 로직보다 정합성이 높다.
|
||||
- 기존 Handler는 응답 계약을 바꾸지 않을 수 있으므로, 백오피스 DB 연결에서 같은 Bearer key를 별도 세션 컨텍스트에 설정해 fallback을 수행한다. fallback은 세션 종료 전에 반드시 `clear_user`를 실행한다.
|
||||
- ORDS 응답 trace가 없거나 fallback 권한이 없으면 권한 결과 자체는 그대로 반환한다. trace는 관측성 보조 기능이며 접근 통제 경로가 아니다.
|
||||
- `ADMIN`은 현재 PoC의 VPD 함수/컨텍스트 소유자다. 다른 소유자 구조는 이번 범위에서 자동 추론하지 않는다.
|
||||
|
||||
## 5. 아키텍처 개요
|
||||
|
||||
```text
|
||||
[Probe form: bearerToken + object]
|
||||
│
|
||||
▼
|
||||
[ORDS Handler: set_vpd_context]
|
||||
│
|
||||
┌──────┴────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
[DBMS_RLS SELECT] [same-context trace]
|
||||
│ │
|
||||
└──────┬────────┘
|
||||
▼
|
||||
[ORDS JSON: items + vpd_predicate + effective_sql]
|
||||
│
|
||||
▼ (trace absent only)
|
||||
[Backoffice DB fallback: set_user_by_bearer → filter → clear_user]
|
||||
│
|
||||
▼
|
||||
[ProbeResult → “토큰 적용 후 SQL”]
|
||||
```
|
||||
|
||||
- I/O 경계: `OrdsProbeService.findVpdPredicate`와 ORDS HTTP 호출은 외부 I/O다. `ProbeResult.hasSqlTrace/withSqlTrace`와 SQL 문자열 조합은 표현/변환 책임으로 둔다.
|
||||
- 안전 경계: predicate 조회 실패는 조회 실패가 아니며 trace만 생략한다. Bearer header는 기존처럼 마스킹된 request detail만 노출한다.
|
||||
|
||||
## 6. 데이터 모델
|
||||
|
||||
- `ProbeResult.vpdPredicate`: VPD 정책 함수가 현재 토큰 컨텍스트에서 반환한 predicate 문자열(nullable).
|
||||
- `ProbeResult.effectiveSql`: Handler의 기본 SELECT와 predicate/row limit을 결합한 확인용 SQL(nullable).
|
||||
- ORDS JSON 성공 응답에 두 문자열을 optional root field로 추가한다. `items`/`rows` 기존 배열 계약은 유지한다.
|
||||
- trace SQL은 `:row_limit` bind 표기를 유지하며 실제 Bearer 값이나 context 값은 문자열에 치환하지 않는다.
|
||||
|
||||
## 7. 함수 명세 (Function Specs)
|
||||
|
||||
| 함수 | 책임(1줄) | 시그니처(잠정) | 입력 | 출력 | 에러/실패 | 복잡? |
|
||||
|------|-----------|----------------|------|------|-----------|-------|
|
||||
| `ProbeResult.hasSqlTrace` | effective SQL 존재 여부를 판단 | `boolean hasSqlTrace()` | result fields | boolean | blank/null이면 false | 단순 |
|
||||
| `ProbeResult.withSqlTrace` | 기존 결과에 trace 두 필드를 불변으로 부착 | `ProbeResult withSqlTrace(String, String)` | predicate, SQL | 새 `ProbeResult` | 없음 | 단순 |
|
||||
| `OrdsProbeService.runProbe` | ORDS 결과를 파싱하고 필요한 경우 trace fallback을 연결 | `ProbeResult runProbe(ProbeCommand)` | token/object/limit | ProbeResult | 기존 상태 분류 유지 | **복잡** |
|
||||
| `parseSuccess` | ORDS JSON 배열과 optional trace 필드 파싱 | `ProbeResult parseSuccess(...)` | body, objectId, HTTP evidence | ProbeResult | 배열 누락 시 예외 | 단순 |
|
||||
| `traceValue` | JSON scalar trace 필드 정규화 | `String traceValue(JsonNode, String)` | root/field | nullable String | 비 scalar/blank이면 null | 단순 |
|
||||
| `addLocalSqlTrace` | predicate와 보호 객체 컬럼으로 effective SQL 생성 | `ProbeResult addLocalSqlTrace(...)` | result/token/object | trace 부착 결과 | DB/컬럼 조회 실패 시 원 결과 | **복잡** |
|
||||
| `findVpdPredicate` | 같은 DB 연결에서 컨텍스트 설정→predicate 조회→컨텍스트 정리 | `String findVpdPredicate(String, ProtectedObject)` | bearer/object | nullable predicate | 권한/DB 오류는 null | **복잡** |
|
||||
| `executeContextSetter` | DB 세션에 Bearer 기반 사용자 컨텍스트 설정 | `void executeContextSetter(Connection, String)` | connection/token | 없음 | SQLException | 단순 |
|
||||
| `clearContext` | fallback 세션의 사용자 컨텍스트 제거 | `void clearContext(Connection)` | connection | 없음 | cleanup 오류 무시 | 단순 |
|
||||
| `objectQuerySource` | 생성 Handler에 trace metadata 출력 블록을 포함 | `String objectQuerySource(ProtectedObject,List<String>)` | object/columns | PL/SQL source | 식별자/컬럼 검증은 호출부 | **복잡** |
|
||||
|
||||
복잡 함수 상세는 `fn-addLocalSqlTrace.md`, `fn-findVpdPredicate.md`에 둔다. `runProbe`의 기본 상태 분류 계약은 기존 #557 설계를 계승한다.
|
||||
|
||||
## 8. 흐름 / 알고리즘
|
||||
|
||||
1. ORDS base URL, token, 보호 객체를 기존 검증 절차로 확인한다.
|
||||
2. ORDS POST를 호출한다. 갱신된 Handler는 `set_vpd_context` 직후 동일 VPD 함수를 호출해 trace를 만들고, `items`와 함께 JSON으로 반환한다.
|
||||
3. 백오피스는 `items`/`rows`를 기존 방식으로 파싱한다.
|
||||
4. trace가 없고 일반 object Handler이면 백오피스 DB의 한 연결에서 `set_user_by_bearer`를 호출한다.
|
||||
5. 같은 연결에서 `SELECT admin.cb_agent_doc_vpd_filter(?, ?) FROM dual`을 수행한다.
|
||||
6. `finally` 경로에서 `clear_user`를 호출하고 연결을 pool에 반환한다.
|
||||
7. 보호 객체의 등록 컬럼과 predicate를 이용해 `SELECT ... FROM OWNER.OBJECT o WHERE (...) AND ROWNUM ...` 형태를 만든다.
|
||||
8. 화면은 결과 행·사용자 권한 뒤에 predicate와 effective SQL을 표시한다. trace가 없으면 ORDS/DB 적용 절차를 안내한다.
|
||||
|
||||
## 9. 엣지케이스 & 에러 처리
|
||||
|
||||
- token/ORDS 오류: 기존 `ProbeStatus`와 HTTP evidence 처리만 수행하며 fallback을 실행하지 않는다.
|
||||
- 0행: `VPD_DENY_EMPTY_RESULT`를 유지하고 trace를 표시한다.
|
||||
- ORDS 신규 trace가 있고 local DB trace가 불가능한 경우: ORDS 응답 trace를 그대로 사용한다.
|
||||
- 기존 Handler·DB 계정에 trace 권한이 없는 경우: 결과 성공/0행을 유지하고 trace 미지원 안내를 표시한다.
|
||||
- fallback context setter가 실패한 경우: 반드시 cleanup을 시도하고 trace는 null로 둔다.
|
||||
- vector object: 전용 Handler의 복합 vector SQL을 일반 `SELECT`로 오표시하지 않도록 local generic fallback을 생략한다. 갱신된 vector Handler가 자체 trace를 반환하면 표시한다.
|
||||
- custom VPD Filter: 기본 함수 반환값을 custom Filter의 실제 predicate로 주장하지 않는다. trace가 없으면 별도 Filter 확인을 안내한다.
|
||||
- predicate가 길어도 DB 함수 반환 한도(`VARCHAR2(32767)`)와 화면 `<pre>`로 처리한다.
|
||||
|
||||
## 10. 테스트 계획
|
||||
|
||||
- `ProbeResultTest`: trace 부착 후 기존 rows/status 보존, blank trace 미표시.
|
||||
- `OrdsMetadataServiceTest`: 생성 object Handler에 `set_vpd_context`, default VPD function, JSON trace 필드와 컬럼 SQL이 포함되는지 확인.
|
||||
- `GuidedFlowTemplateTest`: 결과 화면/Handler 안내/초기화 SQL/기존 설치 grant 및 vector trace source 확인.
|
||||
- `mvn test`: 전체 단위·템플릿 테스트.
|
||||
- 운영 smoke: 신규 토큰으로 `/probe`에서 HR/SELF/ALL 결과를 각각 실행하고 predicate의 `DEPT_CODE`, `OWNER_EMP_NO`, `1 = 1` 또는 `1 = 0` 차이를 확인한다. 토큰 원문이 response body/log에 없는지 확인한다.
|
||||
|
||||
## 11. 리스크 & 대안 검토
|
||||
|
||||
- `V$SQL`/`DBMS_XPLAN` 조회는 connection pool의 다른 세션·cursor·권한 문제 때문에 결과가 불안정하다. 정책 함수 반환값을 같은 context에서 표시하는 방식을 선택한다.
|
||||
- Java에서 permission table을 다시 계산하는 방식은 그룹 상속·DENY 우선순위·custom policy와 어긋날 수 있다. DB의 정책 함수 자체를 trace source로 사용한다.
|
||||
- Handler 응답 계약을 강제로 변경하면 기존 Handler가 실패할 수 있다. optional response fields와 non-failing fallback을 사용한다.
|
||||
- fallback에서 context cleanup을 놓치면 pool connection 간 사용자 혼선이 발생한다. `finally` cleanup을 고정하고 cleanup 오류는 결과를 덮지 않는다.
|
||||
|
||||
## 12. 미해결 질문 (Open Questions)
|
||||
|
||||
- custom VPD Filter까지 정책 함수별로 자동 trace할지는 별도 이슈로 남긴다.
|
||||
- 운영 DB에서 `ADMIN` 이외의 VPD 소유자 구조를 지원하려면 함수 owner 설정을 명시적으로 추가해야 한다.
|
||||
Reference in New Issue
Block a user