记录规则

Odoo 记录规则为什么不是“domain 过滤完就结束”:SQL 域、访问校验与越权错觉讲透

很多人理解记录规则时只记住一句话:ir.rule 会给 search 自动加 domain。这个说法不算错,但远远不够。到了新版本源码里,访问检查已经不再是“前面搜出来就算能操作”,而是模型权限先过一层、记录规则再过一层,最后才得到真正允许的记录集。本文结合 orm/models.py 把这条边界讲透。

Odoo 开发 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

很多人把 Odoo 记录规则总结成一句话:

search 时系统会自动给你拼上 ir.rule 的 domain。

这句话只够应付入门,不够应付真实开发。

更完整的说法应该是:

  1. 先看模型级权限 ir.model.access
  2. 再根据当前操作计算记录规则 domain
  3. 再对当前记录集做允许/不允许判断
  4. 最终得到“这批记录里你到底能对哪些做 read / write / unlink”

所以真正危险的误解是:

  • 以为“能 search 到”就一定能写
  • 以为“count 有数字”就说明前端读详情不会报错
  • 以为 sudo() 只是“让 search 更全一点”

其实这些都把权限边界想简单了。


新版源码视角:访问检查不是一个动作,而是一条链

/home/ubuntu/odoo-temp/odoo/orm/models.py 里,新版核心逻辑已经把旧的 check_access_rights()check_access_rule()_filter_access_rules_python() 都收束到新的访问检查模型上。

最关键的方法是:

  • check_access()
  • has_access()
  • _filtered_access()
  • _check_access()

其中 _check_access() 的顺序非常值得记住。

第一层:模型权限先拦

源码先调用 ir.model.access 检查当前模型在当前操作上是否允许。

如果模型级权限都没有:

  • 不是“返回空记录集”
  • 而是压根不允许你继续讨论具体哪些记录能不能碰

这就是为什么有些问题根本与记录规则无关,而是 ACL 没开。


第二层:记录规则 domain 不是“装饰”,而是真正参与允许记录计算

当模型权限通过后,源码会算出:

  • Rule._compute_domain(self._name, operation)

然后它不是直接说“以后 search 都加这个 domain 就完了”,而是进一步做:

  • 对当前记录集 self
  • sudo().with_context(active_test=False).filtered_domain(domain)
  • 算出这批记录里哪些满足当前规则

最后把不满足规则的记录视作 forbidden。

这段实现特别说明两件事:

  1. 记录规则最终要回答的是“你能不能操作这批真实记录”
  2. 它不是前端检索体验功能,而是安全边界的一部分

为什么会出现“列表里有,点进去报错”的错觉

现实项目里,最常见的抱怨是:

  • 列表里明明看到了
  • 点详情却报权限错误
  • 或者批量操作时只有部分记录失败

这类现象背后,通常有几种可能:

1. 列表和详情走的模型/上下文不同

例如列表页的 domain、公司上下文、active_test、关联字段展开方式不同。

2. 你看到的是上游记录,但真正报错的是下游关联记录

比如订单本身能读,但它的某个关联发票、公司、仓库记录规则更严。

3. 自定义代码提前 sudo() 查出记录,后面又在非 sudo 环境里继续写

这时开发者以为“我都拿到记录了”,但真正写入时还是会重新触发访问检查。

所以“看得到”从来不等于“所有后续动作都过得去”。


sudo() 真正绕过了什么,没绕过什么

很多人一遇到权限问题就先加 sudo(),然后把问题暂时压下去。

但从边界上说,sudo() 的意义不是“多拿几条数据”,而是:

切换到超级用户语义下执行,绕过原本属于当前用户的访问限制。

因此它会带来两个风险:

风险 1:你修的不是问题,是症状

如果本来应该通过规则修正业务边界,却直接 sudo(),你只是把权限设计问题藏起来了。

风险 2:你可能把本不该暴露的数据带回普通用户界面

比如:

  • sudo() 读出不该见的记录
  • 再把结果写到字段、统计、名称搜索或页面计数里

这类 bug 最难排,因为它看起来“没有报错”,但实际上已经越权泄露。


多公司场景下为什么更容易误判

记录规则和多公司叠加时,最容易出现“我明明没改权限,为什么突然不能看”的情况。

原因通常是:

  • 当前公司切换了
  • allowed_company_ids 变化了
  • 某些关联记录 check_company 失败
  • 规则 domain 里含公司条件,而你的上下文没跟上

于是开发者经常误以为:

  • 是 search domain 写错了
  • 是缓存问题
  • 是前端没刷新

实际上很可能只是当前环境下允许访问的记录集已经变了。


开发时最稳的排查顺序

当你怀疑是记录规则导致异常时,建议按这个顺序查:

  1. 当前用户对模型是否有 ACL 权限
  2. 当前操作是 readwrite 还是 unlink
  3. 当前上下文里的公司、active_test、sudo 状态是什么
  4. 目标记录是否真的落在规则 domain 内
  5. 是否是某个关联模型先报错,而不是当前模型本身

这个顺序比“上来就改规则”稳得多。


一个常被忽略的事实:安全边界是按“操作”算的

同一条记录:

  • 可能允许 read
  • 但不允许 write
  • 也可能允许 write
  • 却不允许 unlink

因为规则计算和权限判断都带着 operation 语义。

所以不要把“这个用户有这条记录权限”理解成一个单值判断。

更准确地说,应该理解成:

这个用户对这条记录,在这个操作下,是否被允许。


最后的判断句

在 Odoo 里,记录规则从来都不是“search 自动拼 domain”这么简单。

它的本质更接近:

模型权限 + 记录规则 + 当前上下文 + 当前操作,共同决定这批记录里哪些真的可访问。

理解了这一点,你就不会再把:

  • 看得到
  • 搜得到
  • 计数对得上
  • 偶尔能写

误当成“权限已经没问题”。

真正稳定的权限开发,靠的不是多写几个 sudo(),而是把访问边界先想清楚。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。