权限边界

Odoo 用户明明能看到记录,为什么还是不能改:ACL、记录规则与 sudo 边界讲透

很多实施和开发同学都会遇到同一种困惑:列表里看得到,表单也能打开,但一保存就报权限错。结合 models.py 里的 _check_access 实现,这篇文章把 ACL、记录规则、操作类型和 sudo 的边界拆开讲清。

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

Odoo 权限问题里,最让人烦的不是“完全没权限”,而是这种半吊子状态:

  • 列表能看到;
  • 详情能打开;
  • 但一保存就报错;
  • 或者能改自己那几条,改别人的就不行。

很多人这时会说一句:

不是都给权限了吗?

问题就在这里。Odoo 里的“给权限”至少分两层:

  1. ACL(ir.model.access):这个模型能不能做 read/create/write/unlink
  2. 记录规则(ir.rule):这个用户对“哪些记录”能做这些操作。

/home/ubuntu/odoo-temp/odoo/orm/models.py_check_access() 看,Odoo 的判断顺序其实很直接,但也很容易被误解。

一、先过 ACL,再过记录规则

_check_access() 的源码先调:

  • ir.model.access.check(model, operation)

如果模型级别都没给 write,那根本不会继续往下。

只有模型操作被允许,Odoo 才会继续算记录规则 domain:

  • Rule._compute_domain(self._name, operation)

然后再拿当前记录集去套这个 domain。

这意味着:

  • ACL 决定“这个动作是否原则上允许”
  • 记录规则决定“这批记录里哪些真的允许”

所以“能读不能写”并不奇怪,因为 readwrite 本来就是两套独立判断。

二、为什么“能看到”不等于“能修改”

很多新手默认以为:

  • 看得到 = 肯定能改

这在 Odoo 里完全不成立。

因为记录规则本来就是按操作类型算 domain 的。也就是说,同一条记录可能出现这四种组合:

  • 能读,不能写;
  • 能读,能写;
  • 能写(通常也能读),但不能删;
  • 能创建,但创建后不一定还能改全部字段关联的数据。

尤其是业务里常见这种规则:

  • 所有人能看本部门记录;
  • 只有负责人能改;
  • 只有经理能删。

这不是系统不一致,而是权限模型本来就这么设计。

三、记录规则是对“记录集合”做筛选,不是对按钮做装饰

_check_access() 很值得注意的一句是:

  • 先算 domain;
  • 再用 self.sudo().with_context(active_test=False).filtered_domain(domain) 过滤允许记录;
  • 最后把禁止的那部分记录拿出来报错。

这说明记录规则的本质不是“页面上某个按钮灰不灰”,而是:

这次操作提交到 ORM 时,当前 recordset 里到底哪些记录落在允许域内。

所以前端即便把按钮放出来,最终后端还是会拦。

这也是为什么很多权限问题一定要在 ORM 层排查,不能只看界面。

四、sudo() 到底绕过了什么

源码里 _check_access() 一开头就有一个重要前提:

  • if not self.env.su ...

也就是说,当环境是 superuser 模式时,常规访问校验会被跳过。

这也是大家对 sudo() 又爱又怕的原因:

sudo() 的确能绕过权限检查

它可以绕过:

  • 模型访问权限;
  • 记录规则。

sudo() 不是“业务正确性”开关

它不会自动解决:

  • 多公司一致性;
  • 字段约束;
  • 业务状态机限制;
  • 你把不该写的数据写进去了以后留下的后遗症。

所以实战里最危险的不是不会用 sudo(),而是:

一遇到 AccessError 就立刻全链路 sudo()

短期能过,长期往往把权限边界直接打穿。

五、最常见的三种误判

1)“表单能打开,所以写权限没问题”

错。打开表单通常只说明你有 read

2)“我已经给了一个组写权限,为什么还报错”

错在只看 ACL,没看记录规则是不是把那条记录排除了。

3)“加个 sudo 就算修好了”

很多时候这不是修复,是绕过。

如果业务本意就是“普通用户只能改自己的记录”,那用 sudo() 把别人的也改了,等于把系统设计推翻了。

六、一个更实用的排查顺序

如果你遇到“能读不能写”,我建议按这个顺序查:

  1. 模型 ACL:这个用户所属组到底有没有 write
  2. 记录规则 domainwrite 的规则和 read 的规则是不是不同;
  3. 当前记录是否命中 write domain
  4. 是否在多公司上下文下切错了 allowed companies
  5. 是不是某段自定义代码提前 sudo() 或反而丢了上下文

这个顺序比一上来改 XML 按钮、改前端显隐、或者直接全局 sudo() 靠谱得多。

七、什么时候该用 sudo(),什么时候不该

适合用 sudo() 的场景

  • 系统后台自动任务;
  • 确实由系统代办、且业务上应越过当前用户边界的动作;
  • 技术性同步、落日志、内部桥接。

不适合直接 sudo() 的场景

  • 用户手动编辑业务单据;
  • 你只是懒得梳理记录规则;
  • 你并不确定越权后果。

更稳的做法通常是:

  • 先把规则设计清楚;
  • 必要时只在局部对象、局部步骤上 sudo()
  • 并明确为什么这里允许系统越权。

总结

Odoo 的“能看不能改”不是怪现象,而是权限模型的正常表现。

从源码看,关键逻辑非常清楚:

  • 先看 ACL,判断这个模型操作是否允许;
  • 再看记录规则,判断这批记录是否允许当前操作;
  • readwriteunlink 天生就是不同问题;
  • sudo() 能绕过检查,但不等于业务上就应该绕过。

如果只记一句,就记这句:

在 Odoo 里,看得见的是“读权限结果”,改得动才是“写权限结果”。两者从来不是同一件事。

DISCUSSION

评论区

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