fix #557: guide permission-driven VPD flow

This commit is contained in:
devmrko
2026-06-29 07:01:53 +09:00
parent 8dc77f87ab
commit fbe3d4682b
24 changed files with 1351 additions and 489 deletions

View File

@@ -1,96 +1,67 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('ORDS 검증')}"></head>
<head th:replace="~{fragments/layout :: head('권한 결과 확인')}"></head>
<body>
<nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4">
<div class="page-title">
<h1>ORDS 검증</h1>
<p>ORDS가 서빙하는 TABLE/VIEW 호출 결과로 VPD/Redaction 적용 여부를 확인합니다.</p>
<h1>권한 결과 확인</h1>
<p>한 사용자의 토큰으로 실제 데이터를 요청해, 설계한 권한이 DB에서 그대로 적용되는지 확인합니다.</p>
</div>
<section th:replace="~{fragments/layout :: architectureStrip('ords')}"></section>
<section class="content-band guided-check-intro">
<div>
<span class="architecture-kicker">이번 단계에서 확인하는 것</span>
<h2>“이 사용자는 이 데이터에서 무엇을 볼 수 있는가?”</h2>
<p>토큰은 사용자를 찾는 열쇠입니다. 서버가 직접 역할과 그룹 상속 역할을 합치고, VPD가 저장된 행·열 규칙을 적용한 결과를 보여줍니다.</p>
</div>
<a class="btn rw-btn-secondary" href="/tokens">테스트 토큰이 없나요? 먼저 발급하기</a>
</section>
<section class="content-band">
<div class="section-heading">
<h2>ORDS 호출</h2>
<a class="btn btn-sm rw-btn-secondary" href="/mcp-reasoning">MCP Reasoning으로 해석</a>
<div>
<h2>검증할 사용자와 데이터 입력</h2>
<p class="section-subtitle">등록 토큰 목록에서 고르는 대신 발급할 때 복사한 원문 하나만 사용합니다.</p>
</div>
</div>
<form hx-post="/probe" hx-target="#probe-result" hx-swap="innerHTML" class="form-grid token-form">
<form hx-post="/probe" hx-target="#probe-result" hx-swap="innerHTML" class="form-grid probe-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<label>
등록 토큰
<select class="form-select" name="tokenKeyId" data-token-context-select>
<option value="">원문 직접 입력</option>
<option th:each="token : ${tokens}"
th:value="${token.keyId()}"
th:text="${token.displayLabel()}"
th:attr="data-username=${token.username()},
data-prefix=${token.maskedToken()},
data-status=${token.statusLabel()},
data-expires-at=${token.expiresAt()},
data-description=${token.description()},
data-direct-roles=${#strings.listJoin(token.directRoles(), '|')},
data-groups=${#strings.listJoin(token.groups(), '|')},
data-inherited-roles=${#strings.listJoin(token.inheritedRoles(), '|')}"></option>
</select>
<label class="span-2">
1. 발급받은 토큰 원문
<input class="form-control" name="bearerToken" type="password" autocomplete="off"
placeholder="토큰 발급 직후 복사한 값을 붙여 넣으세요" required>
<span class="form-hint">원문은 DB에 저장되지 않습니다. 목록에 보이는 prefix만으로는 검증할 수 없으며, 원문을 잃었다면 새 토큰을 발급해야 합니다.</span>
</label>
<label>
Bearer Token 원문
<input class="form-control" name="bearerToken" type="password" autocomplete="off" required>
<span class="form-hint">등록 토큰을 선택해도 원문은 저장되지 않아 실제 호출 시 필요합니다.</span>
</label>
<aside class="token-context-preview effective-preview span-2" data-token-context-preview>
<div class="section-heading compact-heading">
<h3>선택 토큰 컨텍스트</h3>
<span class="badge text-bg-secondary" data-token-preview="status">미선택</span>
</div>
<dl>
<div>
<dt>사용자</dt>
<dd data-token-preview="username">원문 직접 입력</dd>
</div>
<div>
<dt>Prefix</dt>
<dd><code data-token-preview="prefix">-</code></dd>
</div>
<div>
<dt>만료</dt>
<dd data-token-preview="expiresAt">-</dd>
</div>
<div>
<dt>직접 역할</dt>
<dd data-token-preview="directRoles">-</dd>
</div>
<div>
<dt>그룹</dt>
<dd data-token-preview="groups">-</dd>
</div>
<div>
<dt>그룹 상속 역할</dt>
<dd data-token-preview="inheritedRoles">-</dd>
</div>
</dl>
<p class="form-hint" data-token-preview="description">토큰을 선택하면 등록된 사용자/역할 컨텍스트를 먼저 확인할 수 있습니다.</p>
</aside>
<label>
ORDS 트랙
2. 확인할 데이터
<select class="form-select" name="objectId" required>
<option th:each="object : ${objects}"
th:value="${object.objectId()}"
th:text="${object.displayName() + ' / ' + object.ordsPath()}"></option>
th:text="${object.displayName() + (defaultObjectKeys.contains(object.displayName()) ? ' · 권한체계 자동 (권장)' : ' · 별도 Filter (고급 점검 필요)')}"></option>
</select>
<span class="form-hint">이 객체에 저장한 권한 규칙과 실제 반환 행을 비교합니다.</span>
</label>
<label>
Limit
최대 확인 행 수
<input class="form-control" name="limit" type="number" min="1" max="500" value="50">
<span class="form-hint">권한 판정에는 영향을 주지 않고 화면에 가져올 최대 행만 제한합니다.</span>
</label>
<button class="btn rw-btn-primary" type="submit">호출</button>
<button class="btn rw-btn-primary probe-submit" type="submit">3. 권한 결과 확인</button>
</form>
</section>
<section id="probe-result" class="content-band">
<div class="text-muted">검증 결과가 여기에 표시됩니다.</div>
<section id="probe-result" class="content-band" aria-live="polite">
<div class="empty-result-guide">
<strong>결과는 세 가지 순서로 설명합니다.</strong>
<ol>
<li>토큰이 어떤 사용자와 역할로 해석됐는지</li>
<li>VPD 적용 후 실제로 몇 행이 보였는지</li>
<li>예상과 다를 때 어디를 확인해야 하는지</li>
</ol>
</div>
</section>
</main>
</body>