ui: reshape DDS navigation as management console

This commit is contained in:
devmrko
2026-07-01 08:04:39 +09:00
parent 12b7e00cef
commit 45621e1b7d
7 changed files with 69 additions and 53 deletions

View File

@@ -67,6 +67,14 @@ body { margin: 0; background: var(--dds-bg); color: var(--dds-ink); font-family:
.journey-card-compact h2 { margin: 0 0 .25rem; } .journey-card-compact h2 { margin: 0 0 .25rem; }
.journey-card-compact strong { font-size: .8rem; } .journey-card-compact strong { font-size: .8rem; }
.compact-context { padding: .75rem; } .compact-context { padding: .75rem; }
.architecture-strip:has(.dds-product-nav) { background: transparent; border: 0; display: block; margin: 0 0 1.25rem; padding: 0; }
.dds-product-nav { background: #fff; border: 1px solid var(--dds-line); border-radius: 10px; display: flex; gap: .25rem; overflow-x: auto; padding: .3rem; }
.dds-product-nav a { border-radius: 7px; color: var(--dds-muted); font-size: .86rem; font-weight: 700; padding: .5rem .75rem; text-decoration: none; white-space: nowrap; }
.dds-product-nav a:hover { background: #f1f4fb; color: var(--dds-ink); }
.dds-product-nav a.active { background: #e8edff; color: var(--dds-accent-dark); }
.page-title .context-summary { font-size: .95rem; max-width: 700px; }
.explanation-details > summary { color: #43516a; font-size: .86rem; }
.explanation-details > summary::marker { color: var(--dds-accent); }
.protection-summary { border-top: 3px solid #3157d5; } .protection-summary { border-top: 3px solid #3157d5; }
.protection-status-card { background: #fbfcff; border: 1px solid #dbe3f4; border-radius: 14px; margin-top: 1rem; padding: 1.25rem; } .protection-status-card { background: #fbfcff; border: 1px solid #dbe3f4; border-radius: 14px; margin-top: 1rem; padding: 1.25rem; }
.protection-status-card.status-danger { background: #fffafa; border-color: #f2c8c8; } .protection-status-card.status-danger { background: #fffafa; border-color: #f2c8c8; }
@@ -104,4 +112,5 @@ body { margin: 0; background: var(--dds-bg); color: var(--dds-ink); font-family:
.panel { padding: 19px; } .panel { padding: 19px; }
.hero { padding-top: 30px; } .hero { padding-top: 30px; }
.protection-status-topline, .protection-status-footer { align-items: flex-start; flex-direction: column; } .protection-status-topline, .protection-status-footer { align-items: flex-start; flex-direction: column; }
.dds-product-nav { border-radius: 8px; }
} }

View File

@@ -7,9 +7,9 @@
<header class="page-title guided-hero"> <header class="page-title guided-hero">
<span class="architecture-kicker">DEEP DATA SECURITY</span> <span class="architecture-kicker">DEEP DATA SECURITY</span>
<h1>DDS 권한 운영</h1> <h1>DDS 권한 운영</h1>
<p class="context-summary">권한 설계 · 보호 상태 · 권한 게시 · 권한</p> <p class="context-summary">권한 관리 · 보호 정책 관리 · 지식자료 관리 · 접근</p>
<details class="explanation-details"> <details class="explanation-details">
<summary>이 공간의 역할 보기</summary> <summary>도움말</summary>
<p>사용자·그룹·역할·접근 규칙을 기준으로 지식자료의 접근 범위를 운영합니다. DDS는 보호 객체별 DATA GRANT로 이 기준을 집행합니다.</p> <p>사용자·그룹·역할·접근 규칙을 기준으로 지식자료의 접근 범위를 운영합니다. DDS는 보호 객체별 DATA GRANT로 이 기준을 집행합니다.</p>
<p class="mb-0">일상적인 권한 변경은 권한 관리에서 하고, 이 공간에서는 보호 상태와 실제 접근 결과를 확인합니다.</p> <p class="mb-0">일상적인 권한 변경은 권한 관리에서 하고, 이 공간에서는 보호 상태와 실제 접근 결과를 확인합니다.</p>
</details> </details>
@@ -18,16 +18,16 @@
<div class="alert alert-warning" th:if="${runtimeError}">DDS 관리 데이터를 불러오지 못했습니다. <span th:text="${runtimeError}"></span></div> <div class="alert alert-warning" th:if="${runtimeError}">DDS 관리 데이터를 불러오지 못했습니다. <span th:text="${runtimeError}"></span></div>
<section class="journey-grid" aria-label="DDS 권한 적용 네 단계"> <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="/permissions"><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-protection"><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="/dds-provision"><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="/vector-knowledge"><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"><div><h2>보호 대상 관리</h2><strong>열기 →</strong></div></a>
</section> </section>
<section class="content-band compact-context"> <section class="content-band compact-context">
<details class="explanation-details m-0"> <details class="explanation-details m-0">
<summary>권한 적용 흐름 설명 보기</summary> <summary>도움말</summary>
<div class="macro-micro-grid mt-3"> <div class="macro-micro-grid mt-3">
<div><span class="architecture-kicker">MACRO · 전체 관점</span><h2>관리 기준은 하나입니다.</h2><p>누가 어떤 지식자료를 볼 수 있는지는 사용자·그룹·역할·권한 규칙에서 설명합니다. VPD와 DDS는 같은 기준을 서로 다른 DB 보호 방식으로 집행합니다.</p></div> <div><span class="architecture-kicker">MACRO · 전체 관점</span><h2>관리 기준은 하나입니다.</h2><p>누가 어떤 지식자료를 볼 수 있는지는 사용자·그룹·역할·권한 규칙에서 설명합니다. VPD와 DDS는 같은 기준을 서로 다른 DB 보호 방식으로 집행합니다.</p></div>
<div><span class="architecture-kicker">MICRO · 실행 관점</span><h2>객체별 DATA GRANT가 최종 집행합니다.</h2><p>토큰 경로는 <code>Bearer → CB_AGENT_CTX → DATA GRANT predicate → 보호 VIEW</code>, 직접 비교는 <code>END USER → DATA ROLE → DATA GRANT → 보호 VIEW</code>입니다. 권한이 없으면 객체가 보이지 않아 <code>ORA-00942</code>가 될 수 있습니다.</p></div> <div><span class="architecture-kicker">MICRO · 실행 관점</span><h2>객체별 DATA GRANT가 최종 집행합니다.</h2><p>토큰 경로는 <code>Bearer → CB_AGENT_CTX → DATA GRANT predicate → 보호 VIEW</code>, 직접 비교는 <code>END USER → DATA ROLE → DATA GRANT → 보호 VIEW</code>입니다. 권한이 없으면 객체가 보이지 않아 <code>ORA-00942</code>가 될 수 있습니다.</p></div>
@@ -47,7 +47,7 @@
<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> <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> </tbody></table></div>
<details class="explanation-details mt-3"> <details class="explanation-details mt-3">
<summary>보호 대상 기술 상세 보기</summary> <summary>기술 상세</summary>
<p class="mb-0">주 대상은 업무 분류값이 붙은 벡터 지식자료 검색입니다. 원본 테이블은 직접 접근 비교를 위한 고급 검증에만 사용합니다.</p> <p class="mb-0">주 대상은 업무 분류값이 붙은 벡터 지식자료 검색입니다. 원본 테이블은 직접 접근 비교를 위한 고급 검증에만 사용합니다.</p>
</details> </details>
</section> </section>

View File

@@ -1,25 +1,25 @@
<!doctype html> <!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"> <html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('권한 게시')}"></head> <head th:replace="~{fragments/layout :: head('정책 적용')}"></head>
<body> <body>
<nav th:replace="~{fragments/layout :: nav}"></nav> <nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4"> <main class="container py-4">
<div class="page-title"> <div class="page-title">
<span class="architecture-kicker">DDS · PUBLISH</span> <span class="architecture-kicker">DDS · PUBLISH</span>
<h1>권한 변경을 보호 규칙에 게시</h1> <h1>보호 정책 적용</h1>
<p class="context-summary">사용자·그룹·역할의 접근 규칙을 검토한 뒤 DDS 보호 규칙에 반영합니다.</p> <p class="context-summary">사용자·그룹·역할의 접근 규칙을 검토한 뒤 DDS 보호 규칙에 반영합니다.</p>
<details class="explanation-details"> <details class="explanation-details">
<summary>게시 방식 기술 상세 보기</summary> <summary>도움말</summary>
<p>이 화면은 직접 DDS END USER 비교 경로와 일반 객체의 선언형 Grant를 미리 보고 게시하는 단계입니다. 토큰 기반 벡터 경로는 별도 객체별 Data Grant predicate가 요청 시 공통 권한 테이블을 다시 평가합니다.</p> <p>이 화면은 직접 DDS END USER 비교 경로와 일반 객체의 선언형 Grant를 미리 보고 게시하는 단계입니다. 토큰 기반 벡터 경로는 별도 객체별 Data Grant predicate가 요청 시 공통 권한 테이블을 다시 평가합니다.</p>
<p class="mb-0">그룹 자체를 DDS 그룹으로 복사하지는 않습니다. 애플리케이션 그룹의 역할 상속을 계산해 직접 비교용 DATA ROLE Grant에 반영하고, 토큰 경로에서는 같은 effective role 계산을 predicate 함수가 사용합니다.</p> <p class="mb-0">그룹 자체를 DDS 그룹으로 복사하지는 않습니다. 애플리케이션 그룹의 역할 상속을 계산해 직접 비교용 DATA ROLE Grant에 반영하고, 토큰 경로에서는 같은 effective role 계산을 predicate 함수가 사용합니다.</p>
</details> </details>
</div> </div>
<section th:replace="~{fragments/layout :: architectureStrip('protection')}"></section> <section th:replace="~{fragments/layout :: architectureStrip('publish')}"></section>
<section class="content-band"> <section class="content-band">
<details class="explanation-details"> <details class="explanation-details">
<summary>권한 규칙과 보호 객체의 매핑 기준</summary> <summary>도움말</summary>
<div class="table-responsive mt-3"> <div class="table-responsive mt-3">
<table class="table table-sm align-middle mb-0"> <table class="table table-sm align-middle mb-0">
<thead><tr><th>공통 권한 관리</th><th>DDS에서의 표현</th><th>반영 시점</th></tr></thead> <thead><tr><th>공통 권한 관리</th><th>DDS에서의 표현</th><th>반영 시점</th></tr></thead>
@@ -43,7 +43,7 @@
<section class="content-band"> <section class="content-band">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<span class="architecture-kicker">1 · COMPILE</span> <span class="architecture-kicker">변경 검토</span>
<h2>게시 전 변경 내용</h2> <h2>게시 전 변경 내용</h2>
<p class="section-subtitle">직접 부여된 역할과 활성 그룹의 역할을 합쳐 보호 규칙을 만듭니다.</p> <p class="section-subtitle">직접 부여된 역할과 활성 그룹의 역할을 합쳐 보호 규칙을 만듭니다.</p>
</div> </div>
@@ -88,7 +88,7 @@
<section class="content-band" th:if="${plan.publishableCount() > 0}"> <section class="content-band" th:if="${plan.publishableCount() > 0}">
<details class="explanation-details"> <details class="explanation-details">
<summary>게시 SQL 기술 상세 보기</summary> <summary>기술 상세</summary>
<div th:each="grant : ${plan.grants()}" th:if="${grant.publishable()}" class="mt-3"> <div th:each="grant : ${plan.grants()}" th:if="${grant.publishable()}" class="mt-3">
<strong th:text="${grant.grantName()}">GRANT</strong> <strong th:text="${grant.grantName()}">GRANT</strong>
<pre class="code-block" th:text="${grant.sql()}">CREATE OR REPLACE DATA GRANT ...</pre> <pre class="code-block" th:text="${grant.sql()}">CREATE OR REPLACE DATA GRANT ...</pre>

View File

@@ -1,35 +1,32 @@
<!doctype html> <!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"> <html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('보호 범위')}"></head> <head th:replace="~{fragments/layout :: head('보호 대상 관리')}"></head>
<body> <body>
<nav th:replace="~{fragments/layout :: nav}"></nav> <nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4"> <main class="container py-4">
<header class="hero"> <header class="hero">
<span class="eyebrow">DEEP DATA SECURITY · KNOWLEDGE POLICY</span> <span class="eyebrow">DEEP DATA SECURITY · KNOWLEDGE POLICY</span>
<h1>지식자료 보호 범위</h1> <h1>보호 대상 관리</h1>
<p class="lead">벡터화된 지식자료와 접근 분류값을 기준으로 접근 범위를 관리합니다.</p> <p class="lead">벡터화된 지식자료와 접근 분류값을 기준으로 접근 범위를 관리합니다.</p>
<details class="explanation"> <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>세일즈 사용자의 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> <p class="mb-0">문서가 벡터로 검색된다는 점은 검색 방식이고, 어떤 사용자가 볼 수 있는지는 청크에 저장된 분류값과 권한 규칙이 결정합니다.</p>
</details> </details>
</header> </header>
<section th:replace="~{fragments/layout :: architectureStrip('token')}"></section> <section th:replace="~{fragments/layout :: architectureStrip('search')}"></section>
<section class="flow-grid" aria-label="DDS 지식 접근 흐름"> <section class="flow-grid" aria-label="DDS 지식 접근 흐름">
<article class="flow-card"> <article class="flow-card">
<span class="flow-number">1</span>
<div><span class="eyebrow">IDENTITY</span><h2>업무 사용자 토큰</h2><p>Bearer 토큰을 세일즈 업무 사용자로 매핑합니다.</p></div> <div><span class="eyebrow">IDENTITY</span><h2>업무 사용자 토큰</h2><p>Bearer 토큰을 세일즈 업무 사용자로 매핑합니다.</p></div>
</article> </article>
<article class="flow-card"> <article class="flow-card">
<span class="flow-number">2</span>
<div><span class="eyebrow">POLICY</span><h2>보호 규칙</h2><p>보호 VIEW의 접근 조건과 컬럼 범위를 선언합니다.</p></div> <div><span class="eyebrow">POLICY</span><h2>보호 규칙</h2><p>보호 VIEW의 접근 조건과 컬럼 범위를 선언합니다.</p></div>
</article> </article>
<article class="flow-card"> <article class="flow-card">
<span class="flow-number">3</span> <div><span class="eyebrow">VALUE</span><h2>접근 분류값</h2><p>허용된 분류값이 붙은 자료만 남깁니다.</p></div>
<div><span class="flow-number result-number">3</span><span class="eyebrow">VALUE</span><h2>접근 분류값</h2><p>허용된 분류값이 붙은 자료만 남깁니다.</p></div>
</article> </article>
</section> </section>

View File

@@ -18,33 +18,34 @@
<span class="badge rounded-pill text-bg-primary ms-2" th:if="${backofficeTrack == 'DDS'}">Deep Data Security</span> <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">
<div class="rw-menu-group"> <div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false">1. 권한 설계</button> <button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '권한 관리' : '1. 권한 설계'}">1. 권한 설계</button>
<div class="rw-menu-panel"> <div class="rw-menu-panel">
<a class="nav-link" href="/users">사용자</a> <a class="nav-link" href="/users" th:text="${backofficeTrack == 'DDS' ? '사용자 관리' : '사용자'}">사용자</a>
<a class="nav-link" href="/groups">그룹</a> <a class="nav-link" href="/groups" th:text="${backofficeTrack == 'DDS' ? '그룹 관리' : '그룹'}">그룹</a>
<a class="nav-link" href="/roles">역할</a> <a class="nav-link" href="/roles" th:text="${backofficeTrack == 'DDS' ? '역할 관리' : '역할'}">역할</a>
<a class="nav-link" href="/permissions">데이터 권한 규칙</a> <a class="nav-link" href="/permissions" th:text="${backofficeTrack == 'DDS' ? '접근 규칙' : '데이터 권한 규칙'}">데이터 권한 규칙</a>
<a class="nav-link" href="/effective-matrix">사용자별 최종 권한</a> <a class="nav-link" href="/effective-matrix" th:text="${backofficeTrack == 'DDS' ? '권한 현황' : '사용자별 최종 권한'}">사용자별 최종 권한</a>
</div> </div>
</div> </div>
<div class="rw-menu-group"> <div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false" <button class="rw-menu-trigger" type="button" aria-expanded="false"
th:text="${backofficeTrack == 'DDS' ? '2. 보호 운영' : '2. 보호·검증'}">2. 보호·검증</button> th:text="${backofficeTrack == 'DDS' ? '보호 정책 관리' : '2. 보호·검증'}">2. 보호·검증</button>
<div class="rw-menu-panel"> <div class="rw-menu-panel">
<a class="nav-link" th:href="${backofficeTrack == 'DDS' ? '/dds-protection' : '/vpd-policies'}" <a class="nav-link" th:href="${backofficeTrack == 'DDS' ? '/dds-protection' : '/vpd-policies'}"
th:text="${backofficeTrack == 'DDS' ? '보호 상태' : 'DB 보호 연결'}">DB 보호 연결</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="/dds-provision" th:if="${backofficeTrack == 'DDS'}">정책 적용</a>
<a class="nav-link" href="/tokens" 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="/probe" th:if="${backofficeTrack != 'DDS'}">권한 결과 확인</a>
<a class="nav-link" href="/dds">보호 범위</a> <a class="nav-link" href="/dds" th:if="${backofficeTrack == 'DDS'}">보호 대상 관리</a>
<a class="nav-link" href="/dds" th:if="${backofficeTrack != 'DDS'}">DDS 직접 조회</a>
</div> </div>
</div> </div>
<div class="rw-menu-group"> <div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '지식 접근' : '연동 도구'}">연동 도구</button> <button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '지식자료 관리' : '연동 도구'}">연동 도구</button>
<div class="rw-menu-panel"> <div class="rw-menu-panel">
<a class="nav-link" href="/objects" th:if="${backofficeTrack != 'DDS'}">ORDS 조회 대상</a> <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="/ords-handlers" th:if="${backofficeTrack != 'DDS'}">ORDS 핸들러</a>
<a class="nav-link" href="/vector-knowledge" th:text="${backofficeTrack == 'DDS' ? '지식자료와 접근 권한' : '지식 검색 관리'}">지식 검색 관리</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-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-reasoning" th:if="${backofficeTrack != 'DDS'}">Reasoning</a>
<a class="nav-link" href="/mcp-sse" th:if="${backofficeTrack != 'DDS'}">SSE 서비스</a> <a class="nav-link" href="/mcp-sse" th:if="${backofficeTrack != 'DDS'}">SSE 서비스</a>
@@ -52,8 +53,9 @@
</div> </div>
</div> </div>
<div class="rw-menu-group"> <div class="rw-menu-group">
<button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '고급 검증' : '운영·고급'}">운영·고급</button> <button class="rw-menu-trigger" type="button" aria-expanded="false" th:text="${backofficeTrack == 'DDS' ? '접근 검증' : '운영·고급'}">운영·고급</button>
<div class="rw-menu-panel"> <div class="rw-menu-panel">
<a class="nav-link" href="/dds-protection#direct-check-heading" th:if="${backofficeTrack == 'DDS'}">직접 접근 검증</a>
<a class="nav-link" href="/operation-status" th:if="${backofficeTrack != 'DDS'}">운영 상태</a> <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> <a class="nav-link" href="/vpd-filter-policies" th:if="${backofficeTrack != 'DDS'}">별도 Filter 관리</a>
<a class="nav-link" href="/settings" th:if="${backofficeTrack != 'DDS'}">설정</a> <a class="nav-link" href="/settings" th:if="${backofficeTrack != 'DDS'}">설정</a>
@@ -74,6 +76,13 @@
</div> </div>
<section th:fragment="architectureStrip(activeLayer)" class="architecture-strip" aria-label="권한 설정부터 결과 확인까지의 흐름"> <section th:fragment="architectureStrip(activeLayer)" class="architecture-strip" aria-label="권한 설정부터 결과 확인까지의 흐름">
<nav th:if="${backofficeTrack == 'DDS'}" class="dds-product-nav" aria-label="DDS 권한 운영 작업">
<a href="/permissions" th:classappend="${activeLayer == 'permission'} ? ' active'">권한 관리</a>
<a href="/dds-protection" th:classappend="${activeLayer == 'protection'} ? ' active'">보호 정책 관리</a>
<a href="/dds-provision" th:classappend="${activeLayer == 'publish'} ? ' active'">정책 적용</a>
<a href="/vector-knowledge" th:classappend="${activeLayer == 'search'} ? ' active'">지식자료 관리</a>
</nav>
<th:block th:unless="${backofficeTrack == 'DDS'}">
<div class="architecture-step" th:classappend="${activeLayer == 'permission'} ? ' active'"> <div class="architecture-step" th:classappend="${activeLayer == 'permission'} ? ' active'">
<span class="architecture-kicker">1 · WHO / WHAT</span> <span class="architecture-kicker">1 · WHO / WHAT</span>
<strong>권한 설계</strong> <strong>권한 설계</strong>
@@ -97,6 +106,7 @@
<strong>결과 확인</strong> <strong>결과 확인</strong>
<p th:if="${backofficeTrack != 'DDS'}">보호 정책을 통과한 데이터만 실제 결과로 확인합니다.</p> <p th:if="${backofficeTrack != 'DDS'}">보호 정책을 통과한 데이터만 실제 결과로 확인합니다.</p>
</div> </div>
</th:block>
</section> </section>
<details th:if="${backofficeTrack == 'DDS'}" class="explanation-details architecture-explanation"> <details th:if="${backofficeTrack == 'DDS'}" class="explanation-details architecture-explanation">

View File

@@ -1,21 +1,21 @@
<!doctype html> <!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"> <html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('지식자료와 접근 권한')}"></head> <head th:replace="~{fragments/layout :: head('지식자료 관리')}"></head>
<body> <body>
<nav th:replace="~{fragments/layout :: nav}"></nav> <nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4"> <main class="container py-4">
<div class="page-title"> <div class="page-title">
<span class="architecture-kicker">DDS · KNOWLEDGE ACCESS</span> <span class="architecture-kicker">DDS · KNOWLEDGE ACCESS</span>
<h1>지식자료와 접근 권한</h1> <h1>지식자료 관리</h1>
<p class="context-summary">등록 · 접근 규칙 · 업무 사용자 검색 · 고급 검증</p> <p class="context-summary">등록 · 접근 규칙 · 업무 사용자 검색 · 고급 검증</p>
<details class="explanation-details"> <details class="explanation-details">
<summary>접근 방식 보기</summary> <summary>도움말</summary>
<p>문서는 검색 단위로 나뉘고 각 단위에 접근 분류값(<code>TECH_TAG</code>)이 연결됩니다. 역할별 허용 값을 등록하면 보호 규칙이 허용된 분류값만 검색 대상으로 남깁니다.</p> <p>문서는 검색 단위로 나뉘고 각 단위에 접근 분류값(<code>TECH_TAG</code>)이 연결됩니다. 역할별 허용 값을 등록하면 보호 규칙이 허용된 분류값만 검색 대상으로 남깁니다.</p>
<p class="mb-0">기본 경로는 Bearer 토큰으로 업무 사용자를 식별합니다. 직접 접근 비교는 고급 검증에서만 사용합니다.</p> <p class="mb-0">기본 경로는 Bearer 토큰으로 업무 사용자를 식별합니다. 직접 접근 비교는 고급 검증에서만 사용합니다.</p>
</details> </details>
</div> </div>
<section th:replace="~{fragments/layout :: architectureStrip('ords')}"></section> <section th:replace="~{fragments/layout :: architectureStrip('search')}"></section>
<div class="alert alert-success" th:if="${message}" th:text="${message}"></div> <div class="alert alert-success" th:if="${message}" th:text="${message}"></div>
<div class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}"></div> <div class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}"></div>
@@ -26,14 +26,14 @@
<section class="content-band"> <section class="content-band">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<span class="architecture-kicker">1 · CONTENT</span> <span class="architecture-kicker">지식자료</span>
<h2>지식자료 등록</h2> <h2>지식자료 등록</h2>
<p class="section-subtitle">문서 ID가 같으면 기존 검색 단위를 교체합니다.</p> <p class="section-subtitle">문서 ID가 같으면 기존 검색 단위를 교체합니다.</p>
</div> </div>
<span class="badge text-bg-secondary" th:text="${summary == null ? '저장소 확인 필요' : summary.chunkCount() + '개 검색 단위'}">0개 검색 단위</span> <span class="badge text-bg-secondary" th:text="${summary == null ? '저장소 확인 필요' : summary.chunkCount() + '개 검색 단위'}">0개 검색 단위</span>
</div> </div>
<details class="explanation-details"> <details class="explanation-details">
<summary>청킹·임베딩 설정 보기</summary> <summary>도움말</summary>
<ul> <ul>
<li>문단을 우선 묶고 길이가 길면 문장·공백 기준으로 청크를 나눕니다.</li> <li>문단을 우선 묶고 길이가 길면 문장·공백 기준으로 청크를 나눕니다.</li>
<li>기본 로컬 임베딩은 외부 API 없이 흐름을 확인하는 개발용 4차원 벡터입니다.</li> <li>기본 로컬 임베딩은 외부 API 없이 흐름을 확인하는 개발용 4차원 벡터입니다.</li>
@@ -70,7 +70,7 @@
<section class="content-band"> <section class="content-band">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<span class="architecture-kicker">2 · POLICY</span> <span class="architecture-kicker">접근 규칙</span>
<h2>권한 규칙은 한 곳에서 관리합니다.</h2> <h2>권한 규칙은 한 곳에서 관리합니다.</h2>
<p class="section-subtitle">사용자·그룹·역할·접근 분류 규칙을 한 곳에서 관리합니다.</p> <p class="section-subtitle">사용자·그룹·역할·접근 분류 규칙을 한 곳에서 관리합니다.</p>
</div> </div>
@@ -84,7 +84,7 @@
<div><h3>보호 방식</h3><p><code th:text="${ddsVectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code>의 보호 규칙이 업무 사용자와 접근 분류값을 비교합니다.</p></div> <div><h3>보호 방식</h3><p><code th:text="${ddsVectorObject}">ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS</code>의 보호 규칙이 업무 사용자와 접근 분류값을 비교합니다.</p></div>
</div> </div>
<details class="explanation-details mt-3"> <details class="explanation-details mt-3">
<summary>보호 방식 기술 상세 보기</summary> <summary>도움말</summary>
<p>VPD는 요청마다 권한 테이블을 읽어 predicate를 계산합니다. DDS의 직접 비교 경로는 보호 VIEW에 선언한 DATA GRANT와 END USER 매핑을 사용합니다. 토큰 경로에서는 토큰이 공통 사용자 Context를 만들고, 보호 VIEW의 객체별 DATA GRANT predicate가 같은 권한 테이블을 다시 평가합니다.</p> <p>VPD는 요청마다 권한 테이블을 읽어 predicate를 계산합니다. DDS의 직접 비교 경로는 보호 VIEW에 선언한 DATA GRANT와 END USER 매핑을 사용합니다. 토큰 경로에서는 토큰이 공통 사용자 Context를 만들고, 보호 VIEW의 객체별 DATA GRANT predicate가 같은 권한 테이블을 다시 평가합니다.</p>
</details> </details>
</section> </section>
@@ -92,14 +92,14 @@
<section class="content-band"> <section class="content-band">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<span class="architecture-kicker">3 · TOKEN CONTEXT</span> <span class="architecture-kicker">업무 사용자 검색</span>
<h2>업무 사용자 검색</h2> <h2>업무 사용자 검색</h2>
<p class="section-subtitle">하나의 기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다.</p> <p class="section-subtitle">하나의 기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다.</p>
</div> </div>
<span class="badge text-bg-secondary">객체별 DATA GRANT</span> <span class="badge text-bg-secondary">객체별 DATA GRANT</span>
</div> </div>
<details class="explanation-details"> <details class="explanation-details">
<summary>업무 사용자 검색 기술 상세 보기</summary> <summary>도움말</summary>
<ol> <ol>
<li>서버가 설정한 하나의 DDS 기술 사용자로 연결합니다.</li> <li>서버가 설정한 하나의 DDS 기술 사용자로 연결합니다.</li>
<li>Bearer 토큰을 <code>CB_AGENT_CTX_PKG.SET_USER_BY_BEARER</code>에 전달합니다.</li> <li>Bearer 토큰을 <code>CB_AGENT_CTX_PKG.SET_USER_BY_BEARER</code>에 전달합니다.</li>
@@ -134,14 +134,14 @@
<section class="content-band"> <section class="content-band">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<span class="architecture-kicker">4 · DIRECT CHECK</span> <span class="architecture-kicker">고급 검증</span>
<h2>고급: 직접 접근 비교</h2> <h2>고급: 직접 접근 비교</h2>
<p class="section-subtitle">DDS 선언형 보호 규칙 점검용 고급 검증</p> <p class="section-subtitle">DDS 선언형 보호 규칙 점검용 고급 검증</p>
</div> </div>
<span class="badge text-bg-primary" th:text="${ddsVectorObject}">DDS view</span> <span class="badge text-bg-primary" th:text="${ddsVectorObject}">DDS view</span>
</div> </div>
<details class="explanation-details"> <details class="explanation-details">
<summary>검색 처리 방식 보기</summary> <summary>도움말</summary>
<ol> <ol>
<li>검색어를 등록 때와 같은 방식으로 임베딩합니다.</li> <li>검색어를 등록 때와 같은 방식으로 임베딩합니다.</li>
<li>DDS END USER로 보호 VIEW에 연결합니다.</li> <li>DDS END USER로 보호 VIEW에 연결합니다.</li>
@@ -177,7 +177,7 @@
<section class="content-band"> <section class="content-band">
<details class="explanation-details"> <details class="explanation-details">
<summary>저장 현황 보기</summary> <summary>저장 현황</summary>
<div class="summary-grid mt-3"> <div class="summary-grid mt-3">
<div class="summary-tile"><span class="label">문서</span><strong th:text="${summary == null ? '-' : summary.documentCount()}">0</strong></div> <div class="summary-tile"><span class="label">문서</span><strong th:text="${summary == null ? '-' : summary.documentCount()}">0</strong></div>
<div class="summary-tile"><span class="label">검색 단위</span><strong th:text="${summary == null ? '-' : summary.chunkCount()}">0</strong></div> <div class="summary-tile"><span class="label">검색 단위</span><strong th:text="${summary == null ? '-' : summary.chunkCount()}">0</strong></div>

View File

@@ -1,12 +1,12 @@
<!doctype html> <!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"> <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> <body>
<nav th:replace="~{fragments/layout :: nav}"></nav> <nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4"> <main class="container py-4">
<header class="page-title"> <header class="page-title">
<span class="architecture-kicker">DDS · PROTECTION STATUS</span> <span class="architecture-kicker">DDS · PROTECTION STATUS</span>
<h1>DDS 보호 상태</h1> <h1>보호 정책 관리</h1>
<p class="context-summary">지식 검색에 쓰는 보호 객체가 현재 권한 기준대로 연결되어 있는지 확인합니다.</p> <p class="context-summary">지식 검색에 쓰는 보호 객체가 현재 권한 기준대로 연결되어 있는지 확인합니다.</p>
</header> </header>
@@ -87,7 +87,7 @@
</div> </div>
</div> </div>
<details class="explanation-details m-0"> <details class="explanation-details m-0">
<summary>두 집행 경로의 차이 보기</summary> <summary>도움말</summary>
<div class="protection-path-comparison"> <div class="protection-path-comparison">
<div><strong>업무 사용자 검색</strong><span>기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다. 보호 규칙이 현재 접근 권한을 평가합니다.</span></div> <div><strong>업무 사용자 검색</strong><span>기술 사용자 연결에서 Bearer 토큰으로 업무 사용자를 식별합니다. 보호 규칙이 현재 접근 권한을 평가합니다.</span></div>
<div><strong>직접 접근 비교</strong><span>게시된 DATA ROLE과 DATA GRANT를 직접 확인하는 고급 진단 경로 · 권한 변경 뒤 별도 게시 필요</span></div> <div><strong>직접 접근 비교</strong><span>게시된 DATA ROLE과 DATA GRANT를 직접 확인하는 고급 진단 경로 · 권한 변경 뒤 별도 게시 필요</span></div>