forked from baron/baron-sso
docs: add standalone tenant policy and update keto rebac policy
This commit is contained in:
@@ -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
32
docs/tenant-policy.md
Normal 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에 주입합니다.
|
||||
Reference in New Issue
Block a user