docs: architecture, permissions, admin guide + refresh schema/workflows
- New ARCHITECTURE.md (3-layer model + data flow + lifecycle matrix) - New PERMISSIONS.md (grant calculation rules + enforcement) - New ADMIN_GUIDE.md (華文极简:Customer/CustomerProduct 授权 + Product/Resource 管理) - Rewrite N8N_WORKFLOWS.md from WF1-6 (obsolete) to actual W1/W2/W3 - Update DATABASE_SCHEMA.md: fix status enum (drop 'disabled'), add 5-status semantics - Update README.md doc index
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# 权限模型说明(Permissions)
|
||||
|
||||
> 本文解释「最终生效的 AList 权限」是怎么从 NocoDB 数据算出来的。更新日期 2026-08-23。
|
||||
|
||||
## 最终生效权限的三要素
|
||||
|
||||
一个客户最终能访问哪些 AList 路径,由三样东西决定:
|
||||
|
||||
1. `Customers.status` — 整户开关(account-level)
|
||||
2. `CustomerProducts` — 买了哪些产品 + 何时到期(product entitlement)
|
||||
3. `Product → Resources` — 每个产品包含哪些路径(M2M),资源有 `alist_path`
|
||||
|
||||
三者合起来 → n8n 调 `calculateEffectiveGrants` → 算出 desired scopes → 写到 AList Role。
|
||||
|
||||
## 计算规则(calculateEffectiveGrants)
|
||||
|
||||
对某个客户,desired scopes = 以下两类的**并集**:
|
||||
|
||||
### (A) CustomerProduct 派生
|
||||
遍历该客所有 CustomerProduct:
|
||||
- 跳过 `status != active` 的
|
||||
- 跳过 `expires_at <= 今天` 的(**到期当天视为已过期**)
|
||||
- 通过 `product` link → Product 的 M2M Resources → 拿到每个 resource 的路径
|
||||
|
||||
### (B) AccessGrant 派生(预留,当前不经 n8n 写入)
|
||||
- 同 (A),跳过非 active / 已过期
|
||||
|
||||
### 合并规则
|
||||
- 同一个 path 来自多个来源 → 取 **MAX(expires_at)**(两个产品共享一个资源时,取较晚的到期)
|
||||
- permission:`readwrite` 覆盖 `read`(readwrite=1,否则 0)
|
||||
- basis:本系统客户一律 **只读**(permission 0),读写在 AccessGrant 手工授权时可用
|
||||
|
||||
## AList 侧落地(enforcement)
|
||||
|
||||
- 每个客户 = 1 个专属 AList Role(`name = customer-<username>`)
|
||||
- Role 的 `permission_scopes` = desired scopes 的绝对路径数组
|
||||
- 客户 User:`base_path = '/'`(**必须 `/`**,否则 JoinPath 双重前缀 → 全 403)
|
||||
- 客户 User:`permission = 16384`(bit 14 = Check path limit 置位 → 只能访问 role scope 内的路径)
|
||||
- `disabled` 由 status 控制:非 active → `disabled=true`
|
||||
|
||||
## 多文件夹授权正确做法(已实测)
|
||||
|
||||
- `base_path = "/"` + role scope 用**绝对路径**(如 `/Class/Forex`、`/Template/Music`)
|
||||
- 结果:客户只看到被授权路径,未授权挂载点(`/Book` 等)不泄露
|
||||
|
||||
## 到期与状态的关系
|
||||
|
||||
| 场景 | 结果 |
|
||||
|---|---|
|
||||
| CustomerProduct 到期 | 该产品派生的路径从 scopes 移除(W2/W3 对账自动修)|
|
||||
| 整个 Customer status 变 non-active | 整户 disable + scopes 清空(W1)|
|
||||
| non-active → active 恢复 | 重算当前有效 scopes,**不恢复已过期产品** |
|
||||
|
||||
## 重要边界
|
||||
|
||||
- `Customers.status` 是账户级开关,**不替代** CustomerProduct 的到期。
|
||||
- 例:Customer = `expired`,其 Product A 仍 active 且未到期。恢复 `expired → active` 后 Product A 会自动回来;已过期的 Product B 不会回来。
|
||||
- 状态变化**不自动改** CustomerProducts(不批量改 CP.status、不改 expires_at、不删除 CP)。
|
||||
- AccessGrants 历史记录**不删除**;恢复时重算,不用旧 AccessGrant scope 直接恢复。
|
||||
Reference in New Issue
Block a user