[Developer] #573 normalize vector access whitelist
This commit is contained in:
@@ -268,15 +268,20 @@ public class OrdsProbeService {
|
||||
}
|
||||
|
||||
static String vectorEffectiveSql(ProtectedObject object, String predicate) {
|
||||
String vpdFilter = isAllowAllPredicate(predicate) ? "" : " AND (" + predicate + ")";
|
||||
return "SELECT chunk_id, document_id, chunk_no, title, chunk_text, source_uri, tech_tag, score"
|
||||
+ " FROM (SELECT d.chunk_id, d.document_id, d.chunk_no, d.title, d.chunk_text,"
|
||||
+ " d.source_uri, d.tech_tag, VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE) AS score"
|
||||
+ " FROM " + object.owner() + "." + object.objectName() + " d"
|
||||
+ " WHERE d.embedding IS NOT NULL AND (" + predicate + ")"
|
||||
+ " WHERE d.embedding IS NOT NULL" + vpdFilter
|
||||
+ " ORDER BY score) ranked_chunks"
|
||||
+ " WHERE ROWNUM <= LEAST(GREATEST(NVL(:row_limit, 10), 1), 100)";
|
||||
}
|
||||
|
||||
private static boolean isAllowAllPredicate(String predicate) {
|
||||
return predicate != null && predicate.trim().replaceAll("\\s+", " ").equals("1 = 1");
|
||||
}
|
||||
|
||||
private String findVpdPredicate(String bearerToken, ProtectedObject object) {
|
||||
try {
|
||||
return jdbcTemplate.execute((ConnectionCallback<String>) connection -> {
|
||||
|
||||
@@ -145,12 +145,13 @@
|
||||
<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>
|
||||
<p class="form-hint mb-0">이 부분이 검색어 벡터와 저장 벡터의 거리 계산입니다. 아래에는 실제로 등록된 역할 기반 행 접근 조건만 표시됩니다.</p>
|
||||
</section>
|
||||
<section class="probe-exchange" data-sql-trace-field="vpd_predicate">
|
||||
<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>
|
||||
<pre th:if="${result.vpdPredicate() == '1 = 1'}">1 = 1 (ALL: 추가 행 필터 없음)</pre>
|
||||
<pre th:unless="${result.vpdPredicate() == '1 = 1'}" 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에서 계산됩니다. 기본 whitelist 역할의 ALL은 추가 행 필터 없이 조회를 허용하고, 실제로 TAG·부서 조건을 등록한 역할만 그 조건이 SQL에 들어갑니다. 권한이 없으면 <code>1 = 0</code>입니다.</p>
|
||||
</section>
|
||||
<section class="probe-exchange" data-sql-trace-field="effective_sql">
|
||||
<h3>권한 적용 SQL</h3>
|
||||
|
||||
@@ -23,4 +23,16 @@ class OrdsProbeServiceTest {
|
||||
.contains("ROWNUM <= LEAST(GREATEST(NVL(:row_limit, 10), 1), 100)")
|
||||
.doesNotContain("Bearer", "0.10", "0.20");
|
||||
}
|
||||
|
||||
@Test
|
||||
void omitsNoopAllowAllPredicateFromVectorEffectiveSql() {
|
||||
ProtectedObject object = new ProtectedObject(
|
||||
5L, "ADMIN", "CB_VECTOR_SEARCH_DOCUMENTS", "cb-agent-vector/search", "Y");
|
||||
|
||||
String sql = OrdsProbeService.vectorEffectiveSql(object, " 1 = 1 ");
|
||||
|
||||
assertThat(sql)
|
||||
.contains("WHERE d.embedding IS NOT NULL ORDER BY score")
|
||||
.doesNotContain("AND (1 = 1)", "REGEXP_LIKE");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,10 +147,26 @@ class GuidedFlowTemplateTest {
|
||||
assertThat(result)
|
||||
.contains("토큰 적용 후 SQL")
|
||||
.contains("권한 적용 SQL")
|
||||
.contains("VPD가 추가한 WHERE 조건");
|
||||
.contains("VPD가 추가한 WHERE 조건")
|
||||
.contains("ALL: 추가 행 필터 없음")
|
||||
.contains("기본 whitelist 역할");
|
||||
assertThat(vectorSql)
|
||||
.contains("VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)")
|
||||
.contains("WHERE d.embedding IS NOT NULL AND (' || v_vpd_predicate || ')");
|
||||
.contains("IF TRIM(v_vpd_predicate) <> '1 = 1' THEN")
|
||||
.contains("' AND (' || v_vpd_predicate || ')'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void vectorDefaultRolesAreSeededAsWhitelistAllRules() throws IOException {
|
||||
String setup = Files.readString(Path.of("sql/adb/32_dds_vector_tag_setup.sql"));
|
||||
|
||||
assertThat(setup)
|
||||
.contains("ensure_vector_all_permission(10)")
|
||||
.contains("ensure_vector_all_permission(20)")
|
||||
.contains("ensure_vector_all_permission(30)")
|
||||
.contains("UPPER(TRIM(rule_type)) = 'TAG'")
|
||||
.contains("NULL, 'ALL', NULL")
|
||||
.doesNotContain("SYS.ODCIVARCHAR2LIST('SPRING_BOOT', 'ORDS')");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user