# 함수 명세: `canAccess` (#617)

> **상태**: Approved design · 구현 Pending · **분류**: 보안 핵심

## 책임

local DDS END USER Context와 기존 `CB_*` 권한 테이블을 사용해 Object·동작·컬럼 그룹·행 속성에 대한 접근을 판단한다. 권한 변경을 사용자별 DDS DDL 재게시 없이 다음 보호 SQL부터 반영한다.

## 호출 형태

```sql
ADMIN.DDS_MCP_AUTHZ_PKG.can_access(
  ORA_END_USER_CONTEXT.username,
  'ADMIN.CUSTOMER',
  'SELECT',
  'CONTACT',
  tenant_id,
  customer_type
)
```

반환값은 `1`(허용) 또는 `0`(거부)이다. 실제 함수 인자와 행 속성은 Object별 Grant가 사용하는 컬럼에 맞춰 제한한다.

## 입력 신뢰 경계

| 입력 | 출처 | 신뢰 규칙 |
|---|---|---|
| END USER 이름 | `ORA_END_USER_CONTEXT.username` | Bearer 검증·Context attach 뒤 DB가 제공한 값만 사용 |
| Object·동작·컬럼 그룹 | DATA GRANT DDL의 문자열 상수 | whitelist 검증 후 provisioning된 값만 사용 |
| 테넌트·분류 등 행 속성 | 보호 Object의 현재 행 컬럼 | 함수 안에서 추가 SQL로 재조회하지 않음 |
| 사용자 ID·권한 값 | `CB_DDS_END_USER_MAP`, `CB_*` | 클라이언트/MCP 인자에서 받지 않음 |

## 알고리즘

1. END USER 이름으로 `CB_DDS_END_USER_MAP`과 활성 `CB_APP_USER`를 찾는다.
2. 사용자 직접 역할과 활성 그룹의 역할을 합친다.
3. 대상 Object/동작/컬럼 그룹에 일치하는 `CB_PERMISSION`을 찾고 ALLOW와 DENY를 평가한다.
4. 권한 규칙과 전달된 행 속성으로 테넌트·부서·본인·상태·태그 범위를 평가한다.
5. 명시적 DENY, 매핑 없음, 비활성 사용자, 권한 없음, 검증 실패, 예외는 `0`을 반환한다.

## DDS DATA GRANT 배치 규칙

- Object/CRUD/컬럼 그룹마다 장기 Grant를 하나 이상 만든다.
- `SELECT (column...)`과 `UPDATE (column...)`의 목록은 정적이며, 민감 컬럼마다 또는 승인 단위 컬럼 그룹마다 분리한다.
- Grant들은 additive이다. 공통 Role에 `AS SELECT` 또는 넓은 컬럼 목록을 만들지 않는다.
- predicate가 같은 보호 Object를 서브쿼리로 읽지 않게 하고, 권한 테이블만 읽는다.

## 실패와 성능

- 패키지는 `AUTHID DEFINER`로 작성하며 권한 테이블 조회 권한은 직접 부여한다.
- 함수 오류는 로그에 안전한 식별자만 남기고 `0`을 반환한다. 오류를 MCP 응답에서 권한 존재 여부로 구분해 노출하지 않는다.
- `DETERMINISTIC`·임의 result cache는 사용하지 않는다. 권한 테이블 변경이 즉시 반영되어야 한다.
- 권한 테이블에는 END USER/user, role, Object, action, column group, active 상태를 시작 열로 하는 인덱스를 둔다. 대량 행 조회 대상은 `EXISTS`/조인 실행 계획을 별도 측정한다.
