1
0
forked from baron/baron-sso

feat: 구현: 유저 그룹 중심 권한 통합 및 미들웨어 정책 고도화

This commit is contained in:
2026-02-13 14:16:13 +09:00
parent b9ad54d459
commit 594fd24adb
37 changed files with 2611 additions and 1564 deletions

View File

@@ -0,0 +1,143 @@
# Ory Keto ReBAC 정책 및 관계 튜플 가이드
이 문서는 Baron SSO의 통합 권한 정책을 Ory Keto(Zanzibar 스타일 권한 엔진)에서 구현하기 위한 네임스페이스 설계와 관계 튜플(Relationship Tuples) 예제를 정의합니다.
## 0. 권한 흐름 다이어그램 (Permission Flow)
```mermaid
graph LR
%% Subjects
U[User: 사용자]
%% Intermediate Groups
subgraph Groups [유저 그룹 / 테넌트 관리 주체]
UG_O[UserGroup: Owners / 그룹장]
UG_M[UserGroup: Members / 멤버]
end
%% Resources
subgraph Resources [테넌트 및 하위 자원]
T[Tenant: 부모 테넌트]
CT[Tenant: 자식 테넌트]
RP[RelyingParty: 앱/클라이언트]
end
%% 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
%% Styles
style UG_O fill:#ff9,stroke:#333
style T fill:#dfd,stroke:#333
style RP fill:#bbf,stroke:#333
```
## 1. 네임스페이스 정의 (Namespaces)
| 네임스페이스 | 역할 | 비고 |
| :--- | :--- | :--- |
| `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))
}
}
```

View File

@@ -18,9 +18,9 @@
- Keto의 관계 튜플에 기반해 `CheckPermission`을 수행합니다.
### 2.3 RequireTenantMatch
- 테넌트 관리자 권한을 가진 사용자가 **자신의 테넌트**에만 접근하도록 보장합니다.
- Super Admin은 즉시 통과합니다.
- API Key 인증은 우회합니다.
- 사용자가 요청한 테넌트에 대한 관리 자격이 있는지 검증합니다.
- **상속 권한 인정:** 사용자의 기본 테넌트뿐만 아니라, 유저 그룹 멤버십이나 그룹장 직책을 통해 **상속받은 모든 테넌트**를 대상으로 합니다.
- Super Admin 및 유효한 API Key 요청은 통과합니다.
## 3. ReBAC 기반인데도 RBAC가 필요한 이유
@@ -32,7 +32,7 @@
- 불필요한 ReBAC 호출을 줄여 장애 전파를 줄입니다.
3) **테넌트 범위 제어의 명확성**
- "Tenant Admin은 자기 테넌트만"은 자주 쓰는 규칙으로, 미들웨어 단에서 즉시 판단이 효율적입니다.
- "Tenant Admin은 자기 테넌트만"은 자주 쓰는 규칙으로, 미들웨어 단에서 즉시 판단이 효율적입니다. 유저 그룹 도입 이후에는 "상속받은 모든 관리 대상 테넌트"로 범위가 확장됩니다.
4) **성능 및 안정성**
- Keto는 외부 서비스 호출이므로 지연/실패 가능성이 있습니다.
@@ -48,16 +48,19 @@
### 4.2 권한/정책 SoT
- **1순위: Keto(ReBAC) 관계 튜플**
- 리소스 접근 권한의 최종 판단 기준
- 리소스 접근 권한의 최종 판단 기준.
- **유저 그룹 상속:** 사용자가 속한 유저 그룹에 부여된 권한은 Keto를 통해 실시간으로 상속됩니다.
- **그룹장-어드민 연동:** 유저 그룹의 장(Leader)은 해당 그룹(테넌트)의 어드민 권한을 자동으로 가집니다.
- **2순위: RBAC(Role)**
- 전역/상위 정책의 단축 규칙
- ReBAC와 충돌 시, ReBAC 결과가 항상 우선
- 전역/상위 정책의 단축 규칙.
- ReBAC와 충돌 시, ReBAC 결과가 항상 우선.
### 4.3 테넌트 컨텍스트 SoT
- **1순위: 서버 측 프로필(예: UserProfile.tenantId)**
- **1순위: 서버 측 프로필 및 상속된 권한 (ManageableTenants)**
- 사용자의 기본 `tenantId`뿐만 아니라, 유저 그룹을 통해 **상속받은 관리 가능 테넌트 목록** 전체를 기준으로 판단합니다.
- **2순위: 요청 헤더(X-Tenant-ID)**
- 헤더는 "요청 의도"를 나타내지만, 항상 서버 프로필과 일치해야 함
- 불일치 시 차단
- 헤더는 "요청 의도"를 나타내며, `ManageableTenants` 목록에 포함된 ID여야 합니다.
- 불일치 시 차단.
### 4.4 OIDC/RP 정보 SoT
- **1순위: Hydra Client/Consent 데이터**

View File

@@ -0,0 +1,68 @@
# 유저 그룹 및 테넌트 통합 권한 정책 (Integrated Policy)
이 문서는 Baron SSO의 테넌트(Tenant)와 유저 그룹(User Group) 간의 관계 및 권한 상속에 관한 공식 정책을 정의합니다.
## 1. 기본 원칙 (Core Axioms)
### 1.1 유저 그룹의 테넌트성 (User Group as a Tenant)
- **모든 테넌트가 유저 그룹은 아니지만, 모든 유저 그룹은 반드시 테넌트의 속성을 가집니다.**
- 유저 그룹은 "사용자들의 집합"인 동시에, 그 자체가 권한을 담고 다른 자원을 소유할 수 있는 **격리된 공간(Tenant)**으로 취급됩니다.
### 1.2 권한 상속 로직의 단일화 (Unified Inheritance)
- 테넌트 간의 상속(Parent-Child Tenant)과 유저 그룹의 권한 전파(Group-Member)는 **기술적으로 동일한 ReBAC 로직**을 사용합니다.
- `UserGroup:members` 관계는 `Tenant:members`와 동일한 우선순위를 가지며, 시스템은 이를 구분 없이 하나의 상속 트리로 처리합니다.
### 1.3 그룹장-어드민 연동 (Leader-Admin Mapping)
- 특정 유저 그룹에 명시적으로 **'그룹장(Group Leader)'**을 지정하면, 시스템은 해당 사용자를 해당 유저 그룹(테넌트)의 **'테넌트 어드민(Tenant Admin)'**으로 자동 격상합니다.
- 그룹장은 해당 그룹이 소유한 모든 하위 테넌트 및 리소스에 대해 완전한 제어권을 가집니다.
## 2. 권한 흐름도 (Mermaid)
```mermaid
graph TD
%% Roles
Leader[Group Leader / 그룹장]
Member[Group Member / 멤버]
%% Entities (Polymorphic)
subgraph UG_T [User Group / Specialized Tenant]
UG_ID[Group: Hanmac 운영팀]
end
subgraph Child_T [Child Tenants / 하위 테넌트]
T1[Tenant: 한맥 엔지니어링]
T2[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
%% Effective Access
Admin -- "Full Control" --> UG_ID
Member -- "Shared Access" --> T1
Member -- "Shared Access" --> T2
%% 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
```
## 3. 기술적 구현 가이드 (Implementation)
### 3.1 Keto Relationship Tuples
- **그룹장 임명:** `UserGroup:<ID>#owners@User:<UserID>`
- **어드민 자동 승격:** `Tenant:<ID>#admins@UserGroup:<ID>#owners` (그룹 소유자는 해당 테넌트의 어드민)
- **멤버십:** `UserGroup:<ID>#members@User:<UserID>`
### 3.2 기대 효과
- **정책 단순화:** '어드민'과 '그룹장'을 별도로 관리할 필요가 없어 시스템 복잡도가 감소합니다.
- **책임 명확화:** 그룹의 장이 해당 자원의 최종 책임자가 되는 직관적인 거버넌스를 수립합니다.
- **일관된 UX:** 사용자는 자신이 관리하는 것이 '테넌트'인지 '그룹'인지 고민할 필요 없이 동일한 관리 도구를 사용합니다.

View File

@@ -0,0 +1,83 @@
# 유저 그룹 기반 ReBAC 권한 아키텍처 (User Group-based ReBAC)
이 문서는 Baron SSO의 이슈 #239를 통해 구현된 유저 그룹 중심의 권한 체계와 Ory Keto를 이용한 ReBAC(Relationship-Based Access Control) 설계 방식을 설명합니다.
## 1. 개요
기존의 '테넌트 그룹(Tenant Group)' 방식에서 '유저 그룹(User Group)' 방식으로 전환하여, 권한 부여의 주체(Subject)를 그룹화하고 자원(Tenant)에 대한 권한을 상속받는 구조로 설계되었습니다.
## 2. 권한 상속 다이어그램
```mermaid
graph TD
%% Entities
subgraph Identity [사용자 계정]
U1[User: A]
U2[User: B]
end
subgraph Subjects [권한 부여 주체]
UG[User Group: 개발팀]
end
subgraph Resources [보호 대상 자원]
T1[Tenant: Project Alpha]
T2[Tenant: Project Beta]
RP[Relying Party: Auth App]
end
%% Relationships
U1 -- "is member of" --> UG
U2 -- "is member of" --> UG
UG -- "assigned role: manage" --> T1
UG -- "assigned role: view" --> T2
%% Inheritance Logic (Keto ReBAC)
T1 -- "owns" --> RP
%% Direct Inheritance
U1 -. "inherits: manage" .-> T1
U1 -. "inherits: view" .-> T2
U2 -. "inherits: manage" .-> T1
%% Recursive Permission
T1 -. "allows access" .-> RP
U1 -. "can manage" .-> RP
%% Styles
style Identity fill:#f9f,stroke:#333,stroke-width:2px
style Subjects fill:#bbf,stroke:#333,stroke-width:2px
style Resources fill:#dfd,stroke:#333,stroke-width:2px
linkStyle 4,5,6,7,8,9 stroke:#ff944d,stroke-width:2px,stroke-dasharray: 5 5
```
## 3. 기술적 관계 설계 (Ory Keto Tuples)
Ory Keto 내부적으로는 다음과 같은 관계 튜플(Relationship Tuples)을 통해 권한을 관리합니다.
### 3.1 그룹 멤버십 (Group Membership)
사용자를 특정 유저 그룹의 멤버로 등록합니다.
- **Tuple:** `UserGroup:<GroupID>#members@User:<UserID>`
- **의미:** `UserID` 사용자는 `GroupID` 유저 그룹의 멤버이다.
### 3.2 테넌트 권한 할당 (Tenant Role Assignment)
유저 그룹 전체에 특정 테넌트에 대한 역할을 부여합니다.
- **Tuple:** `Tenant:<TenantID>#<Relation>@UserGroup:<GroupID>#members`
- **의미:** `GroupID` 유저 그룹의 모든 멤버는 `TenantID` 테넌트에 대해 `<Relation>`(예: `view`, `manage`, `admins`) 권한을 가진다.
### 3.3 자원 소유 및 전파 (Resource Ownership)
테넌트가 소유한 하위 자원(RP, API Key 등)에 대한 권한 전파 규칙입니다.
- **Tuple:** `RelyingParty:<ClientID>#parents@Tenant:<TenantID>`
- **검증 논리:** 사용자가 `ClientID`에 대한 `view` 권한을 요청하면, Keto는 해당 사용자가 부모인 `TenantID`에 대해 `view` 권한이 있는지 역추적하여 승인합니다.
## 4. 주요 장점
1. **중앙 집중식 관리:** 사용자의 부서 이동이나 퇴사 시, 개별 테넌트의 권한을 수정할 필요 없이 유저 그룹의 멤버십만 변경하면 모든 연관 권한이 즉시 회수/부여됩니다.
2. **복합 권한 구성:** 하나의 그룹이 여러 테넌트에 대해 서로 다른 수준의 권한을 가질 수 있어, 실제 조직 구조와 프로젝트 협업 모델을 유연하게 반영할 수 있습니다.
3. **Zanzibar 스타일 확장성:** Google Zanzibar 논리를 따르는 Ory Keto를 활용함으로써, 향후 수만 명의 사용자와 수천 개의 테넌트 환경에서도 성능 저하 없이 정교한 권한 체크가 가능합니다.
## 5. 관련 구현 파일
- **Backend Service:** `backend/internal/service/user_group_service.go`
- **Backend Handler:** `backend/internal/handler/user_group_handler.go`
- **Frontend API:** `adminfront/src/lib/adminApi.ts`
- **Frontend UI:** `adminfront/src/features/user-groups/`