[Developer] #567 record actual VPD execution with FGA
This commit is contained in:
@@ -180,4 +180,35 @@ class ProbeResultTest {
|
||||
assertThat(result.hasExecutionCandidates()).isTrue();
|
||||
assertThat(result.executionCandidates()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsAuditedExecutionEvidenceSeparateFromTheReconstructedSql() {
|
||||
FgaExecutionEvidence evidence = new FgaExecutionEvidence(
|
||||
"2026-07-01T10:00:00Z",
|
||||
"CB_ORDS",
|
||||
"probe-123",
|
||||
"SELECT",
|
||||
"SELECT d.doc_id FROM ADMIN.CB_V_SEARCH_DOCUMENTS d",
|
||||
"CB_AGENT_DOC_POLICY: (DEPT_CODE = 'HR')"
|
||||
);
|
||||
ProbeResult result = new ProbeResult(
|
||||
ProbeStatus.SUCCESS,
|
||||
List.of("DOC_ID"),
|
||||
List.of(Map.of("DOC_ID", 1)),
|
||||
1,
|
||||
List.of(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).withSqlTrace("DEPT_CODE = SYS_CONTEXT(...)", "SELECT ... WHERE (...)")
|
||||
.withFgaExecutionEvidence(evidence, null);
|
||||
|
||||
assertThat(result.hasFgaExecutionEvidence()).isTrue();
|
||||
assertThat(result.fgaExecutionEvidence().clientId()).isEqualTo("probe-123");
|
||||
assertThat(result.fgaExecutionEvidence().hasRlsInfo()).isTrue();
|
||||
assertThat(result.effectiveSql()).contains("WHERE");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class OrdsMetadataServiceTest {
|
||||
String source = service.objectQueryHandlerSource(7L);
|
||||
|
||||
assertThat(source)
|
||||
.contains("cb_ords_handler_pkg.set_vpd_context(:auth_header)")
|
||||
.contains("cb_ords_handler_pkg.set_vpd_context(:auth_header, :probe_id)")
|
||||
.contains("admin.cb_agent_doc_vpd_filter('ADMIN', 'CB_V_SEARCH_DOCUMENTS')")
|
||||
.contains("v_vpd_predicate")
|
||||
.contains("APEX_JSON.WRITE('effective_sql', v_effective_sql)")
|
||||
|
||||
@@ -109,14 +109,14 @@ class GuidedFlowTemplateTest {
|
||||
.doesNotContain("name=\"tokenKeyId\"");
|
||||
assertThat(result)
|
||||
.contains("적용된 사용자와 권한")
|
||||
.contains("DB 실행 증적")
|
||||
.contains("최근 DB cursor 10건")
|
||||
.contains("실행 요청 SQL")
|
||||
.contains("실제 DB cursor SQL")
|
||||
.contains("DB 감사 실행 증적")
|
||||
.contains("DB가 감사한 실행 SQL")
|
||||
.contains("DB가 감사한 VPD predicate")
|
||||
.contains("참고용 SQL 재현")
|
||||
.contains("set_vpd_context 사용자 컨텍스트")
|
||||
.contains("CB_AGENT_CTX.USER_ID")
|
||||
.contains("벡터 유사도 검색 기준")
|
||||
.contains("역할 기반 권한 필터 (VPD)")
|
||||
.contains("역할 기반 권한 필터 (재현)")
|
||||
.contains("VECTOR_DISTANCE")
|
||||
.contains("벡터 검색 Top-K")
|
||||
.contains("벡터 거리 (SCORE)")
|
||||
@@ -265,18 +265,18 @@ class GuidedFlowTemplateTest {
|
||||
String vectorSql = Files.readString(Path.of("sql/adb/29_agent_ords_vector_search_ords.sql"));
|
||||
|
||||
assertThat(result)
|
||||
.contains("DB 실행 증적")
|
||||
.contains("DB가 기록한 원문 SQL")
|
||||
.contains("DBMS_XPLAN Predicate Information")
|
||||
.contains("SQL_ID")
|
||||
.contains("실행 요청 SQL")
|
||||
.contains("실제 DB cursor SQL")
|
||||
.contains("실행 요청 SQL (재현)")
|
||||
.contains("VPD가 추가한 WHERE 조건")
|
||||
.contains("DB 감사 실행 증적")
|
||||
.contains("DB가 감사한 실행 SQL")
|
||||
.contains("DB가 감사한 VPD predicate")
|
||||
.contains("RLS_INFO")
|
||||
.contains("참고용 SQL 재현")
|
||||
.contains("SQL (재현)")
|
||||
.contains("VPD 조건 (재현)")
|
||||
.contains("ALL: 추가 행 필터 없음")
|
||||
.contains("기본 whitelist 역할");
|
||||
assertThat(vectorSql)
|
||||
.contains("VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)")
|
||||
.contains("cb_ords_handler_pkg.set_vpd_context(:auth_header, :probe_id)")
|
||||
.contains("IF TRIM(v_vpd_predicate) <> '1 = 1' THEN")
|
||||
.contains("' AND (' || v_vpd_predicate || ')'");
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.cloudhandson.vpdbackoffice.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.cloudhandson.vpdbackoffice.domain.probe.FgaExecutionEvidence;
|
||||
import com.cloudhandson.vpdbackoffice.domain.probe.ProbeResult;
|
||||
import com.cloudhandson.vpdbackoffice.domain.probe.ProbeStatus;
|
||||
import com.cloudhandson.vpdbackoffice.domain.probe.SqlExecutionCandidate;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -17,7 +17,7 @@ import org.thymeleaf.templateresolver.FileTemplateResolver;
|
||||
class ProbeResultTemplateRenderTest {
|
||||
|
||||
@Test
|
||||
void rendersRecentSqlEvidenceUnavailableMessageAsTextNotAsABoolean() {
|
||||
void rendersFgaEvidenceAndUnavailableMessageAsTextNotAsABoolean() {
|
||||
var resolver = new FileTemplateResolver();
|
||||
resolver.setPrefix(Path.of("src/main/resources/templates").toAbsolutePath() + "/");
|
||||
resolver.setSuffix(".html");
|
||||
@@ -38,11 +38,14 @@ class ProbeResultTemplateRenderTest {
|
||||
"{}",
|
||||
"{}",
|
||||
"{}"
|
||||
).withExecutionEvidence(null,
|
||||
"최근 cursor를 찾지 못했습니다.",
|
||||
List.of(new SqlExecutionCandidate(
|
||||
"4f2jz7n2k0s3p", 0, "CB_ORDS",
|
||||
"SELECT * FROM ADMIN.CB_V_SEARCH_DOCUMENTS", "2026-07-01T15:00", true)));
|
||||
).withFgaExecutionEvidence(new FgaExecutionEvidence(
|
||||
"2026-07-01T15:00:00Z",
|
||||
"CB_ORDS",
|
||||
"8db9fc64-c71b-4b9e-9514-2fcaa355d9f3",
|
||||
"SELECT",
|
||||
"SELECT * FROM ADMIN.CB_V_SEARCH_DOCUMENTS",
|
||||
"CB_AGENT_DOC_POLICY: (DEPT_CODE = 'HR')"
|
||||
), null);
|
||||
var context = new Context(Locale.KOREAN);
|
||||
context.setVariable("result", result);
|
||||
context.setVariable("vectorSearch", false);
|
||||
@@ -52,9 +55,9 @@ class ProbeResultTemplateRenderTest {
|
||||
String rendered = engine.process("fragments/probe-result", context);
|
||||
|
||||
assertThat(rendered)
|
||||
.contains("최근 cursor를 찾지 못했습니다.")
|
||||
.contains("최근 DB cursor 10건")
|
||||
.contains("4f2jz7n2k0s3p")
|
||||
.contains("보호 대상 일치");
|
||||
.contains("DB 감사 실행 증적")
|
||||
.contains("8db9fc64-c71b-4b9e-9514-2fcaa355d9f3")
|
||||
.contains("SELECT * FROM ADMIN.CB_V_SEARCH_DOCUMENTS")
|
||||
.contains("CB_AGENT_DOC_POLICY: (DEPT_CODE = 'HR')");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user