11 KiB
11 KiB
Permission Service
Permission Service — SRS/SDD Document
| Version | Version Date | Editor | Memo |
|---|---|---|---|
| 1.0.0 | 2026/05/21 | Gateway Team | 初版:Gateway Permission 模組 SDD(對齊 frontend Permission Service SDD 格式) |
| 1.1.0 | — | — | 新增 category 樹狀 Permission Catalog |
| 1.2.0 | — | — | 多租戶 B2B RBAC + Casbin + RoleMapping |
1. Introduction
1.1 Purpose
Permission 模組提供 Gateway 多租戶 B2B 自定義 RBAC:平台級 Permission Catalog + 租戶級 Role / RolePermission / UserRole / RoleMapping,搭配 Casbin enforcer 進行 HTTP path/method 授權,並支援外部 IdP(ZITADEL / LDAP / SCIM)角色映射同步。
1.2 Scope
範圍內(Multiple tenants):
- 平台 Permission Catalog(樹狀,dot notation)
- 租戶 Role CRUD(含 system role 防呆)
- Role ↔ Permission 多對多(含 parent closure)
- User ↔ Role 多對多(source: manual / zitadel / ldap / scim)
- 外部 group → 內部 Role.Key 映射(RoleMapping)
- Casbin policy 物化(Redis Set)+ 多 pod Pub/Sub reload
- GET /permissions/me 前端選單渲染
範圍外:
- JWT 簽發 → Auth 模組
- 會員資料 → Member 模組
- Platform admin bypass → middleware 預檢(保留 audit)
1.3 Definitions, Acronyms, and Abbreviation
| 縮寫 | 說明 |
|---|---|
| RBAC | Role-Based Access Control |
| Permission | 平台級權限節點(可為分類或 leaf API 權限) |
| Role | 租戶內角色,key 唯一且不可改 |
| RolePermission | Role 勾選的 Permission 集合 |
| UserRole | 使用者被指派的角色 |
| RoleMapping | 外部 IdP group/role → 內部 Role.Key |
| Casbin | 授權引擎,policy 存 Redis |
| Leaf Permission | 有 http_path + http_methods 的可 enforcement 節點 |
1.4 Technologies to be used
| 項目 | 技術 |
|---|---|
| Application Language | Go 1.22+ |
| Framework | go-zero |
| Cache / Policy Store | Redis(Casbin rules Set + Pub/Sub) |
| Database | MongoDB |
| Authorization Engine | Casbin(keyMatch2 + regexMatch) |
| API Codegen | goctl / .api 檔 |
1.5 Overview
flowchart LR
subgraph Platform["平台層"]
Catalog[Permission Catalog]
end
subgraph Tenant["租戶層"]
Role[Role]
RP[RolePermission]
UR[UserRole]
RM[RoleMapping]
end
Catalog --> RP --> Role
Role --> UR
Role --> RM
Tenant --> Casbin[(Casbin Enforcer<br/>Redis adapter)]
Casbin --> MW[CasbinRBAC Middleware]
- Permission 平台 seed 全局,租戶不可新增,只能勾選
- Role / RolePermission / UserRole 租戶獨立
- Role.Key 一旦建立 不可改(外部 IdP 對應用)
- 多 pod 同步:Redis Pub/Sub 即時 + cron 兜底
2. System Overview
Permission 模組是 Gateway 授權子系統:
- 平台維護 Permission Catalog(
cmd/permission-seed) - 租戶管理員建立 Role、勾選 Permission
- 指派 UserRole(手動或 SyncFromX 同步)
- RBACUseCase 物化 Casbin policy → middleware 檢查
(tenant, role.key, path, method)
前端透過 GET /permissions/me 取得當前使用者的 role keys 與 permission map(含可選 tree)。
3. System Architecture
3.1 System Architecture
flowchart TD
Logic[logic/permission] --> SVC[svc.ServiceContext]
SVC --> AuthQ[AuthorizationQueryUseCase]
SVC --> Perm[PermissionUseCase]
SVC --> Role[RoleUseCase]
SVC --> RolePerm[RolePermissionUseCase]
SVC --> UserRole[UserRoleUseCase]
SVC --> Mapping[RoleMappingUseCase]
SVC --> RBAC[RBACUseCase]
RBAC --> Adapter[Casbin Redis Adapter]
Adapter --> Redis[(Redis)]
RBAC --> Pub[Redis Pub/Sub casbin:reload]
Perm --> PermR[(permissions)]
Role --> RoleR[(roles)]
RolePerm --> RPR[(role_permissions)]
UserRole --> URR[(user_roles)]
Mapping --> RMR[(role_mappings)]
3.2 Decomposition Description
| 套件 | 職責 |
|---|---|
config/ |
CasbinConfig、CacheConfig、ReloadConfig |
domain/entity/ |
Permission、Role、RolePermission、UserRole、RoleMapping |
domain/enum/ |
Status、PermissionType、RoleSource |
domain/repository/ |
5 個 Mongo repo 介面 + Casbin adapter port |
domain/usecase/ |
7 個 usecase 介面 + DTO |
repository/ |
Mongo + Redis Casbin adapter |
usecase/ |
7 個 atomic usecase 實作 + permission_tree |
seed/ |
catalog.json + Apply CLI |
3.3 RBAC Model
Casbin 模型(etc/rbac.conf):
[request_definition]
r = tenant, role, path, method
[policy_definition]
p = tenant, role, path, methods, name
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.tenant == p.tenant && r.role == p.role && keyMatch2(r.path, p.path) && regexMatch(r.method, p.methods)
授權檢查(any-allow): 使用者所有 open role 逐一 EnforceEx,任一 allow 即通過。
sequenceDiagram
participant MW as CasbinRBAC Middleware
participant RBAC as RBACUseCase
participant URR as UserRoleRepository
participant Enf as casbin.Enforcer
MW->>RBAC: Check{tenant, uid, path, method}
RBAC->>URR: ListByUser
loop each open role
RBAC->>Enf: EnforceEx(tenant, role.key, path, method)
alt allow
RBAC-->>MW: Allow
end
end
RBAC-->>MW: Deny → 403
3.4 Permission Tree
member.info.management ← 分類(無 HTTP)
├── member.basic.info
│ ├── member.info.select GET /api/v1/members/me
│ └── member.info.update PATCH /api/v1/members/me
└── member.admin.list GET /api/v1/members
permission.role.management
├── permission.role.read GET /api/v1/permissions/roles
└── permission.role.write POST/PUT/DELETE /api/v1/permissions/roles*
分類節點(無 http_path)不寫入 Casbin policy;Replace RolePermission 時自動補齊 parent closure。
3.5 Policy Reload(多 Pod)
sequenceDiagram
participant PodA as Pod A
participant Redis
participant PodB as Pod B
PodA->>PodA: RolePermission.Replace + LoadPolicy
PodA->>Redis: PUBLISH casbin:reload {tenant_id}
Redis-->>PodB: message
PodB->>PodB: LoadPolicy(tenant)
4. Data Design
4.1 Data Dictionary
permissions(MongoDB — 平台全局)
| Field | Type | Comment | Index |
|---|---|---|---|
| _id | ObjectId | PK | PK |
| parent | String | 父節點 ObjectId hex | parent |
| name | String | dot notation 唯一名稱 | Unique |
| http_methods | String | GET 或 GET|POST|PATCH | — |
| http_path | String | keyMatch2 path pattern | — |
| status | String | open / close | status |
| type | String | backend_user / frontend_user | type |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
roles(MongoDB — 租戶範圍)
| Field | Type | Comment | Index |
|---|---|---|---|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, key) |
| key | String | 角色 key(不可改) | Unique(tenant_id, key) |
| display_name | String | 顯示名稱 | — |
| creator_uid | String | 建立者 UID | — |
| status | String | open / close | (tenant_id, is_system) |
| is_system | Bool | 平台預設角色 | (tenant_id, is_system) |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
role_permissions(MongoDB)
| Field | Type | Comment | Index |
|---|---|---|---|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, role_id, permission_id) |
| role_id | String | Role ObjectId hex | (tenant_id, role_id) |
| permission_id | String | Permission ObjectId hex | (tenant_id, permission_id) |
| create_at | Int64 | 建立時間 ms | — |
user_roles(MongoDB)
| Field | Type | Comment | Index |
|---|---|---|---|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, uid, role_id) |
| uid | String | 會員 UID | (tenant_id, uid, source) |
| role_id | String | Role ObjectId hex | (tenant_id, role_id) |
| source | String | manual / zitadel / ldap / scim | (tenant_id, uid, source) |
| create_at | Int64 | 建立時間 ms | — |
role_mappings(MongoDB)
| Field | Type | Comment | Index |
|---|---|---|---|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, external_source, external_key) |
| external_source | String | zitadel / ldap / scim | Unique(tenant_id, external_source, external_key) |
| external_key | String | 外部 group/role key | Unique(tenant_id, external_source, external_key) |
| internal_role_id | String | 內部 Role ID | (tenant_id, internal_role_id) |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
Redis Keys
| Key Pattern | 內容 | TTL |
|---|---|---|
| permission:casbin:rules:{tenant_id} | Set of JSON Casbin rules | 永久 |
| perm:user_roles:{tenant_id}:{uid} | role keys 快取(預留) | 300s |
| perm:role_perms:{tenant_id}:{role_id} | permission names 快取(預留) | 300s |
| (channel) casbin:reload | Pub/Sub reload payload | — |
Casbin rule 格式: [tenant, role.key, http_path, http_methods, perm.name]
5. API Design
Base Path: /api/v1/permissions
| Method | Path | 說明 |
|---|---|---|
| GET | /catalog |
全局 Permission Catalog(tree/list) |
| GET | /me |
當前 user 的 roles + permissions |
| GET | /roles |
租戶角色清單 |
| POST | /roles |
建立角色 |
| PATCH | /roles/:id |
更新 display_name / status |
| DELETE | /roles/:id |
刪除角色 |
| GET | /roles/:id/permissions |
角色 permission 集合 |
| PUT | /roles/:id/permissions |
全量取代 + parent closure + reload |
| GET | /users/:uid/roles |
使用者 role 列表 |
| POST | /users/:uid/roles |
指派角色 |
| DELETE | /users/:uid/roles/:role_id |
撤銷角色 |
| GET | /role-mappings |
外部映射列表 |
| PUT | /role-mappings |
Upsert 外部映射 |
| DELETE | /role-mappings |
刪除外部映射 |
| POST | /policy/reload |
強制重載 policy(單租戶或 *) |
完整 schema 見 generate/api/permission.api。
6. Resource
| 資源 | 路徑 |
|---|---|
| API 定義 | generate/api/permission.api |
| Logic | internal/logic/permission/ |
| Middleware | internal/middleware/casbin_rbac.go |
| 模組原始碼 | internal/model/permission/ |
| 開發 README | internal/model/permission/README.md |
| Casbin 模型 | etc/rbac.conf |
| Seed 資料 | internal/model/permission/seed/catalog.json |
| Seed CLI | cmd/permission-seed |
| 設定範例 | etc/gateway.dev.example.yaml → Permission 區塊 |
| 設計參考 | docs/identity-member-design.md §6 / §7.3 / §13 |