[UX] #577 clarify VPD flow and SQL evidence
This commit is contained in:
@@ -7,15 +7,83 @@
|
||||
<header class="dashboard-hero">
|
||||
<div>
|
||||
<span class="architecture-kicker">VPD 권한 운영</span>
|
||||
<h1>권한 관리</h1>
|
||||
<h1>권한 운영 흐름</h1>
|
||||
<p>업무 사용자와 데이터 접근 기준을 관리하고, DB가 적용한 결과까지 확인합니다.</p>
|
||||
</div>
|
||||
<div class="dashboard-hero-actions" aria-label="주요 작업">
|
||||
<a class="btn rw-btn-primary" href="/probe">접근 검증</a>
|
||||
<a class="btn rw-btn-secondary" href="/permissions">접근 규칙</a>
|
||||
</div>
|
||||
<details class="explanation-details dashboard-explanation">
|
||||
<summary>도움말</summary>
|
||||
<p>사용자·그룹·역할에 권한을 연결하면 Oracle VPD가 요청할 때마다 그 규칙을 읽어 허용된 행만 반환합니다. 별도 SQL 필터를 만드는 일은 예외적인 고급 작업입니다.</p>
|
||||
<summary>도움말: 메뉴와 권한 적용 구조 보기</summary>
|
||||
<div class="product-help-overview">
|
||||
<section class="product-help-section">
|
||||
<h2>왜 권한 테이블을 따로 관리하나요?</h2>
|
||||
<p>DB 연결은 VPD/DDS 실행 계정 하나로 유지하되, 실제 업무 사용자는 <code>CB_APP_USER</code>와 역할·권한 테이블에서 찾습니다. 요청마다 토큰이 사용자를 식별하고 VPD가 그 사용자의 권한만 조건으로 계산하므로, 같은 실행 계정으로도 사용자마다 다른 행과 컬럼을 안전하게 반환할 수 있습니다.</p>
|
||||
</section>
|
||||
|
||||
<section class="product-help-section">
|
||||
<h2>메뉴 안내</h2>
|
||||
<dl class="menu-role-grid">
|
||||
<div><dt>권한 관리</dt><dd>사용자·그룹·역할과 접근 규칙을 정하고, 최종 권한을 확인합니다.</dd></div>
|
||||
<div><dt>보호·검증</dt><dd>보호 정책을 연결하고 토큰으로 실제 DB 조회 결과를 검증합니다.</dd></div>
|
||||
<div><dt>연동 도구</dt><dd>조회 대상, ORDS 연동, 지식 검색과 MCP 연결을 관리합니다.</dd></div>
|
||||
<div><dt>운영</dt><dd>현재 연결과 실행 상태를 확인합니다.</dd></div>
|
||||
<div><dt>관리자</dt><dd>일상 권한 작업과 분리된 시스템 연결, DB 준비, 고급 예외 조건을 관리합니다.</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="product-help-section">
|
||||
<h2>요청마다 권한이 적용되는 흐름</h2>
|
||||
<div class="diagram-canvas">
|
||||
<svg class="product-flow-diagram" viewBox="0 0 1120 270" role="img" aria-labelledby="vpd-flow-title vpd-flow-desc">
|
||||
<title id="vpd-flow-title">VPD 권한 적용 흐름</title>
|
||||
<desc id="vpd-flow-desc">Bearer Token 요청이 공용 실행 계정과 사용자 컨텍스트를 거쳐 권한 테이블에서 계산한 VPD 조건으로 보호 데이터를 조회하는 흐름</desc>
|
||||
<defs><marker id="flow-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z"/></marker></defs>
|
||||
<path class="diagram-link" marker-end="url(#flow-arrow)" d="M190 92 H245"/>
|
||||
<path class="diagram-link" marker-end="url(#flow-arrow)" d="M395 92 H450"/>
|
||||
<path class="diagram-link" marker-end="url(#flow-arrow)" d="M600 92 H655"/>
|
||||
<path class="diagram-link" marker-end="url(#flow-arrow)" d="M805 92 H860"/>
|
||||
<path class="diagram-link diagram-link-secondary" marker-end="url(#flow-arrow)" d="M720 210 V143"/>
|
||||
<g class="diagram-node"><rect x="30" y="48" width="160" height="88" rx="12"/><text x="110" y="81" class="diagram-node-title">요청·토큰</text><text x="110" y="108" class="diagram-node-detail">Bearer Token</text></g>
|
||||
<g class="diagram-node"><rect x="245" y="48" width="150" height="88" rx="12"/><text x="320" y="78" class="diagram-node-title">공용 실행 계정</text><text x="320" y="105" class="diagram-node-detail">CB_ORDS</text><text x="320" y="123" class="diagram-node-note">한 개의 DB 연결</text></g>
|
||||
<g class="diagram-node"><rect x="450" y="48" width="150" height="88" rx="12"/><text x="525" y="78" class="diagram-node-title">사용자 컨텍스트</text><text x="525" y="105" class="diagram-node-detail">CB_AGENT_CTX</text><text x="525" y="123" class="diagram-node-note">사용자별로 설정</text></g>
|
||||
<g class="diagram-node diagram-node-accent"><rect x="655" y="48" width="150" height="88" rx="12"/><text x="730" y="78" class="diagram-node-title">VPD 조건 계산</text><text x="730" y="105" class="diagram-node-detail">ALLOW · DENY · 행 · 열</text></g>
|
||||
<g class="diagram-node diagram-node-accent"><rect x="860" y="48" width="210" height="88" rx="12"/><text x="965" y="78" class="diagram-node-title">보호 데이터 조회</text><text x="965" y="105" class="diagram-node-detail">허용된 행·컬럼만 반환</text></g>
|
||||
<g class="diagram-node diagram-node-data"><rect x="570" y="180" width="300" height="62" rx="12"/><text x="720" y="207" class="diagram-node-title">사용자 · 역할 · 권한 테이블</text><text x="720" y="229" class="diagram-node-detail">요청 시 동적으로 조회</text></g>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="product-help-section">
|
||||
<h2>권한 데이터 모델</h2>
|
||||
<div class="diagram-canvas">
|
||||
<svg class="permission-erd-diagram" viewBox="0 0 1180 355" role="img" aria-labelledby="erd-title erd-desc">
|
||||
<title id="erd-title">VPD 권한 ERD</title>
|
||||
<desc id="erd-desc">사용자와 그룹에서 역할을 얻고 역할에서 권한과 권한 규칙을 거쳐 보호 대상으로 연결되는 데이터 모델</desc>
|
||||
<defs><marker id="erd-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z"/></marker></defs>
|
||||
<path class="diagram-link" marker-end="url(#erd-arrow)" d="M175 90 H230"/><path class="diagram-link" marker-end="url(#erd-arrow)" d="M390 90 H445"/><path class="diagram-link" marker-end="url(#erd-arrow)" d="M605 90 H660"/><path class="diagram-link" marker-end="url(#erd-arrow)" d="M820 90 H875"/>
|
||||
<path class="diagram-link diagram-link-secondary" marker-end="url(#erd-arrow)" d="M175 245 H230"/><path class="diagram-link diagram-link-secondary" marker-end="url(#erd-arrow)" d="M390 245 H445"/><path class="diagram-link diagram-link-secondary" marker-end="url(#erd-arrow)" d="M605 245 V144"/>
|
||||
<path class="diagram-link diagram-link-secondary" marker-end="url(#erd-arrow)" d="M100 290 V144"/>
|
||||
<g class="erd-entity"><rect x="25" y="50" width="150" height="80" rx="10"/><text x="100" y="80">CB_APP_USER</text><text x="100" y="105">업무 사용자</text></g>
|
||||
<g class="erd-entity"><rect x="230" y="50" width="160" height="80" rx="10"/><text x="310" y="80">CB_USER_ROLE</text><text x="310" y="105">직접 역할 연결</text></g>
|
||||
<g class="erd-entity"><rect x="445" y="50" width="160" height="80" rx="10"/><text x="525" y="80">CB_APP_ROLE</text><text x="525" y="105">역할</text></g>
|
||||
<g class="erd-entity"><rect x="660" y="50" width="160" height="80" rx="10"/><text x="740" y="80">CB_PERMISSION</text><text x="740" y="105">객체 접근</text></g>
|
||||
<g class="erd-entity"><rect x="875" y="50" width="160" height="80" rx="10"/><text x="955" y="80">CB_PERMISSION_RULE</text><text x="955" y="105">행·열 조건</text></g>
|
||||
<g class="erd-entity erd-entity-accent"><rect x="1045" y="50" width="115" height="80" rx="10"/><text x="1102" y="80">보호 대상</text><text x="1102" y="105">TABLE / VIEW</text></g>
|
||||
<g class="erd-entity"><rect x="25" y="205" width="150" height="80" rx="10"/><text x="100" y="235">CB_USER_GROUP</text><text x="100" y="260">그룹 소속</text></g>
|
||||
<g class="erd-entity"><rect x="230" y="205" width="160" height="80" rx="10"/><text x="310" y="235">CB_GROUP</text><text x="310" y="260">사용자 그룹</text></g>
|
||||
<g class="erd-entity"><rect x="445" y="205" width="160" height="80" rx="10"/><text x="525" y="235">CB_GROUP_ROLE</text><text x="525" y="260">그룹 역할 연결</text></g>
|
||||
<g class="erd-entity erd-entity-data"><rect x="745" y="205" width="215" height="80" rx="10"/><text x="852" y="235">CB_AGENT_BEARER_KEY</text><text x="852" y="260">토큰 → 사용자 식별</text></g>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="product-help-section product-help-sql">
|
||||
<h2>실행 SQL은 어디에서 확인하나요?</h2>
|
||||
<p><strong>실행 요청 SQL</strong>은 현재 사용자 컨텍스트와 VPD 조건을 결합한 읽기용 재현 SQL입니다. 실제 DB cursor가 기록한 원문 SQL과 Predicate Information은 <code>V$SQL</code>·<code>DBMS_XPLAN</code> 권한이 있을 때 접근 검증 결과에서 함께 표시됩니다. Oracle은 VPD가 내부적으로 붙인 최종 rewrite 문자열 자체를 별도 SQL 텍스트로 보관하지 않으므로, 실제 적용 근거는 실행계획의 Predicate Information으로 확인합니다.</p>
|
||||
</section>
|
||||
</div>
|
||||
</details>
|
||||
</header>
|
||||
|
||||
@@ -25,9 +93,22 @@
|
||||
<div class="mt-2" th:if="${showSupportCommand}"><code>./run.sh backoffice-support</code></div>
|
||||
</div>
|
||||
|
||||
<section class="dashboard-menu-flow" aria-labelledby="menu-flow-title">
|
||||
<div class="dashboard-section-heading">
|
||||
<span class="architecture-kicker">업무 흐름</span>
|
||||
<h2 id="menu-flow-title">설정은 권한 관리에서, 적용 확인은 접근 검증에서</h2>
|
||||
</div>
|
||||
<div class="dashboard-flow-grid">
|
||||
<a class="dashboard-flow-card" href="/permissions"><strong>권한 관리</strong><span>사용자·역할·접근 규칙</span></a>
|
||||
<a class="dashboard-flow-card" href="/vpd-policies"><strong>보호·검증</strong><span>보호 연결·검증 세션·접근 확인</span></a>
|
||||
<a class="dashboard-flow-card" href="/objects"><strong>연동 도구</strong><span>조회 대상·지식 검색·MCP 연동</span></a>
|
||||
<a class="dashboard-flow-card" href="/operation-status"><strong>운영</strong><span>상태와 실행 결과 확인</span></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="dashboard-command-center" aria-label="주요 권한 작업">
|
||||
<div class="dashboard-primary-action">
|
||||
<h2>주요 작업</h2>
|
||||
<h2>바로 시작</h2>
|
||||
<div class="dashboard-action-grid">
|
||||
<a class="dashboard-action" href="/permissions">
|
||||
<span><strong>접근 규칙</strong><small>역할별 객체·행·컬럼 접근을 설정합니다.</small></span>
|
||||
@@ -51,21 +132,8 @@
|
||||
</section>
|
||||
|
||||
<details class="dashboard-details">
|
||||
<summary>상세 현황</summary>
|
||||
<summary>등록 대상 상세</summary>
|
||||
<div class="dashboard-details-body">
|
||||
<section class="macro-micro-grid" aria-label="권한 적용 방식">
|
||||
<div>
|
||||
<span class="architecture-kicker">전체 관점</span>
|
||||
<h2>권한체계가 유일한 기준입니다.</h2>
|
||||
<p>일상적인 변경은 사용자, 그룹, 역할, 권한 규칙에서만 합니다. 같은 규칙을 화면과 DB 필터에 이중으로 작성하지 않습니다.</p>
|
||||
</div>
|
||||
<div>
|
||||
<span class="architecture-kicker">실행 관점</span>
|
||||
<h2>VPD가 요청마다 조건을 계산합니다.</h2>
|
||||
<p>토큰에서 사용자를 찾고 직접 역할과 그룹 상속 역할을 합친 뒤, 객체의 ALLOW/DENY 및 행·열 규칙을 적용합니다. 근거는 결과 확인 단계에서 봅니다.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="dashboard-object-list">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
<button class="rw-menu-trigger" type="button" data-submenu-trigger="access" aria-controls="submenu-access" aria-expanded="false">권한 관리</button>
|
||||
<button class="rw-menu-trigger" type="button" data-submenu-trigger="protection" aria-controls="submenu-protection" aria-expanded="false">보호·검증</button>
|
||||
<button class="rw-menu-trigger" type="button" data-submenu-trigger="integration" aria-controls="submenu-integration" aria-expanded="false">연동 도구</button>
|
||||
<button class="rw-menu-trigger" type="button" data-submenu-trigger="operations" aria-controls="submenu-operations" aria-expanded="false">운영·고급</button>
|
||||
<button class="rw-menu-trigger" type="button" data-submenu-trigger="operations" aria-controls="submenu-operations" aria-expanded="false">운영</button>
|
||||
</div>
|
||||
<div class="rw-nav-utility ms-auto">
|
||||
<button class="rw-admin-trigger" type="button" data-submenu-trigger="admin" aria-controls="submenu-admin" aria-expanded="false">관리자</button>
|
||||
<form method="post" action="/logout">
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
|
||||
<button class="btn btn-sm btn-outline-secondary" type="submit">로그아웃</button>
|
||||
</form>
|
||||
</div>
|
||||
<form method="post" action="/logout" class="ms-auto">
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
|
||||
<button class="btn btn-sm btn-outline-secondary" type="submit">로그아웃</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="rw-submenu-bar" data-submenu-bar hidden>
|
||||
<div class="container">
|
||||
@@ -48,10 +51,13 @@
|
||||
<a class="nav-link" href="/mcp-sse">MCP 서비스</a>
|
||||
<a class="nav-link" href="/mcp-client-demo">연동 점검</a>
|
||||
</div>
|
||||
<div id="submenu-operations" class="rw-submenu" data-submenu-panel="operations" role="navigation" aria-label="운영 및 고급 메뉴" hidden>
|
||||
<div id="submenu-operations" class="rw-submenu" data-submenu-panel="operations" role="navigation" aria-label="운영 메뉴" hidden>
|
||||
<a class="nav-link" href="/operation-status">운영 현황</a>
|
||||
</div>
|
||||
<div id="submenu-admin" class="rw-submenu" data-submenu-panel="admin" role="navigation" aria-label="관리자 메뉴" hidden>
|
||||
<a class="nav-link" href="/vpd-filter-policies">고급 접근 조건</a>
|
||||
<a class="nav-link" href="/settings">시스템 설정</a>
|
||||
<a class="nav-link" href="/settings/database">DB 준비 상태</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -142,18 +142,13 @@
|
||||
th:text="${result.executionEvidenceMessage()}"></div>
|
||||
</section>
|
||||
|
||||
<div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasExecutionEvidence() and result.executionEvidenceMessage() != null}"
|
||||
th:text="${result.executionEvidenceMessage()}">
|
||||
최근 SQL_ID를 찾지 못했습니다.
|
||||
</div>
|
||||
|
||||
<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입니다.' : 'ORDS Handler가 반환한 VPD predicate 또는 같은 토큰 컨텍스트를 기본 조회문에 결합한 설명용 SQL입니다.'}">
|
||||
ORDS Handler가 반환한 VPD predicate 또는 같은 토큰 컨텍스트를 재현해 조회한 predicate를 기본 조회문에 합친 형태입니다.
|
||||
th:text="${vectorSearch ? 'VECTOR_DISTANCE 검색과 현재 사용자의 역할 기반 VPD 조건을 결합한 읽기용 SQL입니다.' : '현재 사용자 컨텍스트에서 계산한 VPD 조건을 ORDS 조회문에 결합한 읽기용 SQL입니다.'}">
|
||||
현재 사용자 컨텍스트에서 계산한 VPD 조건을 ORDS 조회문에 결합한 읽기용 SQL입니다.
|
||||
</p>
|
||||
</div>
|
||||
<span class="badge text-bg-light">DBMS_RLS predicate</span>
|
||||
@@ -193,15 +188,20 @@ ROWNUM <= :row_limit</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은 권한 조건을 읽기 쉽게 재현한 표현입니다. 실제 실행 증적은 SQL_ID와 DBMS_XPLAN 영역에서 확인합니다. 컬럼 마스킹은 별도 Redaction 정책입니다.
|
||||
이 SQL은 현재 요청의 권한 조건을 읽기 쉽게 재현한 표현입니다. 실제 DB cursor 원문과 적용 predicate는 SQL_ID·DBMS_XPLAN 증적에서 확인합니다. 컬럼 마스킹은 별도 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>
|
||||
|
||||
<div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasSqlTrace()}">
|
||||
이 Handler는 SQL trace 정보를 반환하지 않았고 운영 DB에서도 기본 VPD predicate를 조회하지 못했습니다. <a href="/ords-handlers">조회 연동</a>에서 trace가 포함된 소스를 적용하고, <code>CB_AGENT_DOC_VPD_FILTER</code> 실행 권한을 확인하세요.
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<h1>접근 검증</h1>
|
||||
<details class="explanation-details">
|
||||
<summary>도움말</summary>
|
||||
<p>한 사용자의 토큰으로 실제 데이터를 요청해, 설계한 권한이 DB에서 그대로 적용되는지 확인합니다.</p>
|
||||
<p>한 사용자의 토큰으로 실제 데이터를 요청해, 설계한 권한이 DB에서 그대로 적용되는지 확인합니다. 결과의 실행 요청 SQL은 현재 컨텍스트를 결합한 읽기용 표현이며, DB cursor가 기록한 원문 SQL은 <code>V$SQL</code>·<code>DBMS_XPLAN</code> 진단 권한이 있을 때만 함께 표시됩니다.</p>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted small span-2 mb-0">모든 대상에 표준 <code>CB_PERMISSION_SELECT_POLICY</code>와 <code>CB_AGENT_DOC_VPD_FILTER</code>를 적용합니다. 별도 predicate가 필요하면 운영·고급 메뉴의 가이드를 먼저 확인하세요.</p>
|
||||
<p class="text-muted small span-2 mb-0">모든 대상에 표준 <code>CB_PERMISSION_SELECT_POLICY</code>와 <code>CB_AGENT_DOC_VPD_FILTER</code>를 적용합니다. 별도 predicate가 필요하면 관리자 메뉴의 고급 접근 조건 가이드를 먼저 확인하세요.</p>
|
||||
<button class="btn rw-btn-primary" type="submit">선택 스키마에 권한체계 일괄 연결</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
Reference in New Issue
Block a user