# 설계서: 권한체계 중심 VPD 안내와 토큰 검증 흐름 (#557)

> **상태**: Approved
> **작성**: [AI] Architect · **최종수정**: 2026-06-29
> **추적성** — Redmine: #557 · 후속 UX: #558~#565 · 관련 ADR: 없음
> · 구현 파일: `ProbeController`, `ProbeResult`, `VpdPolicyService`, 대시보드/VPD/토큰/검증 템플릿
> · 테스트: `ProbeResultTest`, `VpdPolicyServiceTest`, `GuidedFlowTemplateTest`

## 1. 목적 (Why)

권한을 만든 사람의 머릿속 구조를 모르는 운영자도 “누가 어떤 데이터를 볼 수 있는지 정하고, DB가 그대로 제한하는지 확인한다”는 한 흐름으로 제품을 이해하고 사용할 수 있게 한다.

## 2. 범위 (Scope)

- **포함**: Macro→Micro 설명 구조, 내비게이션 재분류, 동적 권한 VPD 기본 적용, custom filter 고급 분리, 토큰 검증 단순화, 원문/임시 토큰 검증, 상태별 일반 문장과 다음 행동, 발급→검증 연결.
- **제외**: 권한 데이터 모델 변경, VPD 함수 SQL 알고리즘 변경, 토큰 원문 저장, 운영 DB DDL 자동 실행, ORDS handler 생성 방식 변경.

## 3. 인수조건 (Acceptance Criteria)

- [x] 첫 화면에서 권한 설계 → DB 보호 → 토큰 발급 → 결과 확인의 목적과 순서를 이해할 수 있다.
- [x] 기본 VPD 적용은 객체만 선택하고 권한체계 동적 함수와 SELECT 정책을 서버가 결정한다.
- [x] `CB_AGENT_DOC_VPD_FILTER`는 일반 UI에서 수정할 수 없고 직접 POST도 거부된다.
- [x] 별도 filter/predicate와 policy 교체는 사용 기준·안전 규칙이 있는 고급 영역에만 노출된다.
- [x] 검증 화면은 등록 토큰 선택 없이 Bearer 원문 또는 테스트 중에만 존재하는 임시 토큰과 대상을 입력한다.
- [x] 검증 결과가 상태, 적용 주체, 행 결과, 다음 행동 순으로 설명되고 HTTP 원문은 접혀 있다.
- [x] 존재하지 않는 테스트 토큰은 “DB에 등록된 토큰이 아님”과 재발급 절차를 안내한다.
- [x] 발급 직후 토큰 복사와 검증 화면 이동이 명확하다.

## 4. 컨텍스트 & 제약

- 토큰 원문은 보안상 저장하지 않고 SHA-256 hash만 저장한다. 따라서 목록에서 원문을 재선택해 호출하는 기능은 제공하지 않는다.
- 권한 원천은 사용자/그룹/역할/권한/규칙 테이블이며 VPD 함수가 요청 시점에 이를 조회한다.
- 권한이 없거나 컨텍스트/규칙이 잘못되면 `1=0`으로 닫는 fail-closed 원칙을 유지한다.
- 운영 DB의 기존 custom policy는 삭제하지 않고 고급 관리 기능으로 남긴다.

## 5. 아키텍처 개요

```text
[Macro: 무엇을 하려는가]
  누가(User/Group) → 어떤 역할(Role) → 어떤 데이터(Object/Rule)
          │
          ▼
[Micro: DB가 어떻게 지키는가]
  CB_PERMISSION* 테이블 → CB_AGENT_DOC_VPD_FILTER → Oracle VPD
          │
          ▼
[Evidence: 실제로 지켜졌는가]
  1회 표시/임시 토큰 → ORDS 호출 → 사용자/역할 + 보이는 행 + 다음 행동
```

- I/O 경계: controller/service는 DB catalog와 ORDS를 호출한다.
- 순수 표현 경계: `ProbeResult`의 상태별 제목·설명·다음 행동은 외부 I/O 없이 테스트한다.
- 안전 경계: 기본 endpoint는 동적 함수만 사용하고 custom 함수 생성/교체 endpoint는 고급 기능으로 유지한다.

## 6. 데이터 모델

- 입력: `objectKey`, `bearerToken`, `objectId`, `limit`.
- 기본 VPD 명령: policy `CB_PERMISSION_SELECT_POLICY`, function `*.CB_AGENT_DOC_VPD_FILTER`, statements `SELECT`, enabled `true`, update check `false`.
- 검증 표현: 기존 `ProbeResult`에 상태별 `title`, `plainSummary`, `nextAction`, `successLike` 계산 메서드를 둔다.
- 검증 컨텍스트: hash로 찾은 `TokenContextView`와 `ProtectedObject`를 controller model에 추가한다. 임시 토큰은 실행 후 회수한다. 원문은 model/result/log에 저장하지 않는다.

## 7. 함수 명세 (Function Specs)

| 함수 | 책임 | 입력 | 출력 | 에러/실패 | 복잡? |
|---|---|---|---|---|---|
| `createDefaultPermissionPolicy` | 객체에 표준 동적 권한 VPD 연결 | `objectKey` | 없음 | 함수 미설치, 중복/DB 오류 | 단순 |
| `saveFilterFunction` guard | 핵심 동적 함수 덮어쓰기 차단 | owner/name/predicate | 없음 | 핵심 함수명이면 `AppException` | 단순 |
| `ProbeResult.title/plainSummary/nextAction` | 기술 상태를 일반 문장으로 변환 | status/result | 문자열 | 알 수 없는 상태도 안전 안내 | 단순 |
| `findTokenContextByPlainToken` | 원문 hash에 대응하는 사용자/역할 설명 조회 | 토큰 원문 | nullable context | 미등록이면 null | 단순 |
| `ProbeController.run` | 단일 토큰 입력으로 실행·설명 model 구성 | form | fragment | status별 결과 fragment | 단순 |

## 8. 흐름 / 알고리즘

1. 운영자는 사용자/그룹/역할과 객체별 행·열 규칙을 저장한다.
2. 보호할 DB 객체를 선택하고 “권한체계 연결”을 누른다.
3. 서버는 설치된 `CB_AGENT_DOC_VPD_FILTER`를 찾아 동적 SELECT policy를 붙인다.
4. 토큰을 발급하면 원문을 한 번 복사하고 검증으로 이동한다.
5. 검증 시 hash로 토큰과 사용자를 식별하고 ORDS를 호출한다.
6. 화면은 사용자/직접 역할/그룹 상속, 보인 행 수 또는 차단 이유, 다음 행동을 먼저 보여준다.
7. HTTP request/response는 문제 분석용 상세 영역에서만 연다.

## 9. 엣지케이스 & 에러 처리

- 토큰 미등록: 환경 파일과 DB가 어긋난 상태로 설명하고 새 발급을 안내한다.
- 만료/회수: 새 토큰 발급 또는 활성 토큰 사용을 안내한다.
- 0행: 오류가 아니라 VPD가 현재 권한 기준으로 모든 행을 제외했을 가능성을 먼저 설명한다.
- ORDS 미설정/경로 오류/접속 실패: 권한 문제와 인프라 문제를 구분한다.
- 기존 별도 함수가 ORA-28110을 내는 경우: 토큰 오류와 분리해 `VPD_FILTER_ERROR`로 설명하고 정상 동적 권한 객체를 검증 목록에서 우선한다.
- 핵심 함수 미설치: 자동으로 custom predicate를 만들지 않고 설치 상태 확인을 요구한다.
- 핵심 함수 수정 시도: UI와 service 양쪽에서 차단한다.

## 10. 테스트 계획

- `ProbeResultTest`: 성공, 0행, 토큰 미등록, 만료, ORDS 장애의 일반 문장/다음 행동.
- `VpdPolicyServiceTest`: 핵심 함수 수정 거부, 기본 적용이 표준 policy/function/SELECT/DYNAMIC 옵션을 사용.
- `GuidedFlowTemplateTest`: 검증의 단일 token 입력, 기술 상세 접힘, VPD 기본/고급 분리, 대시보드 4단계.
- 전체 `mvn test` 34건, `mvn -DskipTests package`.
- 실행 환경에서는 로그인 후 주요 페이지 HTTP 200과 실제 신규 토큰 검증을 확인한다. 외부 ORDS가 없으면 상태별 설명까지만 검증한다.

## 11. 리스크 & 대안 검토

- 기존 template 선택 UI를 기본으로 유지하면 유연하지만 일반 사용자가 정책 이름과 함수를 이해해야 한다. 객체만 받는 전용 기본 endpoint를 선택한다.
- 목록의 등록 토큰을 선택하게 하려면 원문 저장이 필요해진다. 보안 경계를 유지하고 원문 직접 입력만 제공한다.
- custom 기능을 제거하면 기존 운영 정책 관리가 막힌다. 삭제 대신 고급 영역과 서버 안전장치로 격리한다.

## 12. 미해결 질문 (Open Questions)

- 실제 환경에서 동적 함수가 연결된 객체는 신규 토큰으로 ORDS/VPD 성공 응답까지 확인했다.
- 기존 `BOARD_ASSIGNMENTS_FILTER`는 ORA-28110 상태다. 운영 policy 교체는 DDL 승인 후 별도 Filter 고급 화면에서 표준 동적 함수로 복구해야 한다.
- `.env`의 기존 테스트 토큰 두 개는 현재 DB에 없으며, 환경 비밀 교체는 저장소 밖 운영 작업으로 남긴다.
