1
0
forked from baron/baron-sso

docs: update integrated tenant and ReBAC policy

This commit is contained in:
Lectom C Han
2026-02-20 08:35:52 +09:00
parent 4c9f71147c
commit bc5a18bc1e

View File

@@ -1,68 +1,154 @@
# 유저 그룹 및 테넌트 통합 권한 정책 (Integrated Policy)
# 통합 테넌트 권한 아키텍처 정책 (Integrated Tenant & ReBAC Policy)
이 문서는 Baron SSO의 테넌트(Tenant)와 유저 그룹(User Group) 간의 관계 및 권한 상속에 관한 공식 정책을 정의합니다.
이 문서는 Baron SSO 시스템 내 B2C(개인)부터 B2B(단일기업), B2B2B(그룹사) 및 사내 조직도(`UserGroup`)에 이르는 모든 격리 공간과 권한 상속을 다루는 **다형성 테넌트(Polymorphic Tenant)**의 공식 정책을 정의합니다.
이 정책은 기존의 단순 `Tenant``UserGroup` 모델을 발전시켜, Ory Kratos(Identity), PostgreSQL(Business), Ory Keto(ReBAC) 3개의 분산 시스템 간 역할을 명확히 하고 데이터 정합성을 강제합니다.
---
## 1. 기본 원칙 (Core Axioms)
### 1.1 유저 그룹의 테넌트성 (User Group as a Tenant)
- **모든 테넌트가 유저 그룹은 아니지만, 모든 유저 그룹은 반드시 테넌트의 속성을 가집니다.**
- 유저 그룹은 "사용자들의 집합"인 동시에, 그 자체가 권한을 담고 다른 자원을 소유할 수 있는 **격리된 공간(Tenant)**으로 취급됩니다.
### 1.1 "모든 격리 공간은 테넌트이다" (Everything is a Tenant)
- 개인 워크스페이스, 기업 고객, 지주사, 그리고 사내의 특정 팀이나 본부(`UserGroup`) 등 **모든 종류의 격리 공간은 최상위 범용 단위인 `Tenant`로 취급**됩니다.
- 과거에 혼용되던 `UserGroup` 네임스페이스는 폐기되며, 격리 공간을 나타내는 Keto(ReBAC) 네임스페이스는 `Tenant` 하나로 단일화됩니다. 권한 상속 로직은 테넌트의 성격과 무관하게 동일하게 동작합니다.
### 1.2 권한 상속 로직의 단일화 (Unified Inheritance)
- 테넌트 간의 상속(Parent-Child Tenant)과 유저 그룹의 권한 전파(Group-Member)는 **기술적으로 동일한 ReBAC 로직**을 사용합니다.
- `UserGroup:members` 관계는 `Tenant:members`와 동일한 우선순위를 가지며, 시스템은 이를 구분 없이 하나의 상속 트리로 처리합니다.
### 1.2 "권한 주체와 자원의 분리" (Namespaces)
시스템의 완벽한 권한 통제를 위해, Keto에는 `Tenant` 외에도 다음과 같은 필수 네임스페이스가 존재합니다.
- **`Tenant`:** 격리된 공간 (회사, 부서, 개인)
- **`RelyingParty`:** 테넌트가 소유하는 자원/앱 (OIDC 클라이언트)
- **`System`:** 테넌트에 종속되지 않는 전역 권한 (Super Admin 등)
### 1.3 그룹장-어드민 연동 (Leader-Admin Mapping)
- 특정 유저 그룹에 명시적으로 **'그룹장(Group Leader)'**을 지정하면, 시스템은 해당 사용자를 해당 유저 그룹(테넌트)의 **'테넌트 어드민(Tenant Admin)'**으로 자동 격상합니다.
-룹장은 해당 그룹이 소유한 모든 하위 테넌트 및 리소스에 대해 완전한 제어권을 가집니다.
### 1.3 소유(Ownership)와 가시성(Visibility)의 분리
- 시스템의 모든 자원(예: RelyingParty, 앱)은 반드시 특정 `Tenant`가 소유(`manage`)합니다.
-러나 자원의 소유권과 **누가 접근할 수 있는가(가시성, `access`)는 별개**입니다. 내부망용 앱(Private)과 대국민 서비스(Public)를 동일한 기업(Tenant)이 동시에 소유하고 제어할 수 있습니다.
## 2. 권한 흐름도 (Mermaid)
### 1.4 단일 진실 공급원 분리 (Separation of SoT)
- **Kratos (Identity):** "누구인가?" (인증, 이메일, 패스워드 등 순수 식별 정보). 테넌트, 직급 등 관계형 데이터는 절대 보관하지 않습니다.
- **PostgreSQL (Business):** "어디에 속하며 조직 구조는 어떠한가?" (직급, 조직도, 테넌트 설정 등).
- **Keto (ReBAC):** "무엇을 할 수 있는가?" (권한 및 상속).
---
## 2. 하이브리드(다형성) 테넌트 아키텍처
데이터베이스의 `tenants` 테이블은 가장 가벼운 신분증(Identity) 역할을 수행하며, 세부 비즈니스 설정은 타입별로 별도의 테이블에 1:1 조인(Join)하여 관리합니다.
### 2.1 테넌트 유형 (Tenant Types)
| 타입 (Enum) | 설명 | 특징 및 1:1 조인 테이블 |
| :--- | :--- | :--- |
| `PERSONAL` | B2C 개인 워크스페이스 | 일반 사용자 가입 시 1:1로 생성. 조직도(UserGroup) 기능 비활성화. |
| `COMPANY` | B2B 일반 기업/법인 | 독립된 비즈니스. 사내 조직도를 가짐. 무거운 설정은 `company_settings`에 저장. |
| `COMPANY_GROUP`| B2B2B 지주사/그룹사 | 여러 `COMPANY`를 하위로 거느리며 최고 관리자 권한을 통합. `company_settings` 조인. |
| `USER_GROUP` | 사내 조직 (본부/팀 등) | `COMPANY` 내부에 속하는 조직. 사내 조직도 메타데이터는 `user_groups` 테이블에 저장. |
---
## 3. ReBAC 권한 상속 및 관계 튜플 (Keto Tuples)
이전에는 `UserGroup` 네임스페이스가 존재했으나, 이제 모든 권한 검증은 오직 `Tenant` 네임스페이스 내에서 처리됩니다.
### 3.1 조직장(Leader)과 어드민(Admin) 상속
특정 부서(테넌트)의 그룹장(`owners`)으로 임명되면, 해당 부서 및 그 하위 부서의 최고 관리자(`admins`) 권한을 자동으로 상속받습니다.
- **조직장 임명:** `Tenant:<조직ID>#owners@User:<유저ID>`
- **자동 상속 룰:** `Tenant:<조직ID>#admins@Tenant:<조직ID>#owners`
### 3.2 계층 간 권한 상속 (Hierarchy)
지주사(COMPANY_GROUP) $\rightarrow$ 법인(COMPANY) $\rightarrow$ 사내조직(USER_GROUP) 간의 상속은 모두 `parents` 튜플을 사용합니다.
- **계층 설정:** `Tenant:<하위ID>#parents@Tenant:<상위ID>`
- **자동 상속 룰:** 상위 테넌트의 `admins`는 하위 테넌트의 `manage``view` 권한을 모두 가집니다.
### 3.3 리소스 제어 및 RP Admin (Relying Party)
RP(앱)는 별도의 가상 테넌트를 만들지 않고 자원(Object) 자체의 다중 상속을 통해 권한을 제어합니다.
- **앱 관리 (Manage):**
- `RelyingParty:<앱ID>#parents@Tenant:<소유테넌트ID>` (앱 소유권 지정. 해당 테넌트의 최고 관리자가 앱을 관리할 수 있음)
- `RelyingParty:<앱ID>#admins@User:<유저ID>` (특정 유저를 **RP Admin**으로 직접 지정)
- **Private 앱 접근 (Access):** `RelyingParty:<앱ID>#access@Tenant:<소유테넌트ID>#members` (소유한 회사의 멤버만 접근)
- **Public 앱 접근 (Access):** `RelyingParty:<앱ID>#access@System:authenticated_users#members` (전역 인증 유저 누구나 접근)
---
## 4. 권한 흐름도 (Mermaid)
```mermaid
graph TD
%% Roles
Leader[Group Leader / 그룹장]
Member[Group Member / 멤버]
%% Entities (Polymorphic)
subgraph UG_T [User Group / Specialized Tenant]
UG_ID[Group: Hanmac 운영팀]
%% Types
subgraph COMPANY_GROUP [지주사 테넌트]
CG[Tenant: 한맥 그룹]
end
subgraph Child_T [Child Tenants / 하위 테넌트]
T1[Tenant: 한맥 엔지니어링]
T2[Tenant: 한맥 IT]
subgraph COMPANY [법인 테넌트]
C1[Tenant: 한맥 IT]
end
%% Policy Links
Leader -- "Explicitly Assigned" --> UG_ID
Leader -. "Automatically Becomes" .-> Admin[Tenant Admin]
Member -- "is member of" --> UG_ID
%% Inheritance (Identical Logic)
UG_ID -- "Inherits Access To" --> T1
UG_ID -- "Inherits Access To" --> T2
subgraph USER_GROUP [사내조직 테넌트]
UG1[Tenant: 개발본부]
UG2[Tenant: 클라우드팀]
end
%% Effective Access
Admin -- "Full Control" --> UG_ID
Member -- "Shared Access" --> T1
Member -- "Shared Access" --> T2
subgraph USERS [사용자]
CEO[User: 그룹 최고경영자]
DEV_L[User: 팀장]
DEV[User: 팀원]
RP_ADM[User: RP 전담 관리자]
end
subgraph RESOURCES [자원]
RP1[RelyingParty: 사내 인트라넷]
end
%% Hierarchy Tuples
C1 -- "parents" --> CG
UG1 -- "parents" --> C1
UG2 -- "parents" --> UG1
RP1 -- "parents" --> C1
%% Memberships
CEO -- "owners" --> CG
DEV_L -- "owners" --> UG2
DEV -- "members" --> UG2
%% RP Admin
RP_ADM -- "admins" --> RP1
%% Effective Admin Control (Inherited)
CEO -. "Inherits Admin Control" .-> C1
CEO -. "Inherits Admin Control" .-> UG1
CEO -. "Inherits Admin Control" .-> UG2
CEO -. "Inherits Manage" .-> RP1
DEV_L -. "Inherits Admin Control" .-> UG2
%% Styles
style UG_ID fill:#f9f,stroke:#333,stroke-width:2px
style Leader fill:#ff9,stroke:#333
style Admin fill:#ffd,stroke:#333,stroke-dasharray: 5 5
style CG fill:#dfd,stroke:#333
style C1 fill:#dfd,stroke:#333
style UG1 fill:#f9f,stroke:#333
style UG2 fill:#f9f,stroke:#333
style CEO fill:#ff9,stroke:#333
style DEV_L fill:#ff9,stroke:#333
style RP_ADM fill:#ff9,stroke:#333
style RP1 fill:#bbf,stroke:#333
```
## 3. 기술적 구현 가이드 (Implementation)
---
### 3.1 Keto Relationship Tuples
- **그룹장 임명:** `UserGroup:<ID>#owners@User:<UserID>`
- **어드민 자동 승격:** `Tenant:<ID>#admins@UserGroup:<ID>#owners` (그룹 소유자는 해당 테넌트의 어드민)
- **멤버십:** `UserGroup:<ID>#members@User:<UserID>`
## 5. 분산 시스템 정합성 강제 정책 (Data Consistency Check)
### 3.2 기대 효과
- **정책 단순화:** '어드민'과 '그룹장'을 별도로 관리할 필요가 없어 시스템 복잡도가 감소합니다.
- **책임 명확화:** 그룹의 장이 해당 자원의 최종 책임자가 되는 직관적인 거버넌스를 수립합니다.
- **일관된 UX:** 사용자는 자신이 관리하는 것이 '테넌트'인지 '그룹'인지 고민할 필요 없이 동일한 관리 도구를 사용합니다.
인증(Kratos), 비즈니스 로직(PG), 권한(Keto)이 어긋나지 않도록 다음의 안전 장치를 반드시 구현해야 합니다.
### 5.1 트랜잭셔널 아웃박스 패턴 (Transactional Outbox)
- **목적:** 백엔드 DB에는 저장되었으나, 일시적인 네트워크 장애로 인해 Keto(ReBAC)에 권한이 저장되지 않는 '부분 실패(Partial Failure)' 방지.
- **방식:** DB에 새로운 테넌트(`UserGroup` 등) 생성 시, 동일 DB 트랜잭션 내에서 `keto_outbox` 테이블에 튜플 이벤트를 함께 저장합니다. 이후 Background Worker가 이를 안전하게 Keto로 동기화(재시도 보장)합니다.
### 5.2 삭제 정책 연동 (Delete Cascade)
- **보안 원칙 (즉시 회수):** 백엔드에서 테넌트나 유저를 Soft Delete(`deleted_at`) 처리하는 즉시, Outbox를 통해 **Keto의 관련된 모든 튜플을 Hard Delete**하여 권한을 즉각적으로 회수합니다.
- **Kratos 계정 삭제:** 사용자가 계정을 영구 탈퇴할 경우, Kratos에서 데이터가 지워지기 직전에 백엔드로 Webhook을 전송하여 Keto 권한 및 로컬 `users` 테이블의 프로필 데이터를 먼저 비우도록 연동합니다.
### 5.3 Kratos-Backend 실시간 동기화 (Webhooks)
- Kratos에서의 회원 가입(`after_registration`) 및 정보 변경(`after_settings`) 발생 시, 즉시 Webhook을 발생시켜 Backend의 `users` 테이블(프로필 로컬 캐시)을 Upsert 합니다.
### 5.4 정기 대사 스크립트 (Reconciliation Cron Job)
- 매일 1회 이상 배치 작업을 돌려 분산 시스템 간 엣지 케이스 오류를 복구합니다.
- **검증 항목:**
1. Keto에 남아있는 고아 튜플(Orphaned Tuples) 검출 및 자동 삭제.
2. DB의 `user_groups` 멤버십에는 존재하나 Keto 튜플에서 누락된 권한 색출 및 복원.
3. Kratos와 Backend 간 유저 ID 매핑 상태 일치 여부 스캔.