[Developer] #572 separate vector search and VPD filter trace
This commit is contained in:
54
docs/design/572-vector-vpd-filter-separation/README.md
Normal file
54
docs/design/572-vector-vpd-filter-separation/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 설계서: VECTOR_DISTANCE와 역할 기반 VPD 필터 분리 표시 (#572)
|
||||
|
||||
> **상태**: Approved
|
||||
> **작성**: [AI] Architect · **최종수정**: 2026-06-30
|
||||
> **추적성** — Redmine: #572 · 선행 기능: #558, #570, #571
|
||||
> · 구현 파일: `probe-result.html`, `OrdsProbeService`, `GuidedFlowTemplateTest`
|
||||
> · 테스트: `mvn test`
|
||||
|
||||
## 1. 목적
|
||||
|
||||
벡터 검색의 유사도 계산과 VPD 권한 필터를 서로 다른 개념으로 표시한다.
|
||||
|
||||
- **벡터 검색**: `VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)`로 score를 계산하고 `ORDER BY score`로 가까운 순서의 Top-K를 반환한다.
|
||||
- **권한 필터**: `set_vpd_context`가 설정한 사용자/역할을 `CB_AGENT_DOC_VPD_FILTER`가 읽고, 해당 역할의 permission rule에 따라 `TECH_TAG` 등 대상 컬럼 조건을 만든다. 이 조건은 검색어가 아니라 접근 통제다.
|
||||
|
||||
최종 effective SQL에는 두 조건이 함께 존재할 수 있지만, 화면에서는 각각을 분리해 오해를 막는다.
|
||||
|
||||
## 2. 인수조건
|
||||
|
||||
- [ ] 벡터 결과에 `VECTOR_DISTANCE`와 `ORDER BY score`가 “벡터 유사도 검색 기준”으로 별도 표시된다.
|
||||
- [ ] `REGEXP_LIKE`가 있으면 “역할 기반 VPD 권한 필터”로 표시하고, 벡터 검색 조건이 아님을 설명한다.
|
||||
- [ ] 권한 필터 설명에 선택 사용자에게 직접/그룹으로 연결된 role의 permission rule에서 계산되었다고 표시한다.
|
||||
- [ ] `ALL` 권한은 `1 = 1`, 권한 없음/허용 rule 없음은 `1 = 0`, TAG 권한은 해당 role의 TAG 값만 표시한다.
|
||||
- [ ] 최종 effective SQL은 실제 결합 형태를 유지하되, 화면 앞에서 검색 기준과 권한 필터를 분리해 보여준다.
|
||||
- [ ] 일반 객체의 기존 VPD trace, 사용자 context, SQL 결과는 회귀하지 않는다.
|
||||
|
||||
## 3. 화면 흐름
|
||||
|
||||
```text
|
||||
[set_vpd_context 사용자]
|
||||
│
|
||||
▼
|
||||
[역할 permission rule → VPD 권한 필터]
|
||||
│
|
||||
├──────────────┐
|
||||
▼ ▼
|
||||
[TECH_TAG 조건] [VECTOR_DISTANCE + ORDER BY score]
|
||||
└──────┬───────┘
|
||||
▼
|
||||
[effective SQL / Top-K]
|
||||
```
|
||||
|
||||
## 4. 데이터/보안 계약
|
||||
|
||||
- 사용자 입력 평문은 임베딩으로 변환되어 `:embedding` 경계로만 전달된다. 평문·임베딩은 VPD predicate에 섞지 않는다.
|
||||
- VPD predicate는 DB 함수가 현재 context에서 반환한 값만 표시한다. UI가 임의로 role 조건이나 SQL을 생성하지 않는다.
|
||||
- `REGEXP_LIKE`는 TAG rule을 저장한 역할이 실제로 effective role에 포함될 때만 나타난다. role/permission이 없으면 fail-closed predicate `1 = 0`이 반환된다.
|
||||
- `SELECT *`를 사용하지 않고 명시적 컬럼과 score만 반환해 embedding 원문을 노출하지 않는다.
|
||||
|
||||
## 5. 테스트/운영 검증
|
||||
|
||||
- 템플릿 테스트: 벡터 유사도 기준, 역할 기반 VPD 필터, `VECTOR_DISTANCE`, `ORDER BY score` 문구 확인.
|
||||
- 전체 테스트: `mvn test`.
|
||||
- 운영 smoke: `agent_hr`에서 `SPRING_BOOT/ORDS` 권한 필터와 vector score를 함께 확인하고, `agent_all`에서는 전체 TAG rule이 role에 연결된 경우에만 전체 조건이 표시되는지 확인한다.
|
||||
@@ -112,7 +112,10 @@
|
||||
<div class="section-heading compact-heading">
|
||||
<div>
|
||||
<h3>토큰 적용 후 SQL</h3>
|
||||
<p class="section-subtitle">ORDS Handler가 반환한 VPD predicate 또는 같은 토큰 컨텍스트를 재현해 조회한 predicate를 기본 조회문에 합친 형태입니다.</p>
|
||||
<p class="section-subtitle"
|
||||
th:text="${vectorSearch ? 'VECTOR_DISTANCE 유사도 검색과 현재 사용자의 역할 기반 VPD 권한 필터를 결합한 재현용 SQL입니다.' : 'ORDS Handler가 반환한 VPD predicate 또는 같은 토큰 컨텍스트를 재현해 조회한 predicate를 기본 조회문에 합친 형태입니다.'}">
|
||||
ORDS Handler가 반환한 VPD predicate 또는 같은 토큰 컨텍스트를 재현해 조회한 predicate를 기본 조회문에 합친 형태입니다.
|
||||
</p>
|
||||
</div>
|
||||
<span class="badge text-bg-light">DBMS_RLS predicate</span>
|
||||
</div>
|
||||
@@ -137,9 +140,17 @@
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="probe-exchange" data-sql-trace-field="vector_search" th:if="${vectorSearch}">
|
||||
<h3>벡터 유사도 검색 기준</h3>
|
||||
<pre>VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE) AS score
|
||||
ORDER BY score ASC
|
||||
ROWNUM <= :row_limit</pre>
|
||||
<p class="form-hint mb-0">이 부분이 검색어 벡터와 저장 벡터의 거리 계산입니다. 아래 권한 필터의 TAG 조건은 검색어 유사도 계산이 아니라 접근 통제입니다.</p>
|
||||
</section>
|
||||
<section class="probe-exchange" data-sql-trace-field="vpd_predicate">
|
||||
<h3>VPD가 추가한 WHERE 조건</h3>
|
||||
<h3 th:text="${vectorSearch ? '역할 기반 권한 필터 (VPD)' : 'VPD가 추가한 WHERE 조건'}">VPD가 추가한 WHERE 조건</h3>
|
||||
<pre th:text="${result.vpdPredicate()}">(DEPT_CODE = SYS_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE'))</pre>
|
||||
<p class="form-hint mb-0" th:if="${vectorSearch}">선택한 사용자의 직접 역할·그룹 상속 역할에 연결된 permission rule에서 계산된 조건입니다. 권한이 없으면 <code>1 = 0</code>, ALL이면 <code>1 = 1</code>이 됩니다.</p>
|
||||
</section>
|
||||
<section class="probe-exchange" data-sql-trace-field="effective_sql">
|
||||
<h3>권한 적용 SQL</h3>
|
||||
|
||||
@@ -37,6 +37,9 @@ class GuidedFlowTemplateTest {
|
||||
.contains("토큰 적용 후 SQL")
|
||||
.contains("set_vpd_context 사용자 컨텍스트")
|
||||
.contains("CB_AGENT_CTX.USER_ID")
|
||||
.contains("벡터 유사도 검색 기준")
|
||||
.contains("역할 기반 권한 필터 (VPD)")
|
||||
.contains("VECTOR_DISTANCE")
|
||||
.contains("벡터 검색 Top-K")
|
||||
.contains("벡터 거리 (SCORE)")
|
||||
.contains("vpd_predicate")
|
||||
|
||||
Reference in New Issue
Block a user