<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<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="badge text-bg-warning">고급 설정</span>
    <h1 class="mt-2">고급 접근 조건</h1>
    <details class="explanation-details">
      <summary>도움말</summary>
      <p>일반 권한은 이 화면에서 만들지 않습니다. 권한체계로 표현할 수 없는 객체 전용 조건이 있을 때만 별도 함수를 추가합니다.</p>
    </details>
  </div>

  <div class="alert alert-warning" th:if="${runtimeError}">
    <strong th:text="${runtimeError.title()}">조회할 수 없습니다.</strong>
    <span th:text="${runtimeError.message()}">message</span>
  </div>
  <div class="alert alert-success" th:if="${successMessage}" th:text="${successMessage}">등록되었습니다.</div>
  <div class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}">처리할 수 없습니다.</div>

  <section class="content-band default-filter-explainer">
    <div class="section-heading">
      <div>
        <span class="architecture-kicker">기본 동작: 권한체계 자동 반영</span>
        <h2>대부분의 변경은 권한 규칙 화면에서 끝납니다.</h2>
      </div>
      <a class="btn btn-sm rw-btn-primary" href="/permissions">권한 규칙으로 이동</a>
    </div>
    <details class="explanation-details">
      <summary>운영자 설정과 DB 자동 처리의 차이 보기</summary>
      <div class="macro-micro-grid">
        <div>
          <h3>운영자가 관리하는 것</h3>
          <p>사용자, 그룹, 역할, 객체별 ALLOW/DENY, 행 조건과 컬럼 공개 범위를 관리합니다.</p>
        </div>
        <div>
          <h3>DB가 자동으로 하는 것</h3>
          <p><code>CB_AGENT_DOC_VPD_FILTER</code>가 요청마다 그 권한을 합쳐 predicate를 만들고, 권한이나 유효한 컨텍스트가 없으면 <code>1=0</code>으로 차단합니다.</p>
        </div>
      </div>
    </details>
    <div class="protected-function-card">
      <strong>보호되는 핵심 함수</strong>
      <code>CB_AGENT_DOC_VPD_FILTER</code>
      <span>이 화면과 서버 양쪽에서 직접 덮어쓰기를 차단합니다.</span>
    </div>
    <details class="explanation-details mt-3">
      <summary>Filter 설명과 작성 기준 보기</summary>
      <p><strong>Filter 설명:</strong> 이 화면의 function은 DB가 행을 걸러낼 predicate를 반환합니다. 기본 함수는 사용자·그룹·역할·TAG 권한을 동적으로 읽고, 별도 Filter는 객체 고유 조건을 추가할 때만 사용합니다. 저장하는 설명에는 대상, 조건, 예상되는 허용/차단 결과를 적습니다.</p>
    </details>
  </section>

  <section class="content-band">
    <details class="advanced-details">
      <summary>고급: 별도 Filter 만들기</summary>
      <div class="advanced-guidance mt-3">
        <h2>먼저 이 기준을 확인하세요.</h2>
        <ul>
          <li><strong>사용 가능:</strong> 권한 테이블로 표현할 수 없는 객체 고유 조건을 별도 함수로 격리할 때</li>
          <li><strong>사용 금지:</strong> 부서, 본인, 특정 값, 역할별 허용처럼 기존 권한 규칙이 지원하는 조건을 중복 작성할 때</li>
          <li><strong>안전 원칙:</strong> 컨텍스트가 없거나 값이 잘못되면 반드시 <code>1=0</code>인 fail-closed 결과를 사용</li>
          <li><strong>검증 원칙:</strong> 허용·거부·빈 컨텍스트·예상 밖 값 테스트를 만든 뒤 한 객체에 먼저 적용</li>
        </ul>
      </div>

      <form method="post" action="/vpd-filter-policies/filters" class="form-grid mt-3">
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
        <label>
          Function owner
          <select class="form-select" name="functionOwner">
            <option value="">현재 연결 사용자</option>
            <option th:each="owner : ${formOptions.owners()}" th:value="${owner}" th:text="${owner}"></option>
          </select>
        </label>
        <label>
          새 Function 이름
          <input class="form-control" name="functionName" placeholder="예: BOARD_POSTS_SPECIAL_FILTER" required>
          <span class="form-hint">CB_AGENT_DOC_VPD_FILTER 이름은 사용할 수 없습니다.</span>
        </label>
        <label class="span-2">
          반환할 predicate
          <textarea class="form-control" id="filter-only-predicate" name="filterPredicate" rows="4"
                    placeholder="예: REGEXP_LIKE(UPPER(TECH_TAG), '(^|,)SPRING_BOOT(,|$)')" required></textarea>
        </label>
        <label class="span-2">
          이 Filter가 보호하는 내용
          <input class="form-control" name="description" maxlength="500"
                 placeholder="예: 지식자료 TECH_TAG가 권한에 포함된 검색 단위만 허용" required>
        </label>
        <div class="question-presets span-2" aria-label="Filter predicate 안전 예시">
          <button class="btn rw-btn-secondary question-preset" type="button" data-target="filter-only-predicate" data-question="1=0">안전한 기본 차단</button>
          <button class="btn rw-btn-secondary question-preset" type="button" data-target="filter-only-predicate" data-question="REGEXP_LIKE(UPPER(TECH_TAG), '(^|,)SPRING_BOOT(,|$)')">기술 태그 비교 예시</button>
        </div>
        <button class="btn rw-btn-primary" type="submit">별도 Filter 저장</button>
      </form>

      <div class="section-heading mt-4">
        <h2>설치된 Filter 함수</h2>
        <span class="badge text-bg-secondary" th:text="${#lists.size(formOptions.functions())}">0</span>
      </div>
      <div class="table-responsive">
        <table class="table table-sm align-middle">
          <thead><tr><th>Function</th><th>구분</th><th>설명</th><th>Source / 변경</th></tr></thead>
          <tbody>
          <th:block th:each="function, iter : ${formOptions.functions()}">
            <tr>
              <td><code th:text="${function.value()}">ADMIN.FILTER</code></td>
              <td>
                <span class="badge text-bg-primary" th:if="${function.permissionSystemDefault()}">권한체계 기본</span>
                <span class="badge text-bg-warning" th:unless="${function.permissionSystemDefault()}">별도 Filter</span>
              </td>
              <td>
                <span th:text="${filterDescriptions[function.owner() + '|' + function.functionName()]}">Filter 목적</span>
              </td>
              <td>
                <button class="btn btn-sm rw-btn-secondary" type="button"
                        th:hx-get="@{/vpd-policies/function-source(owner=${function.owner()},packageName=${function.packageName()},functionName=${function.functionName()})}"
                        th:hx-target="${'#filter-source-' + iter.index}" hx-swap="innerHTML">Source 보기</button>
              </td>
            </tr>
            <tr>
              <td colspan="4" class="policy-source-cell">
                <div th:id="${'filter-source-' + iter.index}" class="text-muted small">Source 보기를 누르면 현재 함수 내용을 표시합니다.</div>
                <div class="alert alert-light mt-3 mb-0" th:if="${function.permissionSystemDefault()}">
                  이 함수는 권한체계의 핵심 실행 경로이므로 직접 수정할 수 없습니다. 행 접근 규칙을 변경하세요.
                </div>
                <form method="post" action="/vpd-filter-policies/filters" class="filter-edit-form mt-3"
                      th:unless="${function.permissionSystemDefault()}">
                  <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
                  <input type="hidden" name="functionOwner" th:value="${function.owner()}">
                  <input type="hidden" name="functionName" th:value="${function.functionName()}">
                  <label>
                    이 별도 Filter의 새 predicate
                    <textarea class="form-control" name="filterPredicate" rows="3" required
                              th:text="${filterPredicates[function.owner() + '|' + function.functionName()]}"></textarea>
                  </label>
                  <label>
                    Filter 설명
                    <input class="form-control" name="description" maxlength="500"
                           th:value="${filterDescriptions[function.owner() + '|' + function.functionName()]}">
                  </label>
                  <button class="btn btn-sm rw-btn-secondary" type="submit">별도 Filter 수정</button>
                </form>
              </td>
            </tr>
          </th:block>
          <tr th:if="${#lists.isEmpty(formOptions.functions())}"><td colspan="4" class="text-muted">설치된 VPD 함수가 없습니다.</td></tr>
          </tbody>
        </table>
      </div>
    </details>
  </section>

  <section class="content-band">
    <details class="advanced-details">
      <summary>고급: 적용된 Policy를 별도 Filter로 교체</summary>
      <div class="advanced-guidance mt-3">
        <strong>영향 범위</strong>
        <p>기존 policy를 삭제한 뒤 새 policy를 등록합니다. 기본 권한체계 경로에서 벗어나므로 대상 객체, 거부 조건, 복구할 기본 함수와 검증 토큰을 먼저 준비하세요.</p>
      </div>
      <div class="table-responsive mt-3">
        <table class="table table-sm align-middle">
          <thead><tr><th>Object</th><th>현재 Policy</th><th>현재 Function</th><th>구분</th><th>교체</th></tr></thead>
          <tbody>
          <tr th:each="policy : ${policies}">
            <td><code th:text="${policy.objectDisplayName()}">ADMIN.TABLE</code></td>
            <td><code th:text="${policy.policyName()}">POLICY</code></td>
            <td><code th:text="${policy.functionDisplayName()}">ADMIN.FILTER</code></td>
            <td>
              <span class="badge text-bg-primary" th:if="${policy.permissionSystemDefault()}">권한체계 자동</span>
              <span class="badge text-bg-warning" th:unless="${policy.permissionSystemDefault()}">별도 Filter</span>
            </td>
            <td>
              <form method="post" action="/vpd-filter-policies/replace" class="filter-replace-form">
                <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
                <input type="hidden" name="oldObjectKey" th:value="${policy.objectDisplayName()}">
                <input type="hidden" name="oldPolicyName" th:value="${policy.policyName()}">
                <input type="hidden" name="objectKey" th:value="${policy.objectDisplayName()}">
                <input class="form-control form-control-sm" name="policyName" th:value="${policy.policyName()}" aria-label="새 policy 이름">
                <select class="form-select form-select-sm" name="functionKey" aria-label="새 filter function">
                  <option th:each="function : ${formOptions.functions()}"
                          th:value="${function.value()}" th:text="${function.value()}"
                          th:selected="${function.value() == policy.functionDisplayName()}"></option>
                </select>
                <input type="hidden" name="enabled" value="true">
                <input type="hidden" name="statementTypes" th:value="${policy.statementTypes()} ?: 'SELECT'">
                <button class="btn btn-sm btn-outline-warning" type="submit">영향 확인 후 교체</button>
              </form>
            </td>
          </tr>
          <tr th:if="${#lists.isEmpty(policies)}"><td colspan="5" class="text-muted">적용된 VPD policy가 없습니다.</td></tr>
          </tbody>
        </table>
      </div>
    </details>
  </section>
</main>
</body>
</html>
