Frappe v6.2.0 用户权限行为变更解析:从"静默放行"到"严格过滤"的权限语义
【免费下载链接】frappeLow code web framework for real world applications, in Python and Javascript项目地址: https://gitcode.com/GitHub_Trending/fr/frappe
导读
本文围绕 frappe/change_log/v6/v6_2_0.md 中记录的 v6.2.0 权限行为变更展开:当某个 DocType 存在 User Permission(用户权限)规则、但用户尚不满足该规则时,系统默认不再展示不匹配的记录,只有在 System Settings 中勾选Ignore User Permissions If Missing时才允许放行。读完本文,你将掌握这一变更的确切含义、它在源码与测试中的实现形态、如何在 System Settings 中配置,以及如何基于文档、测试与字段定义准确理解这一行为。
说明:本文基于当前仓库(Frappe 现代版本)的源码与测试撰写,v6.2.0 时代的旧设置项名称在后续演进中已发生变化,文中会逐一指出两者的对应关系,帮助读者在旧版本与新版本之间建立准确的对照。
一、变更日志原文与核心语义
原文档(frappe/change_log/v6/v6_2_0.md)完整内容如下:
Permissions:
- If User Permissions are missing for a DocType, don't show non-matching records.
- IfIgnore User Permissions If Missingis checked in System Settings, show records even if User Permissions are not defined.
拆解这两条行为规则:
- 默认行为(收紧):当某个 DocType 配置了 User Permission(即存在针对特定用户/角色的记录过滤规则),而当前用户的记录不满足这些规则时,系统不再显示不匹配的记录。在 v6.2.0 之前,这类"规则缺失或记录不匹配"的情况往往被宽松对待,v6.2.0 将其收紧为"宁可少显示,也不越权显示"。
- 显式放行开关:在 System Settings 中勾选Ignore User Permissions If Missing后,即使 User Permissions 未定义(或记录不匹配),也照常显示这些记录,把"是否遵守缺失规则"的决策权交给系统管理员。
简言之:v6.2.0 让 User Permission 的过滤语义从"宽松"走向"严格默认、显式放行"。
二、当前仓库中的对应实现:从开关到开关语义的演进
2.1 设置项名称的演进:从 Ignore 到 Apply Strict
需要特别说明的是:v6.2.0 日志中提到的Ignore User Permissions If Missing设置项,在当前仓库中已演化为方向相反的语义:
- 在 frappe/core/doctype/system_settings/system_settings.json 中,字段名为
apply_strict_user_permissions,标签为Apply Strict User Permissions,默认值为"0"(不勾选); - 字段描述给出了精确语义:"If Apply Strict User Permission is checked and User Permission is defined for a DocType for a User, then all the documents where value of the link is blank, will not be shown to that User"(当勾选该选项且用户对某 DocType 定义了 User Permission 时,链接字段为空的文档将不会展示给该用户);
- 对应的类型声明位于 frappe/core/doctype/system_settings/system_settings.py:
apply_strict_user_permissions: DF.Check。
也就是说,v6.2.0 时代的"忽略缺失的用户权限(Ignore)"在语义上等价于当前仓库中"不启用严格用户权限(Apply Strict = 不勾选)";而"严格模式"正是 v6.2.0 之后默认收紧的过滤行为。理解这个"开关取反"的演进,是读懂新旧版本行为的关键。
2.2 单例 DocType 的读取与缓存
System Settings 是典型的单例(Single)DocType。仓库提供了便捷读取接口get_system_settings(frappe/core/doctype/system_settings/system_settings.py),它优先从frappe.local.system_settings获取,未命中时通过frappe.client_cache.get_doc("System Settings")加载并缓存;保存设置后clear_system_settings_cache(同文件 L276-L279)会清除相关缓存键,保证开关变更立即生效。
三、权限判定源码:has_user_permission的两步检查
User Permission 的最终裁决入口是frappe/permissions.py中的has_user_permission(frappe/permissions.py)。它采用"两步检查 + 严格模式开关"的结构:
前置判断(L362-L386):
- 若
get_user_permissions(user)返回空(用户完全不受任何 User Permission 影响),直接返回True——这与日志第一条"缺失即不过滤"的默认宽松语义呼应; apply_strict_user_permissions = strict and (False if doc.meta.issingle else frappe.get_system_settings("apply_strict_user_permissions")):单例 DocType 因为含空链接字段,永远不启用严格模式;- 对于
__islocal的未保存文档(ptype为 read/write 且数据库中不存在该记录),严格模式会被跳过,避免在新建过程中误判。
STEP 1:检查文档自身(L388-L418)
- 当
doctype in user_permissions且存在允许的文档列表allowed_docs时,若docname不在允许列表内,则拒绝访问。这对应"对 DocType 本身做记录级过滤"。
STEP 2:检查所有 Link 字段(L420-L485)
- 遍历父表与所有子表记录的 Link 字段(
check_user_permission_on_link_fields,L423-L476):- 字段设置了
ignore_user_permissions则跳过; - 关键分支:
if not d.get(field.fieldname) and not apply_strict_user_permissions: continue——当链接值为空时,非严格模式直接放行;严格模式下空值也会被检查并拒绝; - 当该 Link 目标 DocType 在
user_permissions中且存在允许列表时,若当前值不在允许列表内,则拒绝,并给出精确的错误消息(子表行会附带row {idx}, field {label}定位信息)。
- 字段设置了
这也印证了日志中"不显示不匹配记录"的落点:在 v6.2.0 收紧后,空链接值/不匹配链接值在默认情况下不再被展示。
四、列表查询层的过滤:db_query.py的 SQL 级实现
记录级访问控制同样作用于列表查询。在 frappe/model/db_query.py 的add_user_permissions中:
- 遍历当前 DocType 的所有 Link 字段(并追加
name字段作为自身过滤入口); - 对每个存在 User Permission 的字段构造过滤条件:
- 严格模式开启时
condition = ""(不附加空值豁免,空链接值的行也进入过滤); - 非严格模式时
condition = "ifnull(...)='' or "(空值行豁免,直接放行)。
- 严格模式开启时
这是对has_user_permission语义在 SQL 层的复刻:同一开关同时决定"单文档级"与"列表级"的行为一致性。此外,frappe/model/mapper.py(L70、L99、L179)在文档映射(copy/map)场景中也读取同一设置,说明该开关对跨 DocType 的复制操作同样生效。
五、测试验证:三条测试用例锁定的行为契约
仓库中的 frappe/tests/test_permissions.py 用测试用例固化了这一语义,可将其作为行为契约的权威参考:
test_ignore_user_permissions_if_missing(L455-L477)- 先为用户
test2@example.com添加Test Blog Category的 User Permission,使用另一个分类(_Test Blog Category 2)创建文档时has_permission("write")为False(记录不匹配 → 拒绝); - 移除该 User Permission 后,同一文档
has_permission("write")变为True——"权限缺失即按角色放行",正是 v6.2.0 第一条语义的正面写照。
- 先为用户
test_strict_user_permissions(L479-L519)- 通过
self.set_strict_user_permissions(0/1)切换 System Settings 中的apply_strict_user_permissions; - 关闭严格模式时,未匹配的
other_contact也可读,frappe.get_list("Contact")返回 2 条(空值豁免生效); - 开启严格模式后,
other_contact不可读,列表仅返回 1 条(空值不再豁免)。
- 通过
更早的
test_user_permissions_in_doc(L420 前段)验证了"未配置 User Permission 时插入被拒、配置后放行"的完整闭环,以及权限检查对 read/write/create 的区分。
六、实战配置建议与行为对照表
6.1 在 System Settings 中配置
- 以 System Manager 角色登录,打开System Settings(单例 DocType,定义于 frappe/core/doctype/system_settings/system_settings.json);
- 在Permissions区块找到Apply Strict User Permissions复选框;
- 勾选后保存,
clear_system_settings_cache会自动清除缓存,新行为即刻生效(frappe/core/doctype/system_settings/system_settings.py); - 之后在User PermissionDocType 中为用户添加具体的记录级权限规则。
6.2 新旧语义对照表
| v6.2.0 日志语义 | 当前仓库等价语义 | 源码落点 |
|---|---|---|
| 不勾选 Ignore User Permissions If Missing → 不显示不匹配记录 | 勾选 Apply Strict User Permissions(默认关闭)→ 空值/不匹配记录被过滤 | frappe/permissions.py、frappe/model/db_query.py |
| 勾选 Ignore User Permissions If Missing → 即使未定义也显示记录 | 不勾选 Apply Strict User Permissions → 空值豁免、按角色放行 | frappe/permissions.py |
| — | 单例 DocType 永不启用严格模式 | frappe/permissions.py |
| — | 字段级ignore_user_permissions可单独豁免某个 Link 字段 | frappe/permissions.py |
6.3 选择建议
- 安全敏感场景(财务、人事、多租户数据隔离):保持严格模式开启,确保空链接字段的记录也绝不越权可见;
- 日常业务流畅性优先:关闭严格模式,允许空链接值记录正常展示,仅对已定义规则且明确不匹配的记录进行过滤;
- 对树形 DocType 的 create 操作、
__islocal未保存文档,框架已内置豁免逻辑,无需额外配置。
七、总结
v6.2.0 的权限变更确立了 Frappe 用户权限体系的基线语义:默认按"存在规则即过滤、不匹配即隐藏"执行,同时提供全局开关允许管理员显式放宽。这一语义在 frappe/permissions.py(单文档裁决)、frappe/model/db_query.py(列表 SQL 过滤)、frappe/model/mapper.py(文档映射)三层实现中保持一致,并由 frappe/tests/test_permissions.py 中的test_ignore_user_permissions_if_missing与test_strict_user_permissions固化验证。理解并正确配置Apply Strict User Permissions开关,是任何 Frappe 应用在数据隔离与业务体验之间取得平衡的第一步。
【免费下载链接】frappeLow code web framework for real world applications, in Python and Javascript项目地址: https://gitcode.com/GitHub_Trending/fr/frappe
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考