Files
MyDoc/tenant-usergroup-policy.md

164 lines
9.8 KiB
Markdown

# 통합 테넌트 및 권한 아키텍처 정책 (Integrated Tenant & ReBAC Policy)
이 문서는 Baron SSO 시스템 내 B2C(개인)부터 B2B(단일기업), B2B2B(그룹사) 및 사내 조직도(`UserGroup`)에 이르는 모든 격리 공간과 권한 상속을 다루는 **다형성 테넌트(Polymorphic Tenant)**의 공식 정책을 정의합니다.
연구 결과에 기반하여, 단순한 B2C 수준을 넘어 복잡한 테넌트 생태계를 안정적이고 고성능으로 지원하기 위해 **외부 DB 의무 채택, 트랜잭셔널 아웃박스 동기화, Keto 기반 인가 백본, 논리적 다중 테넌트 OIDC 관리**라는 4대 핵심 아키텍처 원칙을 준수합니다.
---
## 1. 기본 원칙 (Core Axioms)
### 1.1 "모든 격리 공간은 테넌트이다" (Everything is a Tenant)
- 개인 워크스페이스, 기업 고객, 지주사, 그리고 사내의 특정 팀이나 본부(`UserGroup`) 등 **모든 종류의 격리 공간은 최상위 범용 단위인 `Tenant`로 취급**됩니다.
- 과거에 혼용되던 `UserGroup` 네임스페이스는 폐기되며, 격리 공간을 나타내는 Keto(ReBAC) 네임스페이스는 `Tenant` 하나로 단일화됩니다. 권한 상속 로직은 테넌트의 성격과 무관하게 동일하게 동작합니다.
### 1.2 "권한 주체와 자원의 분리" (Namespaces)
시스템의 완벽한 권한 통제를 위해, Keto에는 `Tenant` 외에도 다음과 같은 필수 네임스페이스가 존재합니다.
- **`Tenant`:** 격리된 공간 (회사, 부서, 개인)
- **`RelyingParty`:** 테넌트가 소유하는 자원/앱 (OIDC 클라이언트)
- **`System`:** 테넌트에 종속되지 않는 전역 권한 (Super Admin 등)
### 1.3 소유(Ownership)와 가시성(Visibility)의 분리
- 시스템의 모든 자원(예: RelyingParty, 앱)은 반드시 특정 `Tenant`가 소유(`manage`)합니다.
- 그러나 자원의 소유권과 **누가 접근할 수 있는가(가시성, `access`)는 별개**입니다. 내부망용 앱(Private)과 대국민 서비스(Public)를 동일한 기업(Tenant)이 동시에 소유하고 제어할 수 있습니다.
### 1.4 Ory SSOT와 Backend read model 분리
사용자 데이터를 Kratos의 내부 트레이트(Traits)에 무분별하게 저장하는 것은 안티 패턴입니다. 이는 토큰 비대화와 쿼리 성능 저하를 초래합니다.
- **Kratos (Identity):** "누구인가?" (인증, 이메일, 패스워드 등 순수 식별 정보). 테넌트, 직급 등 관계형 데이터는 절대 보관하지 않습니다.
- **Backend DB read model:** Ory에 저장되지 않거나 Ory API로 필요한 조회가 불가능한 조직 표시/검색 metadata, 테넌트 설정, 외부 연동 상태만 보관합니다.
- **Keto (ReBAC Authorization Backbone):** "무엇을 할 수 있는가?" (권한 및 상속).
---
## 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. 중앙집중형 인가 백본 (Keto ReBAC Tuples)
복잡한 다단계 B2B2B 조직도 내에서의 상속 권한을 외부 RDBMS 논리만으로 실시간 검증하는 것은 과부하를 초래합니다. 따라서 조직도 변경 시 해당 데이터를 즉시 Keto의 관계 튜플로 변환하여 전송하고, **인가 검증(Check)은 초고속 병렬 처리가 가능한 Keto 엔진으로 오프로딩**합니다.
### 3.1 조직장(Leader)과 어드민(Admin) 상속
특정 부서(테넌트)의 그룹장(`owners`)으로 임명되면, 해당 부서 및 그 하위 부서의 최고 관리자(`admins`) 권한을 자동으로 상속받습니다.
- **조직장 임명:** `Tenant:<조직ID>#owners@User:<유저ID>`
- **자동 상속 룰:** `Tenant:<조직ID>#admins@Tenant:<조직ID>#owners`
### 3.2 계층 간 권한 상속 (Hierarchy)
지주사 $\rightarrow$ 법인 $\rightarrow$ 사내조직 간의 상속은 모두 `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
%% Types
subgraph COMPANY_GROUP [지주사 테넌트]
CG[Tenant: 한맥 그룹]
end
subgraph COMPANY [법인 테넌트]
C1[Tenant: 한맥 IT]
end
subgraph USER_GROUP [사내조직 테넌트]
UG1[Tenant: 개발본부]
UG2[Tenant: 클라우드팀]
end
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 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
```
---
## 5. 지능형 프록시를 통한 논리적 다중 테넌트 OIDC 관리
인프라 비용 팽창을 막기 위해 테넌트별로 Hydra(OAuth2/OIDC) 클러스터를 물리적으로 복제하는 것은 지양합니다.
### 5.1 Logical Pooling for OAuth2
- 모든 테넌트(`COMPANY`, `PERSONAL`)는 소수의 공유된 Hydra 클러스터를 사용합니다.
- Hydra 클러스터 앞단에 도메인 및 헤더를 재작성하는 지능형 프록시(API Gateway)를 배치하여, 테넌트별로 물리적으로 분리된 것과 같은 라우팅 효과를 제공합니다.
### 5.2 동적 클레임 조립 (Dynamic Claim Assembly)
- 로그인 및 동의(Consent) 흐름의 프로토콜 원장은 Ory Hydra입니다.
- 백엔드는 Ory에서 확인한 identity/relationship과 허용된 read model을 조합해 요청된 클라이언트(RP)의 테넌트 맥락(Context)을 계산하고, Hydra에 전달할 claim을 조립합니다.
---
## 6. 분산 시스템 동기화 무결성 확보 (Data Consistency)
Kratos 웹훅 통신 지연이나 이중 쓰기(Dual-Write) 오류로 인한 '고아 레코드(Ghost Identity)'를 원천 차단해야 합니다.
### 6.1 빠른 제어권 반환 및 비동기 처리 혼합 (Kratos $\rightarrow$ DB)
- **가입 웹훅(after_registration):** Kratos에서 웹훅 요청이 오면, 백엔드는 국소 DB 트랜잭션을 열어 최소한의 필수 식별자 데이터만 `users` 테이블에 커밋하고 **즉각적으로 200 OK를 응답하여 Kratos에 제어권을 반환**합니다. (가입 중단 방지)
- 웰컴 이메일 발송, 초기 조직도 프로비저닝 등 무거운 작업은 즉시 처리하지 않고 메시지 큐(Message Queue)나 비동기 워커로 넘겨 후속 처리합니다.
### 6.2 트랜잭셔널 아웃박스 패턴 (DB $\rightarrow$ Keto)
- 백엔드 로직에 의해 테넌트나 권한이 변경될 때, Keto API를 직접 호출하면 네트워크 오류 시 부분 실패(Partial Failure)가 발생할 수 있습니다.
- 이를 방지하기 위해 DB 커밋 시 동일 트랜잭션 내에서 `keto_outbox` 테이블에 튜플 변경 이벤트를 기록합니다. 이후 CDC(Change Data Capture) 로직이나 릴레이 워커를 통해 비동기로 시스템 간 동기화를 진행하여 응답 속도와 정합성을 동시에 달성합니다.
### 6.3 삭제 정책 (Cascade) 및 정기 대사
- **즉시 회수:** 백엔드 DB에서 Soft Delete(`deleted_at`)가 발생하면, Outbox 워커는 지연 없이 즉각적으로 Keto의 튜플을 Hard Delete 합니다.
- **정기 대사 (Reconciliation):** Kratos(Identity), PostgreSQL(DB), Keto(ReBAC) 3자 간의 불일치(고아 튜플, 누락된 멤버십 등)를 매일 1회 이상 배치 크론 잡을 통해 능동적으로 색출하고 자동 복구/삭제합니다.