forked from baron/baron-sso
144 lines
5.1 KiB
Markdown
144 lines
5.1 KiB
Markdown
# 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))
|
|
}
|
|
}
|
|
```
|