[Developer] #567 expose VPD effective SQL trace

This commit is contained in:
devmrko
2026-06-30 11:40:02 +09:00
parent 5c40a9aa33
commit 3a07788e5c
17 changed files with 675 additions and 5 deletions

View File

@@ -97,4 +97,29 @@ class ProbeResultTest {
assertThat(result.plainSummary()).contains("토큰과 사용자 권한은 확인");
assertThat(result.nextAction()).contains("토큰이나 권한을 바꾸지 말고").contains("자동 Filter");
}
@Test
void exposesVpdSqlTraceWithoutChangingTheRows() {
ProbeResult result = new ProbeResult(
ProbeStatus.SUCCESS,
List.of("DEPT_CODE"),
List.of(Map.of("DEPT_CODE", "HR")),
1,
List.of(),
null,
null,
null,
null,
null,
null
).withSqlTrace(
"(DEPT_CODE = SYS_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE'))",
"SELECT * FROM ADMIN.DOCUMENTS WHERE (DEPT_CODE = SYS_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE'))"
);
assertThat(result.hasSqlTrace()).isTrue();
assertThat(result.vpdPredicate()).contains("DEPT_CODE");
assertThat(result.effectiveSql()).contains("ADMIN.DOCUMENTS");
assertThat(result.rowCount()).isEqualTo(1);
}
}

View File

@@ -0,0 +1,54 @@
package com.cloudhandson.vpdbackoffice.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedColumn;
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedObject;
import com.cloudhandson.vpdbackoffice.mapper.OrdsMetadataMapper;
import java.util.List;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
class OrdsMetadataServiceTest {
@Test
void generatedObjectHandlerIncludesTokenSpecificVpdSqlTrace() {
OrdsMetadataMapper mapper = mock(OrdsMetadataMapper.class);
ProtectedObjectService protectedObjectService = mock(ProtectedObjectService.class);
DataSource dataSource = mock(DataSource.class);
ProtectedObject object = new ProtectedObject(
7L,
"ADMIN",
"CB_V_SEARCH_DOCUMENTS",
"cb-ords/cb-object-query/admin/cb_v_search_documents",
"Y"
);
when(protectedObjectService.assertEnabled(7L)).thenReturn(object);
when(protectedObjectService.findColumns(7L)).thenReturn(List.of(
new ProtectedColumn(1L, 7L, "DOC_ID", "N", null, "PUBLIC", "NONE"),
new ProtectedColumn(2L, 7L, "DEPT_CODE", "N", null, "INTERNAL", "NONE")
));
OrdsMetadataService service = new OrdsMetadataService(
mapper,
new JdbcTemplate(dataSource),
protectedObjectService,
dataSource,
"",
"",
""
);
String source = service.objectQueryHandlerSource(7L);
assertThat(source)
.contains("cb_ords_handler_pkg.set_vpd_context(:auth_header)")
.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)")
.contains("SELECT o.DOC_ID, o.DEPT_CODE FROM ADMIN.CB_V_SEARCH_DOCUMENTS o");
}
}

View File

@@ -31,6 +31,8 @@ class GuidedFlowTemplateTest {
.doesNotContain("name=\"tokenKeyId\"");
assertThat(result)
.contains("적용된 사용자와 권한")
.contains("토큰 적용 후 SQL")
.contains("vpd_predicate")
.contains("다음에 할 일")
.contains("<details")
.contains("기술 상세");
@@ -138,6 +140,18 @@ class GuidedFlowTemplateTest {
.doesNotContain("JSON_QUERY(\n :body_text")
.doesNotContain("JSON_VALUE(\n :body_text");
assertThat(sql.split("v_body_text := :body_text", -1)).hasSize(2);
assertThat(sql).contains("vpd_predicate").contains("effective_sql");
}
@Test
void existingOrdsSetupCanReturnTheVpdPredicateTrace() throws IOException {
String setup = Files.readString(Path.of("sql/adb/22_agent_ords_security_ords_handler_setup.sql"));
String grant = Files.readString(Path.of("sql/adb/33_agent_ords_sql_trace_grant.sql"));
assertThat(setup)
.contains("admin.cb_agent_doc_vpd_filter('ADMIN', 'CB_V_SEARCH_DOCUMENTS')")
.contains("APEX_JSON.WRITE('effective_sql', v_effective_sql)");
assertThat(grant).contains("GRANT EXECUTE ON cb_agent_doc_vpd_filter TO cb_ords");
}
private String template(String relativePath) throws IOException {