SuperAPI AI 工具集

erpnext-permissions

安全与审计

查看项目

更新于 10-16 02:20

技能介绍

用途

该技能用于在 Frappe/ERPNext 应用中实现权限系统,提供确定性的实现模式,覆盖 v14、v15、v16 版本。

主要能力

Frappe 权限系统分为五层:

  • 角色权限(Role Permissions):控制用户能做什么,通过 DocType 权限表配置,适用于所有版本。
  • 用户权限(User Permissions):控制用户能看到哪些文档,通过 User Permission 记录配置,适用于所有版本。
  • 权限级别(Perm Levels):控制用户能看到哪些字段,通过字段的 permlevel 属性配置,适用于所有版本。
  • 权限钩子(Permission Hooks):自定义逻辑,通过 hooks.py 配置,适用于所有版本。
  • 数据脱敏(Data Masking):对字段值进行掩码显示,通过字段的 mask 属性配置,适用于 v16+。

权限类型包括:read、write、create、delete、submit、cancel、select(v14+)、mask(v16+)。

自动角色包括:Guest(所有人,含匿名)、All(所有注册用户)、Administrator(仅 Administrator 用户)、Desk User(System Users,v15+)。

使用方式

核心 API:

  • 检查权限:frappe.has_permission("Sales Order", "write"),支持 DocType 级、文档级、指定用户、throw=True 抛出异常;文档实例上可用 doc.has_permission("write") 与 doc.check_permission("write")。
  • 获取权限:from frappe.permissions import get_doc_permissions,返回如 {'read': 1, 'write': 1, 'create': 0, 'delete': 0, ...}。
  • 用户权限:from frappe.permissions import add_user_permission, remove_user_permission, get_user_permissions。
  • 文档共享:from frappe.share import add as add_share,可指定 read、write 等权限。

数据脱敏(v16+):

  • 在 DocType(开发者模式)或 Customize Form 中为字段设置 "mask": 1。
  • 支持字段类型:Data、Date、Datetime、Currency、Float、Int、Percent、Phone、Password、Link、Dynamic Link、Select、Read Only、Duration。
  • 为应查看未脱敏数据的角色添加 mask 权限,例如 {"role": "HR Manager", "permlevel": 0, "read": 1, "mask": 1}。
  • 无 mask 权限的用户在表单视图、列表视图、报表视图、API 响应(/api/resource/、/api/method/)中看到脱敏值。
  • 注意:数据脱敏不适用于自定义 SQL 查询、使用原始 SQL 的 Query Report、直接 frappe.db.sql() 调用,需手动实现脱敏。

权限钩子:

  • has_permission 钩子:添加自定义权限逻辑,只能拒绝不能授予;返回 None 继续标准检查,返回 False 拒绝权限;默认始终返回 None。
  • permission_query_conditions 钩子:过滤列表查询,仅影响 get_list(),不影响 get_all();返回 SQL WHERE 片段,必须使用 frappe.db.escape(),禁止字符串拼接。

字段级权限(Perm Levels):

  • 为字段设置 permlevel,为角色配置对应 permlevel 的读写权限。
  • 规则:必须先授予 Level 0,才能授予更高级别。

决策树:

  • 控制整个 DocType 访问 → 角色权限
  • 控制特定文档 → 用户权限
  • 完全隐藏特定字段 → 权限级别
  • 显示脱敏的特定字段 → 数据脱敏(v16+)
  • 自定义逻辑 → has_permission 钩子
  • 列表查询过滤 → permission_query_conditions 钩子

代码中检查权限:

  • 操作前 → frappe.has_permission() 或 doc.has_permission()
  • 抛出错误 → doc.check_permission() 或 throw=True
  • 需要绕过 → doc.flags.ignore_permissions = True(需记录原因)

常见模式:

  • 仅所有者可编辑:{"role": "Sales User", "read": 1, "write": 1, "create": 1, "if_owner": 1}
  • 操作前检查:在 @frappe.whitelist() 方法中先 doc.has_permission("write"),无权限则 frappe.throw(_("No permission"), frappe.PermissionError)。
  • 角色限制端点:@frappe.whitelist() 方法中使用 frappe.only_for(["Manager", "Administrator"])。

关键规则:

  • 始终使用权限 API,而非角色检查
  • 始终转义 SQL:frappe.db.escape(user)
  • 面向用户的查询始终使用 get_list
  • has_permission 钩子中始终返回 None(而非 True)
  • 使用 ignore_permissions 时始终记录原因
  • 权限变更后始终清除缓存:frappe.clear_cache()
  • 自定义 SQL 查询中始终手动脱敏(v16+)

反模式:

  • 不要用 if "Role" in frappe.get_roles(),应使用 frappe.has_permission(dt, ptype)
  • 不要用 frappe.get_all() 做用户查询,应使用 frappe.get_list()
  • 不要在 has_permission 中返回 True,应返回 None
  • 不要用 f"owner = '{user}'",应使用 f"owner = {frappe.db.escape(user)}"
  • 不要在钩子中 frappe.throw(),应返回 False
  • 不要假设自定义 SQL 会自动脱敏,应手动实现

版本差异:

  • select 权限:v14、v15、v16 均支持
  • Desk User 角色:v14 不支持,v15、v16 支持
  • 自定义权限类型:v14、v15 不支持,v16 支持(实验性)
  • 数据脱敏:v14、v15 不支持,v16 支持
  • mask 权限类型:v14、v15 不支持,v16 支持

调试:

  • frappe.has_permission("Sales Order", "read", doc, debug=True)
  • print(frappe.local.permission_debug_log)
  • from frappe.permissions import get_doc_permissions; get_doc_permissions(doc, user="john@example.com")

参考文件(references/ 文件夹):permission-types-reference.md、permission-api-reference.md、permission-hooks-reference.md、examples.md、anti-patterns.md。

相关技能:erpnext-database、erpnext-syntax-controllers、erpnext-syntax-hooks。

适用场景

适用于在 Frappe/ERPNext 应用中实现或调试权限控制,包括角色与用户权限配置、字段级权限、数据脱敏、权限钩子编写、列表查询过滤、文档共享,以及权限相关的代码审查与反模式排查。

限制

数据脱敏仅 v16+ 支持,且不适用于自定义 SQL 查询、原始 SQL 的 Query Report 和直接 frappe.db.sql() 调用,需手动实现。自定义权限类型为 v16 实验性功能。has_permission 钩子只能拒绝权限,不能授予权限。permission_query_conditions 钩子仅影响 get_list(),不影响 get_all()。