1
0
forked from baron/baron-sso

docs: add standalone tenant policy and update keto rebac policy

This commit is contained in:
Lectom C Han
2026-02-20 10:00:55 +09:00
parent 8ed3bd8c77
commit 3e05c4e5f6
2 changed files with 57 additions and 134 deletions

View File

@@ -1,143 +1,34 @@
# Ory Keto ReBAC 정책 및 관계 튜플 가이드
문서는 Baron SSO의 통합 권한 정책을 Ory Keto(Zanzibar 스타일 권한 엔진)에서 구현하기 위한 네임스페이스 설계와 관계 튜플(Relationship Tuples) 예제를 정의합니다.
문서는 Baron SSO의 다형성 테넌트 구조를 지원하기 위해, Google Zanzibar 모델을 차용한 Ory Keto의 네임스페이스 관계 튜플(Relation Tuples) 설계 가이드를 정의합니다.
## 0. 권한 흐름 다이어그램 (Permission Flow)
## 1. 중앙집중형 인가 백본 (Authorization Backbone)
복잡한 다단계 B2B2B 조직도 내에서의 상속 권한은 외부 백엔드의 RDBMS 논리만으로 실시간 검증하기에 과부하가 심합니다.
따라서 조직도나 권한이 변경될 때마다 이를 Keto의 관계 튜플로 실시간 변환/전송하고, 권한 검증(Check)은 초고속 병렬 처리가 가능한 Keto 엔진으로 오프로딩(Offloading)합니다.
```mermaid
graph LR
%% Subjects
U[User: 사용자]
%% Intermediate Groups
subgraph Groups [유저 그룹 / 테넌트 관리 주체]
UG_O[UserGroup: Owners / 그룹장]
UG_M[UserGroup: Members / 멤버]
end
## 2. 네임스페이스 (Namespaces)
과거 혼용되던 `UserGroup` 네임스페이스는 폐기되며, 철저한 권한 통제를 위해 아래 3개의 네임스페이스만 존재합니다.
%% Resources
subgraph Resources [테넌트 및 하위 자원]
T[Tenant: 부모 테넌트]
CT[Tenant: 자식 테넌트]
RP[RelyingParty: 앱/클라이언트]
end
1. **`Tenant`**: 모든 격리 공간 (회사, 지주사, 사내 부서, 개인 워크스페이스)
2. **`RelyingParty`**: 테넌트가 소유하는 자원/앱 (OIDC 클라이언트)
3. **`System`**: 테넌트에 종속되지 않는 전역 권한 (Super Admin 등)
%% Relations (Solid = Direct, Dash = Inherited)
U -- "owner of" --> UG_O
U -- "member of" --> UG_M
UG_O -- "becomes Admin of" --> T
UG_M -- "gets View/Manage of" --> T
T -- "controls" --> CT
T -- "owns" --> RP
%% Effective Permissions (Dash)
U -. "inherits Admin" .-> T
U -. "inherits Access" .-> CT
U -. "can manage" .-> RP
## 3. 관계 튜플 규칙 (Relationship Tuples)
%% Styles
style UG_O fill:#ff9,stroke:#333
style T fill:#dfd,stroke:#333
style RP fill:#bbf,stroke:#333
```
### 3.1 테넌트 계층 및 리더십
- **조직장 임명**: `Tenant:<조직ID>#owners@User:<유저ID>`
- **어드민 자동 상속**: `Tenant:<조직ID>#admins@Tenant:<조직ID>#owners`
- **테넌트 계층(부모-자식)**: `Tenant:<하위ID>#parents@Tenant:<상위ID>`
*(상위 테넌트의 `admins`는 하위 테넌트의 모든 권한을 상속받습니다.)*
## 1. 네임스페이스 정의 (Namespaces)
### 3.2 Relying Party (앱 자원) 제어 및 RP Admin
RP에 별도의 가상 테넌트를 만들지 않고, 자원 객체 자체의 다중 상속을 사용합니다.
- **앱 소유권 지정**: `RelyingParty:<앱ID>#parents@Tenant:<소유테넌트ID>` (소유 테넌트의 최고 관리자가 앱 관리 가능)
- **전담 관리자(RP Admin) 직접 할당**: `RelyingParty:<앱ID>#admins@User:<유저ID>`
- **Private 앱 접근 허용**: `RelyingParty:<앱ID>#access@Tenant:<소유테넌트ID>#members`
- **Public 앱 접근 허용**: `RelyingParty:<앱ID>#access@System:authenticated_users#members`
| 네임스페이스 | 역할 | 비고 |
| :--- | :--- | :--- |
| `Tenant` | 격리된 자원 공간 (Workspace) | 모든 유저 그룹은 테넌트의 한 종류임 |
| `UserGroup` | 사용자의 집합 (Specialized Tenant) | `Tenant` 네임스페이스를 상속하거나 호환됨 |
| `RelyingParty` | OAuth2 클라이언트 앱 | 특정 테넌트에 소속됨 |
| `System` | 시스템 전역 권한 | Super Admin 등을 관리 |
## 2. 핵심 정책의 Keto 구현
### 2.1 "모든 유저 그룹은 테넌트이다"
유저 그룹이 생성될 때, 해당 ID는 `Tenant` 네임스페이스에도 동시에 존재하며 동일한 상속 로직을 공유합니다.
### 2.2 "그룹장은 해당 테넌트의 어드민이다"
그룹장(Leader/Owner) 관계가 형성되면, Keto의 **Subject Set** 기능을 통해 테넌트의 `admins` 권한으로 자동 전파됩니다.
---
## 3. Keto 관계 튜플 예제 (Relationship Tuples)
Keto에 저장되는 데이터 포맷 예시입니다: `namespace:object#relation@subject`
### 3.1 사용자-그룹 관계 (Identity to Group)
| 설명 | Keto 튜플 예제 |
| :--- | :--- |
| **그룹 멤버 추가** | `UserGroup:dev-team#members@User:uuid-123` |
| **그룹장 임명 (Leader)** | `UserGroup:dev-team#owners@User:uuid-leader` |
### 3.2 그룹-테넌트 권한 전파 (Group to Resource)
| 설명 | Keto 튜플 예제 |
| :--- | :--- |
| **그룹장 -> 어드민 자동 상속** | `Tenant:dev-team#admins@UserGroup:dev-team#owners` |
| **그룹 -> 하위 테넌트 관리 권한** | `Tenant:project-alpha#manage@UserGroup:dev-team#members` |
| **그룹 -> 하위 테넌트 조회 권한** | `Tenant:project-beta#view@UserGroup:dev-team#members` |
### 3.3 테넌트-자원 소속 관계 (Hierarchy)
| 설명 | Keto 튜플 예제 |
| :--- | :--- |
| **자식 테넌트 설정** | `Tenant:child-dept#parents@Tenant:parent-corp` |
| **앱(RP) 소속 테넌트 지정** | `RelyingParty:auth-app#parents@Tenant:hanmac-family` |
---
## 4. 권한 검증 로직 (Permission Check Logic)
시스템이 권한을 확인할 때(Check API) 사용하는 로직입니다.
### 4.1 그룹장의 테넌트 관리 권한 확인
사용자 `uuid-leader``dev-team` 테넌트를 관리할 수 있는지 확인:
```bash
# 요청 (Check)
GET /relation-tuples/check?namespace=Tenant&object=dev-team&relation=manage&subject_id=uuid-leader
# Keto 내부 추론 경로
1. Tenant:dev-team#manage 권한은 Tenant:dev-team#admins에게 있음.
2. Tenant:dev-team#admins는 UserGroup:dev-team#owners를 포함함.
3. UserGroup:dev-team#owners에 User:uuid-leader가 존재함.
=> 결과: ALLOW (True)
```
### 4.2 그룹 멤버의 하위 자원(RP) 접근 확인
사용자 `uuid-123``auth-app` 설정을 볼 수 있는지 확인:
```bash
# 요청 (Check)
GET /relation-tuples/check?namespace=RelyingParty&object=auth-app&relation=view&subject_id=uuid-123
# Keto 내부 추론 경로
1. RelyingParty:auth-app#view 권한은 부모인 Tenant:hanmac-family#view에 의존함.
2. Tenant:hanmac-family#view 권한은 UserGroup:dev-team#members에게 부여됨.
3. UserGroup:dev-team#members에 User:uuid-123이 존재함.
=> 결과: ALLOW (True)
```
## 5. 정책 요약 코드 (Namespace Config - DSL Style)
이 정책을 지원하기 위한 Keto 네임스페이스 설정 스키마 개념도입니다.
```typescript
class Tenant implements Namespace {
related: {
admins: (User | UserGroup#owners)[]
members: (User | UserGroup#members)[]
parents: Tenant[]
}
permits: {
view: (ctx: Context): boolean =>
this.related.members.includes(ctx.subject) ||
this.related.admins.includes(ctx.subject) ||
this.related.parents.traverse((p) => p.permits.view(ctx)),
manage: (ctx: Context): boolean =>
this.related.admins.includes(ctx.subject) ||
this.related.parents.traverse((p) => p.permits.manage(ctx))
}
}
```
## 4. 트랜잭셔널 아웃박스를 통한 정합성 확보
Keto와의 데이터 일관성 문제는 시스템의 치명적인 아킬레스건입니다.
백엔드 DB에서 테넌트나 멤버십을 변경할 때, Keto API 직접 호출에 따른 부분 실패(Partial Failure)를 방지하기 위해 **트랜잭셔널 아웃박스(Transactional Outbox) 패턴**을 반드시 사용해야 합니다.
DB 커밋과 함께 아웃박스 테이블에 튜플 이벤트를 기록하고, 비동기 릴레이 워커가 Keto로 동기화를 보장합니다. Soft delete 발생 시 즉각적으로 Keto의 튜플을 Hard delete 하여 권한을 회수합니다.

32
docs/tenant-policy.md Normal file
View File

@@ -0,0 +1,32 @@
# 테넌트 기본 아키텍처 및 데이터 정책 (Tenant Architecture Policy)
본 문서는 Baron SSO의 테넌트 다형성(Polymorphism) 모델과 데이터베이스 스키마, 그리고 외부 시스템(Kratos)과의 역할 분리에 대한 핵심 아키텍처 정책을 정의합니다.
## 1. 테넌트 다형성 모델 (Polymorphic Tenant)
모든 형태의 격리 공간은 `Tenant`라는 단일 개념(Base Unit)으로 취급되며, 데이터베이스의 `type` 속성을 통해 그 역할이 구분됩니다.
- **`PERSONAL`**: B2C 개인 워크스페이스. 개인 유저 가입 시 1:1로 자동 생성됩니다.
- **`COMPANY`**: B2B 법인/기업. 독립적인 비즈니스와 사내 조직도를 가집니다.
- **`COMPANY_GROUP`**: B2B2B 지주사/그룹사. 여러 `COMPANY`를 하위로 거느리며 권한을 통합합니다.
- **`USER_GROUP`**: 사내 조직 (본부/팀 등). 과거에는 분리된 개념이었으나, 현재는 완벽한 통합을 위해 테넌트의 한 종류로 1:1 매핑됩니다.
## 2. 외부 백엔드 데이터베이스 의무 채택 (Separation of SoT)
Kratos 내부 트레이트(Traits)에 테넌트, 직급 등 관계형 데이터를 저장하는 것은 토큰 비대화 및 쿼리 성능 저하를 일으키는 안티 패턴입니다. 따라서 데이터의 진실 공급원(SoT)을 철저히 분리합니다.
- **Ory Kratos (Identity SoT)**: 이메일, 패스워드 등 순수 식별 정보만 저장합니다.
- **PostgreSQL (Business SoT)**: 반드시 커스텀 외부 백엔드 DB를 구축하여, 테넌트의 트리 구조, 사용자 직급, 애플리케이션 설정 등을 전담하여 관리합니다.
## 3. 데이터베이스 스키마 분리 전략
테넌트 테이블의 비대화를 막기 위해, Identity(신분증) 역할과 무거운 Business 데이터를 분리 조인(Join)합니다.
- **`tenants` 테이블**: 최소한의 식별 정보(`id`, `name`, `type`)만 저장하는 초경량 베이스 테이블.
- **`company_settings` 테이블**: `COMPANY``COMPANY_GROUP` 타입 전용 무거운 비즈니스 설정 (결제 정보, 커스텀 도메인 등).
- **`user_groups` 테이블**: `USER_GROUP` 타입 전용 사내 조직도 메타데이터 (`parent_id`, 조직장 정보 등).
## 4. 논리적 다중 테넌트 OIDC 관리 (Logical Pooling)
인프라 비용의 팽창을 막기 위해 테넌트별로 Hydra(OAuth2) 데이터베이스를 물리적으로 복제하는 방식은 금지합니다.
대신 공유되는 소수의 Hydra 클러스터 앞단에 도메인 및 헤더를 재작성하는 지능형 프록시를 배치하고, 백엔드의 동의(Consent) 로직을 통해 요청된 클라이언트의 테넌트 맥락에 맞는 **동적 클레임(Dynamic Claim)**을 ID Token에 주입합니다.