权限与规则

Odoo 权限别再混了:Access Rights、Groups、Record Rules 到底怎么分工

很多 Odoo 权限问题并不是“没权限”这么简单,而是模型级权限、分组、记录规则、多公司上下文叠在一起的结果。本文帮你一次理清。

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

先给你一个不会乱的框架

Odoo 权限最好按两层来记:

  1. 能不能操作这个模型 → Access Rights(ir.model.access
  2. 能操作这个模型里的哪些记录 → Record Rules(ir.rule

如果你脑子里先没有这条分层,后面就很容易把所有报错都理解成“系统抽风”。

实际上,大多数权限问题都能落回这两个问题:

  • 你有没有这个模型的读写删建权?
  • 就算有,你能碰到的是不是当前这批记录?

Groups 不是最终权限,但它是入口

很多人第一次接触 Odoo,会把 group 直接当成权限本身。

其实 group 更像“权限包的挂钩点”。

它会影响至少三类东西:

  • ir.model.access:模型级 CRUD 权限
  • ir.rule:记录级过滤规则
  • 视图 / 菜单 / 按钮的显示逻辑

所以 group 本身不是终点,而是把安全策略挂到用户身上的桥。


第一层:Access Rights 决定“你是否能进门”

模型级权限说的是:

  • 对某个 model 能不能 read
  • 能不能 write
  • 能不能 create
  • 能不能 unlink

如果这层没有放行,后面的 record rule 根本没机会发挥。

也就是说:

Access Rights 是门禁卡,Record Rule 是门里之后的活动范围。

这也是为什么很多人明明配了 record rule,用户还是报没权限——因为他连模型级 read 都没有。


第二层:Record Rule 决定“进门后能看到哪几条”

ir.rule 的核心就是一个 domain。

源码中 IrRule._eval_context() 会给 domain 提供常见上下文:

  • user
  • company_id
  • company_ids

所以你在规则里经常会写到:

  • 只能看自己负责的记录
  • 只能看自己公司的记录
  • 只能看被分配给自己的单据

这类限制都不是 access rights 干的,而是 record rule 干的。


全局规则 vs 分组规则:这是最容易踩坑的地方

源码里有个特别关键的实现:

  • global rules(没有 group)会一起做 AND
  • group rules(绑定 group)先做 OR,再整体并到全局约束里

这是理解 Odoo 记录规则的核心。

直白翻译

假设你有:

  • 全局规则 A:只能看本公司数据
  • 全局规则 B:只能看 active 记录
  • 分组规则 C:销售员可看自己的单
  • 分组规则 D:经理可看本团队的单

最终效果更像:

A AND B AND (C OR D)

而不是所有规则都简单 OR 或简单 AND。

这就是为什么你“多加一条规则”后,结果可能不是放宽,反而更窄。


为什么多公司问题总像权限问题

ir.rule 源码里,_eval_context() 和错误构造逻辑都明确考虑了 company_id / company_ids

这意味着很多 AccessError 不是“你角色不够”,而是:

  • 记录属于别的公司
  • 当前启用公司上下文不对
  • 规则 domain 把记录过滤掉了

所以多公司场景下,用户说“我明明有权限,为什么打不开”,你第一反应应该是:

这是不是 company context 问题?

而不是立刻改 group。


_compute_domain():Odoo 真正把规则拼起来的地方

IrRule._compute_domain() 里,Odoo 会:

  1. 找出当前模型、当前操作模式匹配的规则
  2. 区分全局规则和分组规则
  3. 计算每条规则的 domain
  4. 组合成最终 domain
  5. 再去 search / read 时统一应用

所以 record rule 不是“报错时才检查一次”的小钩子,它其实深度参与日常查询。

这也是为什么规则写差了,不只是报错,性能和列表结果也会一起出问题。


为什么有时是“看不到”,有时是“点进去报错”

这取决于规则是在搜索阶段就把记录过滤掉了,还是在后续操作阶段才触发失败。

典型表现:

  • 列表里压根没有 → search 时 domain 已经拦掉
  • 列表能看到但点开报错 → 某个后续 read/write 操作命中了更严格约束

源码里 _make_access_error() 还会尽量拼出更友好的报错信息,甚至提示可能是 multi-company 问题。

所以前端症状不同,底层触发位置也可能不同。


sudo() 为什么既好用又危险

你在 Odoo 源码里会经常看到 sudo()

它的作用很直接:

  • 跳过普通用户权限边界
  • 让系统流程能继续推进

它适合用在:

  • 业务流程内部创建下游对象
  • 系统维护或后台计算
  • 需要稳定履约但不希望卡在操作者个人权限上的场景

但危险也非常明显:

  • 如果你在不该 sudo() 的地方用了它
  • 用户原本不该看到的数据也可能被带出来
  • 定制代码会悄悄绕过 record rule

所以经验法则是:

对执行对象适度 sudo(),对用户可见结果保持克制。


一套特别实用的排查顺序

遇到权限问题时,不要上来就乱加 group。按这个顺序看:

1. 用户属于哪些 groups

先确认入口包。

2. 模型级 access rights 有没有对应 CRUD

没这层就别往下想。

record rule 是分 mode 的。

4. 当前模型有哪些生效的 ir.rule

尤其注意全局规则。

5. 规则 domain 里是否带了 company_iduser.id、负责人字段

这往往是根因。

6. 是否有 sudo() 或定制代码改变了安全边界

很多“偶发权限问题”其实是定制代码把链路切裂了。


一句话记忆法

把 Odoo 权限体系记成一句话:

Access Rights 负责决定你能不能操作这个模型,Record Rules 负责决定你能操作这个模型里的哪些记录。

抓住这句,80% 的权限问题都会立刻变得可分析。

DISCUSSION

评论区

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