본문으로 건너뛰기

Ontology + Physical DataGrant Fall-through — M4 Step 4 (#1250)

본 가이드는 Epic #1246 M4 종결 시점의 통합 ABAC engine — evaluate_combined_access — 동작을 다룹니다. M3 Step 1 의 ontology Class grant 평가 위에, 물리 데이터셋의 DataGrant 까지 fall-through 평가해 "ontology 가 비어 있어도 물리 권한이 있으면 통과" 하는 Unity Catalog 패턴 일관성을 확보합니다.

UI 진입점

/ontology 페이지에 Class 목록이 표시되고, 각 Class 에 정의된 data_source 가 fall-through 평가의 입력이 됩니다.

Ontology landing page (AKS prod)

물리 DataGrant/admin/policies 의 "접근 정책" 탭에서 관리합니다 (행 필터·컬럼 마스킹·역할별).

Access Policies — DataGrant management

왜 fall-through 가 필요한가

시나리오M3 Step 1 (ontology-only)M4 Step 4 (combined)
ontology grant 명시 allowallowallow (즉시)
ontology grant 명시 denydenydeny (즉시)
ontology grant 0건 + 물리 grant 있음role-based fall-through (ALLOW_NO_GRANT) — 운영 환경에서 의도와 무관하게 허용물리 DataGrant 매치 검사 후 결정
ontology grant 0건 + 물리 grant 0건role-based ALLOW_NO_GRANT (위험)fail-closed deny

운영 환경에서 ontology 메타데이터를 점진 도입할 때, ontology grant 가 비어 있다고 무조건 통과시키면 안전 정책이 무력화됩니다. M4 Step 4 는 물리 권한이라는 두 번째 방어선을 추가합니다.

평가 순서

핵심: 명시 정책 우선 — ontology layer 에서 allow / deny 가 명시되면 물리 layer 평가하지 않음.

코드 예시

from gend_api.services.abac import evaluate_combined_access

decision = await evaluate_combined_access(
db, class_row, caller, action="read",
)
if not decision.allow:
raise HTTPException(403, f"Access denied for class {class_row.name}")
# decision.decision_kind ∈ {
# "allow", "deny", "no_grant", "role_fallback",
# "physical_fallback_allow", "physical_fallback_deny",
# }
# decision.evaluated_layers ∈ {("ontology",), ("ontology", "physical")}

OntologyClass.data_source 와 DataGrant 매핑

OntologyClass.data_source (String 512) 는 catalog.schema.table 형식 (예: iceberg.lng.equipment_tag). 물리 fall-through 는 이 좌표를 기준으로 DataGrant 테이블을 검색.

DataGrant 계층 (Unity Catalog 패턴) — schema_name="*" / table_name="*" 와일드카드 사용:

grantee_typegrantee_nameobject_typecatalogschematableprivilege효과
groupopscatalogiceberg**SELECTiceberg.*.* read 권한 — iceberg.lng.equipment_tag 도 cover
useralicetableiceberglngequipment_tagALL특정 테이블만 전권
roledata_engineerschemaiceberglng*INSERTiceberg.lng.* write 권한

grantee_name 매칭: caller.user_idcaller.groupscaller.roles 순. 하나라도 매치되면 subject 인정.

action → physical privilege 매핑

ontology action매치되는 DataGrant.privilege
readSELECT, USE, ALL
writeINSERT, ALL
deleteALL

메트릭

gend_ontology_grant_evaluations_total{decision} Counter 라벨에 두 시리즈 추가 (M3 Step 2 의 allow/deny/no_grant/role_fallback 위):

  • physical_fallback_allow — ontology no_grant 였지만 물리 grant 로 allow
  • physical_fallback_deny — ontology no_grant + 물리에서도 priv 부족/grantee mismatch
  • deny 라벨에는 fail-closed deny (양쪽 layer 모두 매핑 0건) 도 합산됨

Grafana 에서 fall-through 사용 비율을 모니터링:

rate(gend_ontology_grant_evaluations_total{decision=~"physical_fallback_.*"}[5m])
/
rate(gend_ontology_grant_evaluations_total[5m])

이 비율이 높으면 ontology grant 가 충분히 정의되지 않은 상태 — ontology layer 마이그레이션 진척률 지표로 활용.

Fail-closed 정책

다음 케이스는 모두 403 Forbidden + deny 메트릭:

  1. ontology grant 0건 + Class.data_source 설정됨 + 물리 DataGrant 0건
  2. ontology grant 0건 + Class.data_source 형식 깨짐 (parts ≠ 3)
  3. ontology grant 0건 + 물리 평가 중 예외 발생 (logger.warning + deny)

마이그레이션 중 안전을 위해 — Class.data_sourceNone 일 때만 ontology layer 의 no_grant (= role-based ALLOW_NO_GRANT) 가 유지됩니다.

라우터 통합

apps/api/src/gend_api/routers/ontology.py_enforce_class_abac 헬퍼가 M4 Step 4 부터 evaluate_class_access 대신 evaluate_combined_access 호출. Class CRUD / instances / properties 모든 endpoint 가 자동으로 fall-through 평가 받음.

async def _enforce_class_abac(db, class_row, caller, *, action):
decision = await evaluate_combined_access(
db, class_row, caller, action=action,
)
if not decision.allow:
raise HTTPException(403, ...)

테스트

  • 단위: apps/api/tests/test_ontology_combined_abac.py — 13 cases (decision matrix 전수)
  • 회귀: tests/test_ontology_abac.py, tests/test_ontology_router.py — ontology-only 동작 유지 확인
  • LNG 시나리오: tests/test_ontology_lng_scenario.py — admin/viewer 통합 시나리오

후속

  • ontology grant + 물리 DataGrant 충돌 시 차이 알림 (drift detector)
  • Class.data_source 자동 검증 CLI (gend onto data-source --validate)
  • physical fall-through Grafana 패널 (ontology-fall-through.json)

관련 문서