# 설계서: 이해당사자 토큰과 역할 기반 KB 원장 권한 예제 (#619) > **상태**: Implemented · QA passed · deployed > **작성**: [AI] Architect · **최종수정**: 2026-07-07 > **추적성** — Redmine: #619 · 관련 설계: #618 정형 데이터 원장 조회, #492 토큰 검증 흐름 ## 1. 목적 `POC_2.KB_STAKEHOLDERS`를 업무 사용자 원장으로 삼아 모든 이해당사자에게 Bearer Token을 발급한다. 토큰의 실제 주체, 역할, 채널은 해당 원장에서만 해석하고, 접근 허용 여부는 백오피스의 **접근 규칙**(역할 → 보호 객체 → 행 규칙/표시 예외)으로 관리한다. ## 2. 핵심 결정 - `CB_AGENT_BEARER_KEY.STAKEHOLDER_USER_ID`가 `KB_STAKEHOLDERS.USER_ID`를 가리킨다. 기존 숫자형 `USER_ID`는 기존 기능 호환용 application-user bridge로 유지한다. - 이해당사자마다 `CB_APP_USER.STAKEHOLDER_USER_ID` bridge를 하나 만들고, `KB_STAKEHOLDERS.ROLE`이 `지점장`이면 `KB_BRANCH_MANAGER_ROLE`, `설계사`이면 `KB_DESIGNER_ROLE`을 부여한다. 다른 역할은 토큰은 발급되지만 별도 권한을 추가하기 전까지 fail-closed다. - VPD package는 토큰에서 `STAKEHOLDER_USER_ID`, `STAKEHOLDER_ROLE`, 정규화된 `STAKEHOLDER_CHANNEL`을 application context에 설정한다. `설계사채널 → 설계사`, `GA채널 → GA`를 정규화한다. - `지점장` 여부는 package의 하드코딩 분기가 아니라 `KB_BRANCH_MANAGER_ROLE`의 rule value(`지점장`)와 `KB_STAKEHOLDERS.ROLE` 일치로 결정한다. 설계사 분기는 같은 방식으로 `설계사`를 확인한다. - `RLS_FILTER`의 문자열은 실행하지 않는다. 권한 테이블에 저장된 allowlist rule type만 predicate로 변환한다. ## 3. 예제 권한 | 역할 | 보호 객체 | 행 규칙 | 결과 | |---|---|---|---| | KB_DESIGNER_ROLE | KB_CONTRACTS | `FC_ID / STAKEHOLDER_SELF` | 본인 담당 계약 | | KB_BRANCH_MANAGER_ROLE | KB_CONTRACTS | `FC_CHANNEL / STAKEHOLDER_CHANNEL` | 같은 채널 계약 | | KB_DESIGNER_ROLE | 고객·담보·청구·외부보유 | `CUST_ID / STAKEHOLDER_SELF` | 본인 담당 고객 기준 | | KB_BRANCH_MANAGER_ROLE | 고객·담보·청구·외부보유 | `CUST_ID / STAKEHOLDER_CHANNEL` | 같은 채널 고객 기준 | | 두 역할 | KB_STAKEHOLDERS | `USER_ID / STAKEHOLDER_SELF` | 토큰 주체 자신의 매핑 행 | `STAKEHOLDER_SELF`와 `STAKEHOLDER_CHANNEL`은 접근 규칙 화면에서 선택 가능한 행 규칙이다. 두 규칙에는 대상 컬럼과 업무 역할값을 함께 저장한다. Bearer Token을 해석할 때 `KB_STAKEHOLDERS`를 한 번만 조회해 `STAKEHOLDER_USER_ID`, `STAKEHOLDER_ROLE`, `STAKEHOLDER_CHANNEL` secure context를 만들고, 이후 `FC_ID`/`FC_CHANNEL`과 `CUST_ID`/`CONTRACT_NO` 조건은 계약원장과 그 context만 사용한다. 허용된 역할별 분기는 **OR**로 합쳐진다. ```sql -- 고객·청구·외부보유의 CUST_ID 예시: 역할별 허용 분기 EXISTS ( SELECT 1 FROM POC_2.KB_CONTRACTS c WHERE c.CUST_ID = <현재행>.CUST_ID AND SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_ROLE') = '설계사' AND c.FC_ID = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID') ) OR EXISTS ( SELECT 1 FROM POC_2.KB_CONTRACTS c WHERE c.CUST_ID = <현재행>.CUST_ID AND SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_ROLE') = '지점장' AND c.FC_CHANNEL = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_CHANNEL') ) ``` 위의 `'설계사'`/`'지점장'`은 함수 하드코딩이 아니라 각 permission rule의 `rule_value`에서 온다. 토큰 원문은 predicate에 노출하지 않으며, `CB_AGENT_BEARER_KEY → KB_STAKEHOLDERS`에서 한 번 검증·해석한 subject context만 사용한다. ## 4. CLS / NULL 처리 아래 컬럼은 `CB_PERMISSION_COLUMN`의 **원문 표시 허용**에 포함되지 않으면 VPD의 `SEC_RELEVANT_COL_OPT=ALL_ROWS`로 NULL 처리한다. 즉, 행 접근과 원문 표시 예외를 같은 접근 규칙 화면에서 분리 관리한다. | 테이블 | 기본 NULL 처리 컬럼 | |---|---| | KB_CUSTOMERS | CUST_NM, RRN_MASKED | | KB_CLAIMS | CLAIM_AMT, PAID_AMT | | KB_EXTERNAL_HOLDINGS | EXT_INSURER, EXT_PRODUCT_GRP, EXT_PRODUCT_TYPE | 초기 예제는 표시 예외를 비워 두므로 지점장·설계사 모두 해당 원문을 보지 못한다. 운영자는 역할별 권한에서 필요한 컬럼만 명시적으로 추가할 수 있다. ## 5. 흐름 ```text KB_STAKEHOLDERS ├─ token issue → CB_AGENT_BEARER_KEY.STAKEHOLDER_USER_ID └─ role sync → CB_APP_USER bridge → CB_USER_ROLE ↓ Bearer Token → CB_AGENT_CTX_PKG → application context ↓ CB_PERMISSION / CB_PERMISSION_RULE ↓ CB_AGENT_DOC_VPD_FILTER + DBMS_RLS ↓ permitted rows / sensitive-column NULL ``` ## 6. 인수 조건 - [x] 토큰 발급 화면은 모든 `KB_STAKEHOLDERS` 사용자를 선택지로 제공하고, 목록에 업무 역할·채널을 표시한다. - [x] 접힌 메뉴를 열지 않아도 상단에서 권한 설정과 토큰 발급 화면으로 이동할 수 있다. - [x] 권한 설정에 설계사·지점장 예제 역할과 7개 원장 권한이 표시된다. - [x] 지점장 역할은 정규화된 동일 채널 계약/고객 범위 규칙으로 등록된다. - [x] 설계사 역할은 `FC_ID = stakeholder user_id`와 담당 고객 범위 규칙으로 등록된다. - [x] 지정 7개 민감 컬럼은 원문 표시 예외가 없으면 NULL 처리하는 VPD column policy가 등록된다. - [x] 토큰 없음·역할 없음·지원하지 않는 rule mapping은 VPD filter에서 `1 = 0`으로 fail-closed 된다. ## 7. 검증 계획 1. Maven 단위/템플릿 테스트로 token subject 선택 및 새 rule type validation을 확인한다. 2. SQLcl에서 지점장/설계사 token context를 설정하고 VPD predicate 및 row count를 비교한다. 3. SQLcl에서 민감 컬럼이 NULL 처리되는지 확인한다. 4. HTTPS 로그인 뒤 `/permissions`, `/tokens`의 상단 바로가기를 확인한다. ### 2026-07-07 배포 검증 결과 - Maven 테스트 78건 통과. - HTTPS 인증 요청으로 `/permissions`, `/tokens`가 각각 200을 반환했다. 기존 `/permissions`의 잘못된 Thymeleaf fallback 표현식도 함께 수정했다. - `CB_APP_USER` bridge 13명, 지점장 역할 2명, 설계사 역할 5명이 동기화됐다. - `POC_2`에 행 VPD 7개와 민감 컬럼 NULL VPD policy 7개, 관련 CLS 함수 7개가 모두 `VALID` 상태로 확인됐다. ## 8. 리스크와 제한 - `CB_APP_USER`는 업무 원장이 아니라 permission engine용 bridge다. 업무 identity의 원천은 반드시 `KB_STAKEHOLDERS`다. - `RLS_FILTER`는 사람이 읽는 설명으로만 보존한다. 저장된 SQL을 실행하면 rule injection 위험이 있으므로 사용하지 않는다. - 지점장의 “집계만” 요구는 원시 행 조회를 완전히 차단하는 별도 집계 View/ORDS endpoint가 필요하다. 이번 예제는 채널 행 범위와 민감 컬럼 NULL 처리까지 구현하며, 집계 전용 endpoint는 후속 범위다.