# 权限模型说明(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-`) - 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 直接恢复。