ui: adopt product language for DDS console

This commit is contained in:
devmrko
2026-07-01 07:57:52 +09:00
parent 5ab6c53eaf
commit 12b7e00cef
9 changed files with 92 additions and 92 deletions

View File

@@ -1,17 +1,17 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('DDS 권한 백오피스')}"></head>
<head th:replace="~{fragments/layout :: head('DDS 권한 운영')}"></head>
<body>
<nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4">
<header class="page-title guided-hero">
<span class="architecture-kicker">DEEP DATA SECURITY · INDEPENDENT TRACK</span>
<h1>DDS 권한 관리</h1>
<p class="context-summary">권한 설계 · 보호 연결 · 검색</p>
<span class="architecture-kicker">DEEP DATA SECURITY</span>
<h1>DDS 권한 운영</h1>
<p class="context-summary">권한 설계 · 보호 상태 · 권한 게시 · 권한</p>
<details class="explanation-details">
<summary>인스턴스가 하는 일</summary>
<p>8083 DDS 인스턴스는 8082 VPD 인스턴스와 별도로 실행됩니다. 두 화면은 사용자·그룹·역할·권한 규칙을 같은 관리 흐름으로 보여주며, DDS는 보호 객체별 DATA GRANT로 그 규칙을 집행합니다.</p>
<p class="mb-0">일상적인 권한 변경은 권한 관리에서 하고, DDS 보호 객체와 실제 결과는 이 인스턴스에서 확인합니다.</p>
<summary>공간의 역할 보기</summary>
<p>사용자·그룹·역할·접근 규칙을 기준으로 지식자료의 접근 범위를 운영합니다. DDS는 보호 객체별 DATA GRANT로 이 기준을 집행합니다.</p>
<p class="mb-0">일상적인 권한 변경은 권한 관리에서 하고, 이 공간에서는 보호 상태와 실제 접근 결과를 확인합니다.</p>
</details>
</header>
@@ -19,10 +19,10 @@
<section class="journey-grid" aria-label="DDS 권한 적용 네 단계">
<a class="journey-card journey-card-compact" href="/permissions"><span class="journey-number">1</span><div><h2>권한 설계</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds-protection"><span class="journey-number">2</span><div><h2>DDS 보호 상태</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds-provision"><span class="journey-number">3</span><div><h2>DDS 권한 연결</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/vector-knowledge"><span class="journey-number">4</span><div><h2>권한 기반 검색</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds"><span class="journey-number">5</span><div><h2>보호 객체 확인</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds-protection"><span class="journey-number">2</span><div><h2>보호 상태</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds-provision"><span class="journey-number">3</span><div><h2>권한 게시</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/vector-knowledge"><span class="journey-number">4</span><div><h2>권한 검색</h2><strong>열기 →</strong></div></a>
<a class="journey-card journey-card-compact" href="/dds"><span class="journey-number">5</span><div><h2>보호 범위</h2><strong>열기 →</strong></div></a>
</section>
<section class="content-band compact-context">
@@ -38,17 +38,17 @@
<section class="summary-grid" aria-label="현재 DDS 관리 현황">
<a class="summary-tile" href="/permissions"><span class="label">역할</span><strong th:text="${#lists.size(roles)}">0</strong></a>
<a class="summary-tile" href="/permissions"><span class="label">권한 규칙</span><strong th:text="${#lists.size(permissions)}">0</strong></a>
<a class="summary-tile" href="/dds"><span class="label">보조 직접 비교 사용자</span><strong th:text="${configuredUserCount}">0</strong></a>
<a class="summary-tile" href="/dds"><span class="label">직접 접근 검증 프로필</span><strong th:text="${configuredUserCount}">0</strong></a>
</section>
<section class="content-band">
<div class="section-heading"><div><h2>현재 DDS 검증 대상</h2></div></div>
<div class="section-heading"><div><h2>현재 보호 대상</h2></div></div>
<div class="table-responsive"><table class="table table-sm align-middle"><thead><tr><th>대상</th><th>보호 방식</th><th>검증</th></tr></thead><tbody>
<tr><td><code th:text="${vectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code></td><td>객체별 분류값 DATA GRANT + 토큰 Context + VECTOR_DISTANCE</td><td><a href="/vector-knowledge">지식 검색</a></td></tr>
</tbody></table></div>
<details class="explanation-details mt-3">
<summary>검증 대상 설명 보기</summary>
<p class="mb-0">시나리오는 업무 분류값이 붙은 벡터 지식자료 검색입니다. 원본 테이블은 DDS 권한 동작을 비교하는 보조 기술 검증에만 사용합니다.</p>
<summary>보호 대상 기술 상세 보기</summary>
<p class="mb-0">대상은 업무 분류값이 붙은 벡터 지식자료 검색입니다. 원본 테이블은 직접 접근 비교를 위한 고급 검증에만 사용합니다.</p>
</details>
</section>
</main>

View File

@@ -1,15 +1,15 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('DDS 권한 반영')}"></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">
<span class="architecture-kicker">DDS · PUBLISH</span>
<h1>애플리케이션 권한을 DDS에 반영</h1>
<p class="context-summary">사용자·그룹·역할·테이블 권한을 계산한 뒤 DDS DATA GRANT로 게시합니다.</p>
<h1>권한 변경을 보호 규칙에 게시</h1>
<p class="context-summary">사용자·그룹·역할의 접근 규칙을 검토한 뒤 DDS 보호 규칙에 반영합니다.</p>
<details class="explanation-details">
<summary>VPD와 DDS의 반영 차이 보기</summary>
<summary>게시 방식 기술 상세 보기</summary>
<p>이 화면은 직접 DDS END USER 비교 경로와 일반 객체의 선언형 Grant를 미리 보고 게시하는 단계입니다. 토큰 기반 벡터 경로는 별도 객체별 Data Grant predicate가 요청 시 공통 권한 테이블을 다시 평가합니다.</p>
<p class="mb-0">그룹 자체를 DDS 그룹으로 복사하지는 않습니다. 애플리케이션 그룹의 역할 상속을 계산해 직접 비교용 DATA ROLE Grant에 반영하고, 토큰 경로에서는 같은 effective role 계산을 predicate 함수가 사용합니다.</p>
</details>
@@ -19,7 +19,7 @@
<section class="content-band">
<details class="explanation-details">
<summary>VPD 권한과 DDS 객체의 매핑 기준</summary>
<summary>권한 규칙과 보호 객체의 매핑 기준</summary>
<div class="table-responsive mt-3">
<table class="table table-sm align-middle mb-0">
<thead><tr><th>공통 권한 관리</th><th>DDS에서의 표현</th><th>반영 시점</th></tr></thead>
@@ -44,22 +44,22 @@
<div class="section-heading">
<div>
<span class="architecture-kicker">1 · COMPILE</span>
<h2>현재 유효 권한 미리보기</h2>
<p class="section-subtitle">직접 부여된 역할과 활성 그룹에서 상속된 역할을 합쳐 DDS Grant를 만듭니다.</p>
<h2>게시 전 변경 내용</h2>
<p class="section-subtitle">직접 부여된 역할과 활성 그룹 역할을 합쳐 보호 규칙을 만듭니다.</p>
</div>
<span class="badge text-bg-secondary" th:text="${plan.mappedUserCount() + '명 매핑'}">0명 매핑</span>
</div>
<div class="alert alert-info" th:if="${plan.publishableCount() == 0}">
게시할 수 있는 ALLOW 권한이 없습니다. 보호 객체 매핑과 권한 규칙을 먼저 확인하세요.
게시할 접근 허용 규칙이 없습니다. 보호 객체 매핑과 접근 규칙을 먼저 확인하세요.
</div>
<form method="post" action="/dds-provision/publish" class="mb-3" th:if="${plan.publishableCount() > 0}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button class="btn rw-btn-primary" type="submit"
onclick="return confirm('현재 미리보기대로 DDS DATA GRANT를 교체하고 권한 없는 Grant를 회수할까요?')">미리보기대로 DDS에 게시</button>
onclick="return confirm('현재 미리보기대로 보호 규칙을 게시하고 더 이상 허용되지 않는 규칙을 회수할까요?')">변경 내용 게시</button>
</form>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead><tr><th>애플리케이션 주체</th><th>애플리케이션 객체</th><th>DDS 주체/객체</th><th>판정</th><th>predicate</th><th>컬럼 제외</th></tr></thead>
<thead><tr><th>접근 주체</th><th>관리 대상</th><th>보호 대상</th><th>판정</th><th>접근 조건</th><th>제한 컬럼</th></tr></thead>
<tbody>
<tr th:each="grant : ${plan.grants()}">
<td><strong th:text="${grant.userLabel()}">사용자</strong><small class="d-block text-muted" th:text="${'userId=' + grant.applicationUserId() + ' · ' + grant.dataRole()}">mapping</small></td>
@@ -73,14 +73,14 @@
<td><code th:text="${grant.predicate() ?: '-'}">predicate</code></td>
<td th:text="${grant.excludedColumns() ?: '없음'}">없음</td>
</tr>
<tr th:if="${#lists.isEmpty(plan.grants())}"><td colspan="6" class="text-muted">매핑된 유효 권한에서 DDS Grant 대상을 찾지 못했습니다.</td></tr>
<tr th:if="${#lists.isEmpty(plan.grants())}"><td colspan="6" class="text-muted">현재 접근 규칙에서 게시할 보호 대상을 찾지 못했습니다.</td></tr>
</tbody>
</table>
</div>
</section>
<section class="content-band" th:if="${plan.hasWarnings()}">
<div class="section-heading"><div><h2>반영 전 확인할 항목</h2><p class="section-subtitle">지원되지 않는 객체나 매핑 누락은 게시하지 않고 경고로 남깁니다. DDS 객체 매핑이 없는 기존 Grant도 이 화면에서는 건드리지 않습니다.</p></div></div>
<div class="section-heading"><div><h2>게시 전 확인할 항목</h2><p class="section-subtitle">지원되지 않는 객체나 매핑 누락은 게시하지 않고 경고로 남깁니다.</p></div></div>
<ul>
<li th:each="warning : ${plan.warnings()}" th:text="${warning}">경고</li>
</ul>
@@ -88,7 +88,7 @@
<section class="content-band" th:if="${plan.publishableCount() > 0}">
<details class="explanation-details">
<summary>게시되는 SQL 보기</summary>
<summary>게시 SQL 기술 상세 보기</summary>
<div th:each="grant : ${plan.grants()}" th:if="${grant.publishable()}" class="mt-3">
<strong th:text="${grant.grantName()}">GRANT</strong>
<pre class="code-block" th:text="${grant.sql()}">CREATE OR REPLACE DATA GRANT ...</pre>

View File

@@ -1,16 +1,16 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('DDS 지식 권한 구조')}"></head>
<head th:replace="~{fragments/layout :: head('보호 범위')}"></head>
<body>
<nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4">
<header class="hero">
<span class="eyebrow">DEEP DATA SECURITY · KNOWLEDGE POLICY</span>
<h1>업무 사용자의 지식 접근 범위를 DDS로 제한합니다.</h1>
<p class="lead">이 화면의 보호 대상은 PG나 MY 원본이 아닙니다. 벡터화된 지식 청크를 담은 하나의 보호 VIEW와 그 안의 업무 분류값을 대상으로 합니다.</p>
<h1>지식자료 보호 범위</h1>
<p class="lead">벡터화된 지식자료와 접근 분류값을 기준으로 접근 범위를 관리합니다.</p>
<details class="explanation">
<summary>세일즈 시나리오 설명</summary>
<summary>접근 기준 예시 보기</summary>
<p>세일즈 사용자의 Bearer 토큰은 공통 권한 테이블의 <code>agent_sales</code>로 매핑됩니다. 역할에 <code>ALLOW TAG SALES</code>가 있으면 보호 VIEW의 <code>TECH_TAG</code> 값에 SALES가 포함된 청크만 DDS Data Grant를 통과합니다.</p>
<p class="mb-0">문서가 벡터로 검색된다는 점은 검색 방식이고, 어떤 사용자가 볼 수 있는지는 청크에 저장된 분류값과 권한 규칙이 결정합니다.</p>
</details>
@@ -25,11 +25,11 @@
</article>
<article class="flow-card">
<span class="flow-number">2</span>
<div><span class="eyebrow">POLICY</span><h2>객체별 DATA GRANT</h2><p>보호 VIEW의 조건과 컬럼 범위를 선언합니다.</p></div>
<div><span class="eyebrow">POLICY</span><h2>보호 규칙</h2><p>보호 VIEW의 접근 조건과 컬럼 범위를 선언합니다.</p></div>
</article>
<article class="flow-card">
<span class="flow-number">3</span>
<div><span class="flow-number result-number">3</span><span class="eyebrow">VALUE</span><h2>TECH_TAG </h2><p>SALES가 포함된 청크만 남깁니다.</p></div>
<div><span class="flow-number result-number">3</span><span class="eyebrow">VALUE</span><h2>접근 분류</h2><p>허용된 분류값이 붙은 자료만 남깁니다.</p></div>
</article>
</section>
@@ -37,7 +37,7 @@
<div class="panel-heading">
<div>
<span class="eyebrow">BUSINESS SCENARIO</span>
<h2>세일즈 사용자의 지식 검색</h2>
<h2>업무 사용자 검색 예시</h2>
<p>권한 관리에서 정의한 사용자·역할·규칙이 벡터 지식 검색 결과에 어떻게 적용되는지 확인합니다.</p>
</div>
<a class="btn btn-primary" href="/vector-knowledge">토큰 지식 검색 열기</a>
@@ -45,7 +45,7 @@
<div class="matrix-grid">
<div class="matrix-card"><strong>업무 사용자</strong><span>agent_sales</span><small>SALES 부서 · Bearer 토큰으로 식별</small></div>
<div class="matrix-card"><strong>역할</strong><span>SALES_KNOWLEDGE_ROLE</span><small>세일즈 지식 접근 역할</small></div>
<div class="matrix-card"><strong>규칙</strong><span>ALLOW TAG SALES</span><small>TECH_TAG 컬럼 값에 SALES 포함</small></div>
<div class="matrix-card"><strong>접근 규칙</strong><span>SALES 허용</span><small>접근 분류값에 SALES 포함</small></div>
<div class="matrix-card"><strong>결과</strong><span>청크 28004</span><small>세일즈 파이프라인 운영 가이드만 반환</small></div>
</div>
</section>
@@ -54,7 +54,7 @@
<div class="panel-heading">
<div>
<span class="eyebrow">PROTECTED OBJECT</span>
<h2>테이블·컬럼·값 단위의 보호 범위</h2>
<h2>보호 범위</h2>
<p>지식 검색 VIEW와 그 안의 분류 컬럼 값을 기준으로 판단합니다.</p>
</div>
</div>
@@ -62,8 +62,8 @@
<table class="table align-middle">
<thead><tr><th>구성</th><th>현재 예제</th><th>권한 판단</th></tr></thead>
<tbody>
<tr><td>보호 객체</td><td><code th:text="${vectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code></td><td>객체별 DATA GRANT</td></tr>
<tr><td>행 판단 컬럼</td><td><code>TECH_TAG</code></td><td>값에 SALES 포함 여부</td></tr>
<tr><td>보호 객체</td><td><code th:text="${vectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code></td><td>객체별 보호 규칙</td></tr>
<tr><td>접근 분류 컬럼</td><td><code>TECH_TAG</code></td><td>허용된 분류값 포함 여부</td></tr>
<tr><td>업무 규칙</td><td><code>ALLOW TAG SALES</code></td><td>공통 CB_PERMISSION_RULE에서 조회</td></tr>
<tr><td>검색 컬럼</td><td><code>EMBEDDING</code></td><td>거리 계산에 사용하며 응답에는 반환하지 않음</td></tr>
</tbody>
@@ -75,14 +75,14 @@
<div class="panel-heading">
<div>
<span class="eyebrow">IMPLEMENTATION</span>
<h2>추가·수정할 때의 기준</h2>
<h2>보호 범위 변경 기준</h2>
</div>
</div>
<ol>
<li>문서 청킹·임베딩 시 청크에 업무 분류값을 저장합니다. 예: <code>SALES</code>, <code>HR</code>, <code>FINANCE</code>.</li>
<li>권한 관리에서 역할에 허용할 분류값을 <code>TAG</code> 규칙으로 등록합니다.</li>
<li>보호 VIEW별 Data Grant predicate가 해당 컬럼 값을 공통 권한체계와 비교합니다.</li>
<li>세일즈 토큰, 다른 부서 토큰, invalid token으로 결과를 회귀 검증합니다.</li>
<li>허용·차단·만료된 토큰으로 결과를 확인합니다.</li>
</ol>
<p class="mb-0"><a href="/permissions">권한 관리</a>에서 규칙을 확인하고, <a href="/vector-knowledge">지식 검색</a>에서 실제 반환 청크를 확인하세요.</p>
</section>

View File

@@ -4,7 +4,7 @@
<div th:fragment="directComparison" class="table-responsive">
<form hx-post="/dds-protection/verify/direct" hx-target="#direct-comparison-result" hx-swap="innerHTML" class="mb-3">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button class="btn btn-sm rw-btn-primary" type="submit">직접 비교 fixture 검증 실행</button>
<button class="btn btn-sm rw-btn-primary" type="submit">직접 접근 기준 검증 실행</button>
</form>
<table class="table table-sm align-middle protection-direct-table">
<thead><tr><th>검증 프로필</th><th>보호 상태</th><th>권한 반영</th><th>검색 검증</th><th>조치</th></tr></thead>

View File

@@ -30,7 +30,7 @@
</div>
<div class="empty-result-guide" th:if="${searchResult.success() and !searchResult.hasRows()}">DATA GRANT를 통과한 검색 단위가 없습니다.</div>
<details class="technical-details mt-3">
<summary>DDS 연결 정보 보기</summary>
<summary>기술 정보 보기</summary>
<p class="mb-0">SESSION_USER: <code th:text="${searchResult.sessionUser() ?: '-'}">-</code> · ORA_END_USER_CONTEXT: <code th:text="${searchResult.endUser() ?: '-'}">-</code></p>
</details>
</div>

View File

@@ -3,7 +3,7 @@
<head th:fragment="head(title)">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title th:text="${title}">DDS 백오피스</title>
<title th:text="${title}">DDS 권한 운영</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/app.css" rel="stylesheet">
<link th:if="${backofficeTrack == 'DDS'}" href="/css/dds.css" rel="stylesheet">
@@ -14,8 +14,8 @@
<body>
<nav th:fragment="nav" class="navbar rw-nav navbar-expand-lg">
<div class="container">
<a class="navbar-brand" href="/" th:text="${backofficeTrack == 'DDS' ? 'DDS Backoffice' : 'VPD Backoffice'}">DDS Backoffice</a>
<span class="badge rounded-pill text-bg-primary ms-2" th:if="${backofficeTrack == 'DDS'}">독립 DDS 트랙 · 8083</span>
<a class="navbar-brand" href="/" th:text="${backofficeTrack == 'DDS' ? 'DDS 권한 운영' : 'VPD Backoffice'}">DDS 권한 운영</a>
<span class="badge rounded-pill text-bg-primary ms-2" th:if="${backofficeTrack == 'DDS'}">Deep Data Security</span>
<div class="rw-menu">
<div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false">1. 권한 설계</button>
@@ -29,22 +29,22 @@
</div>
<div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false"
th:text="${backofficeTrack == 'DDS' ? '2. DDS 보호·검증' : '2. 보호·검증'}">2. 보호·검증</button>
th:text="${backofficeTrack == 'DDS' ? '2. 보호 운영' : '2. 보호·검증'}">2. 보호·검증</button>
<div class="rw-menu-panel">
<a class="nav-link" th:href="${backofficeTrack == 'DDS' ? '/dds-protection' : '/vpd-policies'}"
th:text="${backofficeTrack == 'DDS' ? 'DDS 보호 연결' : 'DB 보호 연결'}">DB 보호 연결</a>
<a class="nav-link" href="/dds-provision" th:if="${backofficeTrack == 'DDS'}">DDS 권한 반영</a>
th:text="${backofficeTrack == 'DDS' ? '보호 상태' : 'DB 보호 연결'}">DB 보호 연결</a>
<a class="nav-link" href="/dds-provision" th:if="${backofficeTrack == 'DDS'}">권한 게시</a>
<a class="nav-link" href="/tokens" th:if="${backofficeTrack != 'DDS'}">검증 세션 발급</a>
<a class="nav-link" href="/probe" th:if="${backofficeTrack != 'DDS'}">권한 결과 확인</a>
<a class="nav-link" href="/dds">DDS 직접 조회</a>
<a class="nav-link" href="/dds">보호 범위</a>
</div>
</div>
<div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false">연동 도구</button>
<button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '지식 접근' : '연동 도구'}">연동 도구</button>
<div class="rw-menu-panel">
<a class="nav-link" href="/objects" th:if="${backofficeTrack != 'DDS'}">ORDS 조회 대상</a>
<a class="nav-link" href="/ords-handlers" th:if="${backofficeTrack != 'DDS'}">ORDS 핸들러</a>
<a class="nav-link" href="/vector-knowledge">지식 검색 관리</a>
<a class="nav-link" href="/vector-knowledge" th:text="${backofficeTrack == 'DDS' ? '지식자료와 접근 권한' : '지식 검색 관리'}">지식 검색 관리</a>
<a class="nav-link" href="/mcp-chatbot" th:if="${backofficeTrack != 'DDS'}">Chatbot</a>
<a class="nav-link" href="/mcp-reasoning" th:if="${backofficeTrack != 'DDS'}">Reasoning</a>
<a class="nav-link" href="/mcp-sse" th:if="${backofficeTrack != 'DDS'}">SSE 서비스</a>
@@ -52,7 +52,7 @@
</div>
</div>
<div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false">운영·고급</button>
<button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '고급 검증' : '운영·고급'}">운영·고급</button>
<div class="rw-menu-panel">
<a class="nav-link" href="/operation-status" th:if="${backofficeTrack != 'DDS'}">운영 상태</a>
<a class="nav-link" href="/vpd-filter-policies" th:if="${backofficeTrack != 'DDS'}">별도 Filter 관리</a>
@@ -69,7 +69,7 @@
<div th:fragment="trackNotice" th:if="${backofficeTrack == 'DDS'}" class="container pt-3">
<div class="alert alert-info mb-0">
<strong>DDS 독립 데모</strong> · VPD와 같은 관리 흐름을 사용하며, 실제 보호는 객체별 <code>DATA GRANT</code>가 담당합니다. 토큰 경로는 <code>Bearer → CB_AGENT_CTX → predicate</code>, 직접 비교는 <code>END USER → DATA ROLE → DATA GRANT</code>니다.
<strong>DDS 보호 운영</strong> · 실제 보호는 객체별 <code>DATA GRANT</code>가 담당합니다. 업무 사용자 검색은 <code>Bearer → CB_AGENT_CTX → 접근 조건</code>, 직접 접근 비교는 <code>END USER → DATA ROLE → DATA GRANT</code> 경로로 동작합니다.
</div>
</div>
@@ -82,13 +82,13 @@
<div class="architecture-arrow"></div>
<div class="architecture-step" th:classappend="${activeLayer == 'vpd' or activeLayer == 'protection'} ? ' active'">
<span class="architecture-kicker" th:text="${backofficeTrack == 'DDS' ? '2 · ENFORCE' : '2 · ENFORCE'}">2 · ENFORCE</span>
<strong th:text="${backofficeTrack == 'DDS' ? 'DDS 보호 연결' : 'DB 보호 연결'}">DB 보호 연결</strong>
<strong th:text="${backofficeTrack == 'DDS' ? '보호 상태' : 'DB 보호 연결'}">DB 보호 연결</strong>
<p th:if="${backofficeTrack != 'DDS'}" th:text="${'VPD가 저장된 권한체계를 매번 읽어 DB에서 행을 자동 제한합니다.'}">DB 보호 정책이 권한을 적용합니다.</p>
</div>
<div class="architecture-arrow"></div>
<div class="architecture-step" th:classappend="${activeLayer == 'token'} ? ' active'">
<span class="architecture-kicker">3 · IDENTITY</span>
<strong th:text="${backofficeTrack == 'DDS' ? 'END USER 컨텍스트' : '검증 세션'}">검증 세션</strong>
<strong th:text="${backofficeTrack == 'DDS' ? '업무 사용자 식별' : '검증 세션'}">검증 세션</strong>
<p th:if="${backofficeTrack != 'DDS'}">확인할 사용자를 나타내는 일회성 토큰을 준비합니다.</p>
</div>
<div class="architecture-arrow"></div>
@@ -100,7 +100,7 @@
</section>
<details th:if="${backofficeTrack == 'DDS'}" class="explanation-details architecture-explanation">
<summary>DDS 적용 흐름 설명 보기</summary>
<summary>보호 방식 기술 상세 보기</summary>
<ol>
<li>권한 관리에서 사용자·그룹·역할과 TAG 규칙을 정의합니다.</li>
<li>DDS 보호 VIEW/TABLE에 객체별 DATA GRANT를 연결합니다.</li>

View File

@@ -3,15 +3,15 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DDS Permission Console 로그인</title>
<title>DDS 권한 운영 콘솔 로그인</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/dds.css" rel="stylesheet">
</head>
<body class="login-page">
<main class="login-card">
<span class="eyebrow">DEEP DATA SECURITY</span>
<h1>DDS Permission Console</h1>
<p>VPD와 분리된 DDS 전용 인스턴스입니다.</p>
<h1>DDS 권한 운영 콘솔</h1>
<p>관리자 전용 보호 정책 운영</p>
<div class="alert alert-danger" th:if="${param.error}">관리자 인증에 실패했습니다.</div>
<div class="alert alert-success" th:if="${param.logout}">로그아웃되었습니다.</div>
<form method="post" action="/login">

View File

@@ -1,17 +1,17 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('DDS 지식 검색 관리')}"></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">
<span class="architecture-kicker">DDS · KNOWLEDGE ACCESS</span>
<h1>지식자료를 등록하고 DDS 권한으로 검색합니다.</h1>
<p class="context-summary">문서 → 청크·임베딩 → 기술 태그 → 권한 규칙 → DDS DATA GRANT → 검색 결과</p>
<h1>지식자료와 접근 권한</h1>
<p class="context-summary">등록 · 접근 규칙 · 업무 사용자 검색 · 고급 검증</p>
<details class="explanation-details">
<summary>이 화면의 큰 흐름 보기</summary>
<p>문서는 검색 가능한 청크로 나뉘고 각 청크에 업무 접근 분류값(<code>TECH_TAG</code>)이 붙습니다. 권한 관리에서 역할별 허용 값을 등록하면 DDS의 DATA GRANT가 해당 값이 붙은 청크만 검색 대상으로 남깁니다.</p>
<p class="mb-0">VPD 화면과 같은 사용자·그룹·역할·권한 규칙을 사용합니다. 아래에는 DDS END USER를 직접 선택하는 비교 경로와, 하나의 DDS 기술 사용자에 Bearer 토큰을 전달해 공통 권한을 평가하는 제품형 경로를 함께 둡니다.</p>
<summary>접근 방식 보기</summary>
<p>문서는 검색 단위로 나뉘고 각 단위에 접근 분류값(<code>TECH_TAG</code>)이 연결됩니다. 역할별 허용 값을 등록하면 보호 규칙이 허용된 분류값만 검색 대상으로 남깁니다.</p>
<p class="mb-0">기본 경로는 Bearer 토큰으로 업무 사용자를 식별합니다. 직접 접근 비교는 고급 검증에서만 사용합니다.</p>
</details>
</div>
@@ -28,7 +28,7 @@
<div>
<span class="architecture-kicker">1 · CONTENT</span>
<h2>지식자료 등록</h2>
<p class="section-subtitle">문서 ID가 같으면 기존 청크를 교체합니다. 저장소는 VPD 트랙과 동일한 지식자료 형식을 사용합니다.</p>
<p class="section-subtitle">문서 ID가 같으면 기존 검색 단위를 교체합니다.</p>
</div>
<span class="badge text-bg-secondary" th:text="${summary == null ? '저장소 확인 필요' : summary.chunkCount() + '개 검색 단위'}">0개 검색 단위</span>
</div>
@@ -36,7 +36,7 @@
<summary>청킹·임베딩 설정 보기</summary>
<ul>
<li>문단을 우선 묶고 길이가 길면 문장·공백 기준으로 청크를 나눕니다.</li>
<li>기본 로컬 임베딩은 외부 API 없이 흐름을 검증하는 개발용 4차원 벡터입니다.</li>
<li>기본 로컬 임베딩은 외부 API 없이 흐름을 확인하는 개발용 4차원 벡터입니다.</li>
<li>운영에서는 등록과 검색에 같은 모델·차원을 사용해야 합니다.</li>
</ul>
</details>
@@ -72,19 +72,19 @@
<div>
<span class="architecture-kicker">2 · POLICY</span>
<h2>권한 규칙은 한 곳에서 관리합니다.</h2>
<p class="section-subtitle">사용자·그룹·역할·TAG 규칙은 VPD와 같은 권한 관리 화면에서 작성합니다.</p>
<p class="section-subtitle">사용자·그룹·역할·접근 분류 규칙을 한 곳에서 관리합니다.</p>
</div>
<div class="d-flex gap-2">
<a class="btn btn-sm rw-btn-secondary" href="/permissions">권한 관리 열기</a>
<a class="btn btn-sm rw-btn-primary" href="/dds-provision">DDS 권한 반영</a>
<a class="btn btn-sm rw-btn-primary" href="/dds-provision">권한 게시</a>
</div>
</div>
<div class="macro-micro-grid">
<div><h3>관리 관점</h3><p>를 들어 세일즈 역할에 <code>ALLOW TAG SALES</code>를 등록하면 SALES 값이 붙은 청크만 허용합니다. <code>DENY TAG</code> 허용 후보에서 제외니다.</p></div>
<div><h3>DDS 실행 관점</h3><p><code th:text="${ddsVectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code>객체별 DATA GRANT predicate가 토큰으로 식별된 업무 사용자와 청크의 <code>TECH_TAG</code> 값을 비교해 이 규칙을 집행합니다.</p></div>
<div><h3>접근 규칙</h3><p>: 세일즈 역할에 SALES 접근 분류값을 허용하면 SALES 자료만 검색됩니다. 거부 규칙은 허용 대상에서 제외니다.</p></div>
<div><h3>보호 방식</h3><p><code th:text="${ddsVectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code>보호 규칙이 업무 사용자와 접근 분류값을 비교합니다.</p></div>
</div>
<details class="explanation-details mt-3">
<summary>VPD와 DDS의 차이 보기</summary>
<summary>보호 방식 기술 상세 보기</summary>
<p>VPD는 요청마다 권한 테이블을 읽어 predicate를 계산합니다. DDS의 직접 비교 경로는 보호 VIEW에 선언한 DATA GRANT와 END USER 매핑을 사용합니다. 토큰 경로에서는 토큰이 공통 사용자 Context를 만들고, 보호 VIEW의 객체별 DATA GRANT predicate가 같은 권한 테이블을 다시 평가합니다.</p>
</details>
</section>
@@ -93,20 +93,20 @@
<div class="section-heading">
<div>
<span class="architecture-kicker">3 · TOKEN CONTEXT</span>
<h2>Bearer 토큰으로 같은 DDS Data Grant 적용</h2>
<p class="section-subtitle">고객별 DDS 계정을 만들지 않고 하나의 기술 사용자 세션에서 토큰으로 업무 사용자를 식별합니다.</p>
<h2>업무 사용자 검색</h2>
<p class="section-subtitle">하나의 기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다.</p>
</div>
<span class="badge text-bg-secondary">객체별 DATA GRANT</span>
</div>
<details class="explanation-details">
<summary>토큰 경계 설명 보기</summary>
<summary>업무 사용자 검색 기술 상세 보기</summary>
<ol>
<li>서버가 설정한 하나의 DDS 기술 사용자로 연결합니다.</li>
<li>Bearer 토큰을 <code>CB_AGENT_CTX_PKG.SET_USER_BY_BEARER</code>에 전달합니다.</li>
<li><code>CB_DDS_VECTOR_SEARCH_DOCUMENTS</code>의 객체별 DATA GRANT predicate가 <code>CB_APP_USER</code>, 역할·그룹, <code>CB_PERMISSION</code>, <code>CB_PERMISSION_RULE</code>을 조회합니다.</li>
<li>ALLOW TAG는 OR로 합치고 DENY TAG는 최종 결과에서 제외합니다.</li>
</ol>
<p class="mb-0">세일즈 예제에서는 <code>agent_sales</code> 역할이 <code>ALLOW TAG SALES</code>를 가지고, 청크의 <code>TECH_TAG</code>에 SALES가 포함된 지식만 남습니다. 토큰은 DATA GRANT 문법의 바인드 파라미터가 아니라 Context를 만드는 입력이며 저장하지 않습니다.</p>
<p class="mb-0">토큰은 보호 규칙의 입력값이 아니라 업무 사용자 Context를 만드는 이며 저장하지 않습니다.</p>
</details>
<form hx-post="/vector-knowledge/token-search" hx-target="#vector-token-search-result" hx-swap="innerHTML" class="form-grid mt-3">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
@@ -124,10 +124,10 @@
<label class="span-2">검색 질문
<textarea class="form-control" name="query" rows="3" placeholder="예: 세일즈 파이프라인 후속 조치 기준" required></textarea>
</label>
<button class="btn rw-btn-primary" type="submit">토큰 권한으로 검색</button>
<button class="btn rw-btn-primary" type="submit">권한 검색</button>
</form>
<section id="vector-token-search-result" class="mt-3" aria-live="polite">
<div class="empty-result-guide">Bearer 토큰과 질문을 입력하면 공통 권한체계를 반영한 DDS Data Grant 결과를 표시합니다.</div>
<div class="empty-result-guide">Bearer 토큰과 질문을 입력하면 허용된 지식자료만 표시합니다.</div>
</section>
</section>
@@ -135,8 +135,8 @@
<div class="section-heading">
<div>
<span class="architecture-kicker">4 · DIRECT CHECK</span>
<h2>DDS END USER 직접 조회 비교</h2>
<p class="section-subtitle">저수준 DDS 검증을 위해 선택한 END USER로 직접 접속해 DATA ROLE/DATA GRANT 결과를 확인합니다.</p>
<h2>고급: 직접 접근 비교</h2>
<p class="section-subtitle">DDS 선언형 보호 규칙 점검용 고급 검증</p>
</div>
<span class="badge text-bg-primary" th:text="${ddsVectorObject}">DDS view</span>
</div>
@@ -151,7 +151,7 @@
</details>
<form hx-post="/vector-knowledge/search" hx-target="#vector-search-result" hx-swap="innerHTML" class="form-grid mt-3">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<label>DDS END USER
<label>직접 접근 검증 프로필
<select class="form-select" name="userKey" required>
<option th:each="user : ${ddsUsers}" th:value="${user.key()}" th:selected="${user.key() == 'both'}"
th:text="${user.label() + ' · ' + user.username()}"></option>
@@ -168,10 +168,10 @@
<label class="span-2">검색 질문
<textarea class="form-control" name="query" rows="3" placeholder="예: 기술 태그로 지식자료 검색 권한을 제한하는 방법" required></textarea>
</label>
<button class="btn rw-btn-primary" type="submit">DDS 권한으로 검색</button>
<button class="btn rw-btn-primary" type="submit">직접 접근 비교</button>
</form>
<section id="vector-search-result" class="mt-3" aria-live="polite">
<div class="empty-result-guide">END USER와 질문을 선택하면 DDS가 허용 검색 단위만 표시니다.</div>
<div class="empty-result-guide">검증 프로필과 질문을 선택하면 허용 검색 단위만 표시니다.</div>
</section>
</section>

View File

@@ -16,7 +16,7 @@
<div class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}"></div>
<form method="post" action="/dds-protection/setup" class="mb-3">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button class="btn btn-sm rw-btn-secondary" type="submit">DDS 검증 이력·fixture 구성 확인</button>
<button class="btn btn-sm rw-btn-secondary" type="submit">검증 기준 자료 구성 확인</button>
</form>
<section class="content-band protection-summary" aria-labelledby="service-path-heading">
@@ -24,7 +24,7 @@
<div>
<span class="architecture-kicker">기본 서비스 경로</span>
<h2 id="service-path-heading">토큰 기반 지식 검색</h2>
<p class="section-subtitle">일상 운영에서는 이 경로만 확인하면 됩니다. 직접 DDS END USER 비교는 아래 고급 검증에 분리했습니다.</p>
<p class="section-subtitle">일상 운영에서는 이 경로만 확인하면 됩니다. 직접 접근 비교는 아래 고급 검증에 분리했습니다.</p>
</div>
<span class="badge text-bg-danger" th:if="${protection.actionRequiredCount() > 0}"
th:text="${protection.actionRequiredCount() + '개 조치 필요'}">조치 필요</span>
@@ -67,7 +67,7 @@
<a class="btn rw-btn-secondary" href="/vector-knowledge">지식 검색 열기</a>
<form method="post" action="/dds-protection/verify/service" class="d-inline">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button class="btn rw-btn-primary" type="submit">결정적 보호 검증 실행</button>
<button class="btn rw-btn-primary" type="submit">보호 기준 검증 실행</button>
</form>
</div>
</footer>
@@ -79,7 +79,7 @@
<div>
<span class="architecture-kicker">권한 변경</span>
<h2>권한을 바꾸려면</h2>
<p class="section-subtitle">권한 규칙을 먼저 수정하고, 직접 비교 경로의 변경은 DDS 권한 반영에서 미리 확인합니다.</p>
<p class="section-subtitle">접근 규칙을 먼저 수정하고, 직접 접근 비교 경로의 변경은 권한 게시에서 미리 확인합니다.</p>
</div>
<div class="d-flex gap-2">
<a class="btn btn-sm rw-btn-secondary" href="/permissions">권한 규칙 열기</a>
@@ -89,16 +89,16 @@
<details class="explanation-details m-0">
<summary>두 집행 경로의 차이 보기</summary>
<div class="protection-path-comparison">
<div><strong>토큰 기반 서비스</strong><span>기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다. 객체별 DATA GRANT가 현재 공통 권한을 평가합니다.</span></div>
<div><strong>DDS END USER 직접 비교</strong><span>DATA ROLE과 게시된 DATA GRANT를 직접 확인하는 고급 진단 경로입니다. 권한 변경 뒤에는 별도 반영이 필요합니다.</span></div>
<div><strong>업무 사용자 검색</strong><span>기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다. 보호 규칙이 현재 접근 권한을 평가합니다.</span></div>
<div><strong>직접 접근 비교</strong><span>게시된 DATA ROLE과 DATA GRANT를 직접 확인하는 고급 진단 경로 · 권한 변경 뒤 별도 게시 필요</span></div>
</div>
</details>
</section>
<section class="content-band">
<details class="advanced-protection" aria-labelledby="direct-check-heading">
<summary id="direct-check-heading">고급 검증: DDS END USER 직접 비교 보기</summary>
<p class="section-subtitle mt-3">아래 항목은 제품 계정 목록이 아니라 DDS 선언형 권한을 비교하기 위한 검증 프로필입니다. 평소 운영에서는 열 필요습니다.</p>
<summary id="direct-check-heading">고급 검증: 직접 접근 비교 보기</summary>
<p class="section-subtitle mt-3">제품 계정 목록이 아 DDS 선언형 보호 규칙 비교용 검증 프로필 · 평소 운영에서는 열 필요 없</p>
<button class="btn btn-sm rw-btn-secondary mt-2" type="button"
hx-get="/dds-protection/direct" hx-target="#direct-comparison-result" hx-swap="innerHTML">
고급 검증 불러오기