|
|
|
|
@@ -108,73 +108,48 @@
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="result-section" th:if="${result.hasExecutionEvidence()}">
|
|
|
|
|
<section class="result-section" th:if="${result.hasFgaExecutionEvidence()}">
|
|
|
|
|
<div class="section-heading compact-heading">
|
|
|
|
|
<div>
|
|
|
|
|
<h3>DB 실행 증적</h3>
|
|
|
|
|
<p class="section-subtitle">DB가 보관한 최신 cursor 10건에서 같은 보호 대상을 찾아 SQL_ID로 확인한 결과입니다.</p>
|
|
|
|
|
<h3>DB 감사 실행 증적</h3>
|
|
|
|
|
<p class="section-subtitle">이번 요청 ID와 일치하는 DB 감사 행입니다. 화면에서 재계산한 값이 아닙니다.</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="badge text-bg-success" th:text="${'SQL_ID ' + result.executionEvidence().sqlId()}">SQL_ID</span>
|
|
|
|
|
<span class="badge text-bg-success">FGA</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="effective-preview">
|
|
|
|
|
<dl>
|
|
|
|
|
<div><dt>Child cursor</dt><dd th:text="${result.executionEvidence().childNumber()}">0</dd></div>
|
|
|
|
|
<div><dt>최근 실행</dt><dd th:text="${result.executionEvidence().lastActiveAt()} ?: '-'">2026-07-01T10:00</dd></div>
|
|
|
|
|
<div><dt>Cursor 실행 횟수</dt><dd th:text="${result.executionEvidence().executions()}">1</dd></div>
|
|
|
|
|
<div><dt>Cursor 처리 행</dt><dd th:text="${result.executionEvidence().rowsProcessed()}">10</dd></div>
|
|
|
|
|
<div><dt>누적 경과 시간</dt><dd th:text="${result.executionEvidence().elapsedMillis() + ' ms'}">10 ms</dd></div>
|
|
|
|
|
<div><dt>Buffer gets</dt><dd th:text="${result.executionEvidence().bufferGets()}">0</dd></div>
|
|
|
|
|
<div><dt>감사 시각</dt><dd th:text="${result.fgaExecutionEvidence().eventAt()} ?: '-'">2026-07-01T10:00:00Z</dd></div>
|
|
|
|
|
<div><dt>DB 실행 사용자</dt><dd th:text="${result.fgaExecutionEvidence().dbUser()} ?: '-'">CB_ORDS</dd></div>
|
|
|
|
|
<div><dt>요청 식별자</dt><dd><code th:text="${result.fgaExecutionEvidence().clientId()} ?: '-'">request-id</code></dd></div>
|
|
|
|
|
<div><dt>문장 종류</dt><dd th:text="${result.fgaExecutionEvidence().statementType()} ?: '-'">SELECT</dd></div>
|
|
|
|
|
</dl>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="probe-exchange-grid mt-3">
|
|
|
|
|
<section class="probe-exchange" data-sql-trace-field="executed_sql">
|
|
|
|
|
<h3>DB가 기록한 원문 SQL</h3>
|
|
|
|
|
<p class="form-hint">V$SQL의 원문입니다. Oracle VPD가 WHERE 조건을 내부적으로 주입하기 전 SQL 형태로 저장됩니다.</p>
|
|
|
|
|
<pre th:text="${result.executionEvidence().originalSql()}">SELECT ...</pre>
|
|
|
|
|
<h3>DB가 감사한 실행 SQL</h3>
|
|
|
|
|
<p class="form-hint">FGA의 SQL_TEXT입니다.</p>
|
|
|
|
|
<pre th:text="${result.fgaExecutionEvidence().sqlText()}">SELECT ...</pre>
|
|
|
|
|
</section>
|
|
|
|
|
<section class="probe-exchange" data-sql-trace-field="execution_plan" th:if="${result.executionEvidence().hasPredicatePlan()}">
|
|
|
|
|
<h3>DBMS_XPLAN Predicate Information</h3>
|
|
|
|
|
<p class="form-hint">이 cursor의 실행계획에 기록된 Access/Filter predicate입니다. VPD 적용 근거는 여기서 확인합니다.</p>
|
|
|
|
|
<pre th:text="${result.executionEvidence().predicatePlan()}">Predicate Information</pre>
|
|
|
|
|
<section class="probe-exchange" data-sql-trace-field="fga_rls_info" th:if="${result.fgaExecutionEvidence().hasRlsInfo()}">
|
|
|
|
|
<h3>DB가 감사한 VPD predicate</h3>
|
|
|
|
|
<p class="form-hint">FGA의 RLS_INFO입니다. 적용된 VPD 정책명과 predicate가 기록됩니다.</p>
|
|
|
|
|
<pre th:text="${result.fgaExecutionEvidence().rlsInfo()}">RLS_INFO</pre>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="alert alert-light mt-3 mb-0" th:if="${result.executionEvidenceMessage() != null}"
|
|
|
|
|
th:text="${result.executionEvidenceMessage()}"></div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="result-section" th:if="${result.hasExecutionCandidates()}">
|
|
|
|
|
<div class="section-heading compact-heading">
|
|
|
|
|
<div>
|
|
|
|
|
<h3>최근 DB cursor 10건</h3>
|
|
|
|
|
<p class="section-subtitle">백오피스 DB 연결이 읽은 최신 cursor입니다. 보호 대상과 일치한 SQL은 초록 배지로 표시합니다.</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="badge text-bg-light" th:text="${result.executionCandidates().size() + '건'}">10건</span>
|
|
|
|
|
</div>
|
|
|
|
|
<article class="probe-exchange mt-3" th:each="candidate : ${result.executionCandidates()}">
|
|
|
|
|
<div class="section-heading compact-heading">
|
|
|
|
|
<div>
|
|
|
|
|
<h3 th:text="${'SQL_ID ' + candidate.sqlId()}">SQL_ID</h3>
|
|
|
|
|
<p class="form-hint mb-0"
|
|
|
|
|
th:text="${'최근 실행: ' + (candidate.lastActiveAt() ?: '-') + ' · Parsing schema: ' + (candidate.parsingSchema() ?: '-') + ' · Child cursor: ' + candidate.childNumber()}">최근 실행</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="badge"
|
|
|
|
|
th:classappend="${candidate.matchesProtectedObject()} ? ' text-bg-success' : ' text-bg-light'"
|
|
|
|
|
th:text="${candidate.matchesProtectedObject()} ? '보호 대상 일치' : '다른 SQL'">다른 SQL</span>
|
|
|
|
|
</div>
|
|
|
|
|
<pre class="mt-2 mb-0" th:text="${candidate.originalSql()} ?: '-'">SELECT ...</pre>
|
|
|
|
|
</article>
|
|
|
|
|
<div class="alert alert-light mt-3 mb-0" th:if="${result.fgaExecutionEvidenceMessage() != null}"
|
|
|
|
|
th:text="${result.fgaExecutionEvidenceMessage()}"></div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="result-section sql-trace-section" th:if="${result.hasSqlTrace()}">
|
|
|
|
|
<div class="section-heading compact-heading">
|
|
|
|
|
<div>
|
|
|
|
|
<h3>실행 요청 SQL</h3>
|
|
|
|
|
<h3>참고용 SQL 재현</h3>
|
|
|
|
|
<p class="section-subtitle"
|
|
|
|
|
th:text="${vectorSearch ? 'VECTOR_DISTANCE 검색과 현재 사용자의 역할 기반 VPD 조건을 결합한 읽기용 SQL입니다.' : '현재 사용자 컨텍스트에서 계산한 VPD 조건을 ORDS 조회문에 결합한 읽기용 SQL입니다.'}">
|
|
|
|
|
현재 사용자 컨텍스트에서 계산한 VPD 조건을 ORDS 조회문에 결합한 읽기용 SQL입니다.
|
|
|
|
|
th:text="${vectorSearch ? 'VECTOR_DISTANCE 검색과 권한 규칙을 조합한 참고용 표현입니다.' : '현재 권한 규칙을 조합한 참고용 표현입니다.'}">
|
|
|
|
|
현재 권한 규칙을 조합한 참고용 표현입니다.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="badge text-bg-light">DBMS_RLS predicate</span>
|
|
|
|
|
<span class="badge text-bg-light">참고용</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="probe-exchange-grid mt-3">
|
|
|
|
|
<section class="probe-exchange" data-sql-trace-field="vpd_context" th:if="${tokenContext}">
|
|
|
|
|
@@ -205,24 +180,24 @@ ROWNUM <= :row_limit</pre>
|
|
|
|
|
<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>
|
|
|
|
|
<h3 th:text="${vectorSearch ? '역할 기반 권한 필터 (재현)' : 'VPD 조건 (재현)'}">VPD 조건 (재현)</h3>
|
|
|
|
|
<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>
|
|
|
|
|
<h3>SQL (재현)</h3>
|
|
|
|
|
<pre th:text="${result.effectiveSql()}">SELECT ... WHERE (...) AND ROWNUM <= ...</pre>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="form-hint mt-2 mb-0">
|
|
|
|
|
이 SQL은 현재 요청의 권한 조건을 읽기 쉽게 재현한 표현입니다. 실제 DB cursor 원문과 적용 predicate는 SQL_ID·DBMS_XPLAN 증적에서 확인합니다. 컬럼 마스킹은 별도 Redaction 정책입니다.
|
|
|
|
|
이 영역은 권한 규칙을 읽기 쉽게 재현한 표현일 뿐, 실행 증적이 아닙니다. 실제 SQL과 VPD predicate는 위의 DB 감사 실행 증적(FGA)으로 확인합니다. 컬럼 마스킹은 별도 Redaction 정책입니다.
|
|
|
|
|
</p>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasExecutionEvidence() and result.executionEvidenceMessage() != null}">
|
|
|
|
|
<strong>실제 DB cursor SQL</strong>
|
|
|
|
|
<span th:text="${result.executionEvidenceMessage()}">최근 SQL_ID를 찾지 못했습니다.</span>
|
|
|
|
|
<div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasFgaExecutionEvidence() and result.fgaExecutionEvidenceMessage() != null}">
|
|
|
|
|
<strong>DB 감사 실행 증적</strong>
|
|
|
|
|
<span th:text="${result.fgaExecutionEvidenceMessage()}">감사 행을 찾지 못했습니다.</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasSqlTrace()}">
|
|
|
|
|
|