# DDS Permission Console

`dds-backoffice`는 기존 VPD 데모와 코드·프로세스·포트·보호 객체를 분리한 Deep Data Security 전용 데모입니다.

- 기본 포트: `8083`
- 기본 화면: `/` (공통 권한 여정), `/dds` (지식 보호 객체 구조)
- 권한 반영: `/dds-provision` (공통 권한을 DDS DATA GRANT로 미리보기·게시)
- 주 보호 객체: `ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS` (지식 청크·분류값 검색)
- 보조 SQL 검증 객체: `ADMIN.V_DDS_CUSTOMERS_PG`, `ADMIN.V_DDS_CUSTOMERS_MY`
- 보조 DDS SQL 검증 사용자: `dds_demo_my`, `dds_demo_pg`, `dds_demo_both`, `dds_demo_none`
- MCP SSE 경로: Bearer가 기존 업무 사용자를 식별하고 local DDS `END USER` Context로 실행
- 레거시 토큰 데모 기술 사용자: `dds_demo_token` (비교·호환성 검증용)

## 두 데모의 관계

두 인스턴스는 프로세스·포트·실행 경계를 분리하지만, 화면에서 설명하는 관리 순서는 같습니다. PG/MySQL 원본 매트릭스는 DDS의 저수준 행/객체 Grant 예제일 뿐이며, 제품형 주 시나리오는 벡터 지식자료입니다.

```text
사용자·그룹·역할 → 데이터 권한 규칙 → DDS Grant 게시 → 보호 연결 → 실제 결과 확인
```

8083 DDS 화면의 `/users`, `/groups`, `/roles`, `/permissions`, `/effective-matrix`는 8082 VPD 화면과 같은 관리 기준을 보여줍니다. 보호 객체별 DDS `DATA GRANT`가 같은 권한체계를 집행한다는 점은 동일하지만, 두 가지 실행 경로를 분리해 보여줍니다.

`/dds-provision`은 이 차이를 연결하는 명시적 배포 단계입니다. 직접 역할과 활성 그룹에서 상속된 역할을 합쳐 사용자별 유효 권한을 계산하고, 애플리케이션 객체 매핑을 거쳐 `CREATE OR REPLACE DATA GRANT` SQL을 미리 보여줍니다. 그룹을 DDS 그룹으로 복사하지 않고, 그룹이 상속한 역할을 해당 DDS DATA ROLE의 최종 predicate로 합칩니다. 매핑되지 않은 객체는 경고로 남기고 게시하지 않습니다.

| 데모 | 사용자 식별 | 정책 표현 | 권한 변경 |
|---|---|---|---|
| VPD | 공통 계정 + Bearer/세션 컨텍스트 | 동적 predicate 함수 | 업무 권한 테이블 |
| DDS 직접 비교 | DDS `END USER` 직접 로그인 | `DATA ROLE` + `DATA GRANT` | 선언형 DDL/Grant |
| DDS MCP SSE | Bearer → `CB_APP_USER` → local `DDS_U_<id>` Context | `DATA ROLE` + `DATA GRANT` | 권한 변경 시 bulk 재게시 |
| DDS 레거시 토큰 데모 | 단일 DDS 기술 사용자 + Bearer → `CB_AGENT_CTX` | 객체별 `DATA GRANT` predicate + 공통 `CB_*` | 요청 시 공통 권한 재평가 |

MCP SSE 경로는 Confidential Application의 OCI IAM database-access token으로 Context attach를 승인받지만, 업무 사용자 관리는 IAM으로 이전하지 않는다. 기존 `CB_APP_USER`/그룹/역할/permission이 source of truth이고 DDS는 이를 사용자별 보안 주체로 투영한다.

권한 변경 후에는 다음 순서로 확인합니다.

```text
1. /permissions에서 사용자·그룹·역할·행/TAG·컬럼 기준을 저장
2. 저장 직후 활성 사용자의 MCP local END USER/DATA ROLE/DATA GRANT를 bulk 재게시
3. /dds-provision에서 전체 재동기화·변경 검토·복구 게시
4. /dds/mcp/sse의 `dds_vector_search`로 실제 MCP 권한 결과를 검증
5. /dds에서 직접 END USER별 저수준 결과를 비교
```

이 앱의 조회 경계는 다음과 같습니다.

```text
MCP Bearer → CB_APP_USER → DDS_U_<id> Context → DATA ROLE/DATA GRANT → DDS 보호 VIEW → 조회 결과
직접 비교: END USER → DATA ROLE → DATA GRANT → DDS 보호 VIEW
```

ORDS Handler가 Bearer 값을 문자열로 바꾸는 것만으로 DDS Context가 생성되지는 않습니다. MCP 경로는 JDBC `EndUserSecurityContext`를 SQL 실행 전에 attach하고, `finally`에서 해제한다. OCI IAM client-credentials token은 서비스 attach 권한만 나타내며, 사람 사용자는 기존 Bearer→업무 사용자 매핑으로 결정한다.

## 실행

먼저 DDS 데모 전용 로컬 데이터셋과 권한 객체를 적용합니다. 이 스크립트는 VPD VIEW나 외부 RDS DB Link를 사용하지 않습니다.

```bash
sqlplus "$ADB_USER/$ADB_PASSWORD@$ADB_TNS" @sql/adb/31_dds_standalone_demo_setup.sql
sqlplus "$ADB_USER/$ADB_PASSWORD@$ADB_TNS" @sql/adb/32_dds_vector_tag_setup.sql
bash scripts/setup-dds-token-data-grant.sh
sqlplus "$ADB_USER/$ADB_PASSWORD@$ADB_TNS" @sql/adb/36_dds_sales_knowledge_scenario.sql
```

`32_dds_vector_tag_setup.sql`은 공통 청크·태그 저장소를 읽는 DDS 전용 VIEW를 만들고, 동일한 `CB_PERMISSION`/`CB_PERMISSION_RULE`에 역할별 TAG 규칙을 등록합니다. `SPRING_BOOT`, `ORDS`, `ORACLE_VPD`, `MCP`처럼 한 역할에 여러 TAG가 있으면 `/dds-provision`이 OR 조건으로 합쳐 DDS DATA GRANT를 다시 만듭니다. 벡터 검색에 필요한 `EMBEDDING`은 DDS 엔진이 거리 계산에 사용하므로 Grant에서 제외하지 않고, 검색 응답에서는 애플리케이션이 반환하지 않습니다. 그 외 민감 컬럼은 애플리케이션 권한의 원문 허용 목록에 없으면 `ALL COLUMNS EXCEPT`로 제외됩니다. 검색 화면에서는 같은 문서를 청킹·임베딩한 뒤 선택한 END USER로 직접 조회하므로, DDS Grant가 없는 주체는 보호 VIEW 자체를 볼 수 없습니다.

`34_dds_token_data_grant_common_auth.sql`은 `dds_demo_token` 하나와 `cb_dds_token_role` 하나를 만들고, `ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS`에 객체별 Data Grant를 연결합니다. Data Grant의 predicate 함수는 직접 역할과 활성 그룹 역할을 합친 뒤 `CB_PERMISSION`·`CB_PERMISSION_RULE`의 TAG 허용/거부 규칙을 요청 시 평가합니다. 토큰은 Data Grant 문법의 인자가 아니라 `CB_AGENT_CTX`를 초기화하는 입력입니다.

`36_dds_sales_knowledge_scenario.sql`은 `agent_sales`와 `SALES_KNOWLEDGE_ROLE`을 만들고 `TECH_TAG=SALES`인 세일즈 청크(28004)를 추가합니다. 짧은 데모 토큰 `dds_sales_demo_token`을 사용하면 결과는 28004만 남아야 합니다. 운영에서는 이 토큰 대신 Bearer 관리 화면에서 발급한 임시 토큰을 사용합니다.

토큰 경로를 제거할 때는 사용 중인 애플리케이션이 없는지 확인한 뒤 `sql/adb/37_dds_sales_knowledge_scenario_cleanup.sql`과 `sql/adb/35_dds_token_data_grant_cleanup.sql`을 별도로 승인해 실행합니다.

그 다음 DDS 인스턴스를 실행합니다.

```bash
cd dds-backoffice
export DDS_BACKOFFICE_DB_URL="${BACKOFFICE_DB_URL}"
export DDSUSER_MY_PASSWORD='...'
export DDSUSER_PG_PASSWORD='...'
export DDSUSER_BOTH_PASSWORD='...'
export DDSUSER_NONE_PASSWORD='...'
export DDSUSER_TOKEN_PASSWORD='...'
mvn -DskipTests package
java -jar target/dds-permission-backoffice-0.1.0-SNAPSHOT.jar
```

관리자 로그인은 `DDS_BACKOFFICE_ADMIN_USER` / `DDS_BACKOFFICE_ADMIN_PASSWORD`를 사용합니다. 직접 비교용 DDS END USER와 토큰 경로의 기술 사용자 비밀번호는 서버 환경 변수로만 읽고 화면에 표시하거나 저장하지 않습니다. 토큰은 `/vector-knowledge` 요청 본문에서만 처리하고 저장하지 않습니다.

VM 배포는 `scripts/deploy-dds-backoffice-vm.sh`를 사용합니다. 스크립트는 기존 `/home/opc/apps/vpd-backoffice`의 wallet과 환경값을 읽어 `/home/opc/apps/dds-backoffice`를 별도 프로세스로 기동합니다. 기본 바인딩은 `127.0.0.1:8083`이며, 인터넷 공개는 HTTPS reverse proxy와 NSG/firewall 승인을 별도로 거쳐야 합니다.
