本文内容
Odoo 20 权限迁移:如何验收多公司隔离?
下文虚构示例中,用户和记录都没有变,一次简单粗暴的权限改写却把“拒绝”变成了“允许”。业务政策没有改变,授权组合方式变了。
只有获批角色与公司矩阵在升级数据库上通过允许和拒绝两类用例,才能接受 Odoo 20 权限迁移。 先检查转换后的授权及全局限制,再分别验证已激活公司范围与当前公司取值。安装成功或管理员能浏览页面,都不能提供这些证据。
资料核查日期:2026年11月5日。 我们检查了 Odoo 19、20 官方文档、20.0 访问实现及源码转换脚本,并运行了 16 个虚构 Python 政策用例。没有运行 Odoo、数据库升级或官方转换器。下载的原生验收矩阵仍为 NOT_RUN。
Odoo 20 的访问控制发生了什么变化?
Odoo 20 将操作和记录域纳入同一个访问模型。权限迁移需要审查转换后各项授权组合起来的含义,不能只看改名后的安全文件能否加载。保留获批政策作为参照,并逐个角色比较实际结果与预期要求。
官方 20.0 安全参考以组、操作和域定义 ir.access。带组条目授予访问,无组条目限制访问。19.0 参考则描述模型级 ACL 授权之后再应用记录规则。
清点自定义安全记录及依赖它们的业务方法。记录每条配置实现哪项政策。例如,“负责人可读本公司自己的案例”和“审核人可读已激活公司的全部案例”并不相同,即使两类用户都能打开同一菜单。
Odoo 迁移验收指南覆盖数据和流程延续。本文聚焦权限:订单迁移得再准确,如果错误身份也能读取,仍不可接受。
组授权和全局限制怎样组合?
对于普通访问检查,一条记录需要至少命中一个组授权,并满足所有适用的全局限制。示例中,宽泛授权会增加可读记录,而全局公司限制仍约束其范围。单独的限制不会授予访问。应测试组合效果,不能只逐条检查配置。
模型将决定表示为:
至少一个匹配的组授权
并且
满足每一项适用的全局限制虚构组 own_reader 只授权用户 7 读取自己拥有的记录。独立的全局限制将记录范围限定在已激活公司。用户可读 A-OWN,不能读 A-OTHER。增加 broad_reader 后,第二条也变得可读,因为出现了另一项匹配授权。
现在保持公司 B 未激活。全局公司限制存在时,宽泛组仍不能读取 B-OWN。在故意错误的模型配置中移除该限制,同一记录就可读了。这项测试暴露的是示例缺口,不表示 Odoo 默认缺少这条边界。
还要测试合理的允许访问。示例并列要求“只能 A”和“只能 B”的两条全局限制,会阻止本来合法的记录。如果只测禁止访问,可能把这种坏配置当作成功。每项重要拒绝都应搭配一个允许对照,证明用户仍能工作。
测试应记录哪些公司上下文?
分别记录用户获授权加入的公司、已激活公司和当前公司。它们回答不同问题:用户可选择哪些公司、本次操作使用哪个范围、哪些依赖上下文的值来自哪家公司。缺少这些信息,验收结果就无法解释某条记录为何可见或被隐藏。
20.0 访问域求值实现从 env.companies.ids 取得 company_ids,即已激活公司;从 env.company.id 取得 company_id。记录测试范围时,不要用用户全部公司成员资格代替它。
| 维度 | 示例 | 验收问题 |
|---|---|---|
| 获授权的成员资格 | 用户 7 属于 A 和 B。 | 这个身份能否选择公司 B? |
| 已激活公司 | 本次调用只激活 A。 | 按公司限定的操作是否排除 B 记录? |
| 当前公司 | A、B 均激活,当前为 A。 | 操作使用哪家公司的值? |
| 记录所属公司 | B-OWN 属于 B。 | 结果记录是否属于预期公司? |
环境实现会为普通用户验证所选公司;列表为空时采用回退值;在 sudo 模式跳过所述合理性检查。空列表并不表示拒绝全部公司。使用较高权限运行测试,不能证明普通用户隔离成立。
模型将差别直接展示出来:成员资格是 A/B,只激活 A 时,自己的 B 记录被拒绝;激活 A/B 后允许。只有 A 成员资格的用户,在模型中不能激活 B。应保留三个独立用例,而不是统一写成“多公司正常”。
简单改写 ACL 为什么可能扩大访问?
一种错误改写会把旧的宽泛模型授权保留为新的宽泛记录授权,再与较窄授权并列。反例用户持有两个组。旧的分层政策拒绝他人记录,新授权的并集却允许读取。这说明错误改写的后果,不是 Odoo 官方转换器的执行结果。
| 阶段 | 示例政策 | A 中他人拥有的记录 |
|---|---|---|
| 旧分层示例 | 模型 ACL 允许读取,再由适用的本人记录规则过滤。 | 拒绝 |
| 简单的新改写 | 宽泛记录授权与本人记录授权组合。 | 允许 |
| 获批目标政策 | 业务负责人仍要求该角色只能访问自己的记录。 | 必须继续拒绝 |
官方访问转换源码会结合组进行推断,并对宽泛 ACL 组与较窄规则的交互发出警告。警告说明要求复核。我们检查了源码,但没有执行转换器。
将转换器输出和日志与迁移证据一起保存。针对每条警告,确定受影响的实际组组合及操作,再在升级数据库上证明获批结果。即使转换日志没有警告,也仍需回归测试业务政策。
不要据此一律删除宽泛授权,其他角色可能合理需要它。修改共享组前,应比较完整获批矩阵,包括从另一角色继承该组的用户。
16 个本地用例观察到了什么?
16 个本地用例均得到预期观察结果。它们展示测试如何区分缺失限制、额外授权和错误公司上下文,也把可见性、取值与关系一致性分开。这些是小型 Python 模型的结果,不是对真实 Odoo 数据库的安全评估。
| 用例 | 预期 | 观察结果 |
|---|---|---|
| 已激活 A 中的本人记录 | 允许 | 允许 |
| 已激活 A 中的他人记录 | 拒绝 | 拒绝 |
| B 已授权但未激活 | 拒绝 B 记录 | 拒绝 |
| B 已授权且已激活 | 允许本人 B 记录 | 允许 |
| 无成员资格却激活 B | 拒绝上下文 | 上下文错误 |
| 增加宽泛组授权 | 他人的 A 记录变为可读 | 允许 |
| 宽泛组加全局公司限制 | 未激活 B 仍被拒绝 | 拒绝 |
| 省略全局公司限制 | 错误配置暴露未激活 B | 允许 |
| 互相矛盾的仅 A、仅 B 全局限制 | 合法 A 记录被阻止 | 拒绝 |
| 无公司归属的共享记录 | 严格政策拒绝;明确共享政策允许 | 拒绝/允许 |
| 只有全局限制,没有组授权 | 拒绝 | 拒绝 |
| 模型中的只读角色 | 允许读;拒绝写和删除 | 允许/拒绝/拒绝 |
| 简单转换旧分层政策 | 旧拒绝变为非预期允许 | 拒绝/允许 |
| 当前公司为 A 或 B 时,B 记录均可见 | 公司值不同:100.00 与 120.00 | 100.00/120.00 |
| 可见 A 记录关联 B 记录 | 访问通过;示例关系检查失败 | 允许/不一致 |
| 已激活公司输入为空 | 回退到成员资格 A/B | B 记录可读 |
下载可运行权限模型和已记录结果。使用 Python 3.10 或更新版本运行:
python3 permission-model.py模型将已经展开的实际组集作为输入,不实现组继承、Odoo 域、字段权限、缓存或原生创建与写入钩子。关系检查刻意简化为相等比较。它无法证明真实模块里每个可共享字段或公司依赖字段的行为。
原生角色与公司矩阵应包含什么?
原生矩阵需要为每项相关身份、公司上下文和操作确定获批预期,再附上升级系统的证据。将正常允许工作与拒绝访问配对。对禁止写入,除错误信息外还应检查最终状态。菜单缺失的截图无法证明底层操作受到控制。
建议的原生验收矩阵全部从 NOT_RUN 开始。示例预期需要批准或替换,并非通用 Odoo 政策。用于发布决策前,应填写实际用户和组、构建版本、入口、记录标识、公司上下文及证据。
| 维度 | 必要对照 | 证据 |
|---|---|---|
| 角色 | 普通所有者与获批的宽范围审核人 | 各身份的实际组集及预期记录集合 |
| 公司范围 | 仅 A、A/B 激活及未获授权的 B 选择 | 准确上下文与目标记录所属公司 |
| 操作 | 允许读写与禁止修改 | 返回记录、结果状态及禁止变化未发生的证据 |
| 入口 | 用户操作与各相关集成或任务 | 实际执行身份及所调用操作 |
| 政策迁移 | 旧获批预期与新目标预期 | 转换器警告、已审查变化和原生观察结果 |
| 业务一致性 | 正确公司值及允许的关联记录 | 取值与关系证据,独立于可见性 |
除正常导航外,也使用直接记录标识测试。为每种入口定义预期观察,例如禁止搜索应不返回其他公司记录,或请求的操作应被拒绝。界面隐藏了多余行,不能成为接受宽泛结果的理由。
破坏性操作只在演练环境中的受控测试记录上执行。加入允许修改的用例,避免“全部阻断”也能通过。代理业务状态测试指南将同一证据原则扩展到完整任务,因为看似合理的回复可能掩盖错误记录。
访问通过后,为什么还要检查值和关联?
访问通过,表示该身份可按被测政策执行操作。它本身不证明操作使用了目标公司的值,也不证明所选关联兼容。示例可以读取 B 记录,却选中 A 的值;也可以允许访问一条记录,但其新关联未通过独立公司检查。
官方多公司指南将公司依赖值与当前公司关联,并描述通过 _check_company_auto 和 check_company 单独配置关系检查。应在模块的具体模型和字段上验证这些机制。
示例中两家公司均可读。当前公司选 A 时得到虚构值 100.00,选 B 时得到 120.00。任务若针对 B,成功读出 100.00 仍可能是错误业务结果。应在预期数值旁记录预期公司。
关系测试应选择一个允许链接和一个被禁止的跨公司链接,检查实际赋值操作。可见的公司字段或访问域,不能证明所有自定义关系均被检查。自定义 Odoo 模块审计指南覆盖更广泛的实现审查。
哪些证据应阻止或允许发布?
如果非预期身份能访问保护记录、合法用户无法完成必要工作,或无法确认被测公司上下文,就应阻止受影响的迁移范围。只有获批矩阵在目标构建通过、刻意政策变化已签核、未结转换警告已有具体处理决定,才应放行。
将预期矩阵、实际组集快照和准确模块版本与观察结果一起保留。扩大审核角色权限可能合理,但需要明确政策变更。旧拒绝不一定是正确的新规则,旧允许也不一定值得保留。
发布前 QA 检查表将这些证据纳入整体上线决定。Odoo API 架构指南继续负责托管和协议边界;改变 API 路径不能替代原生权限测试。
讨论 Wavect 的软件质量保障服务时,可准备一个受影响角色、两个公司上下文和自定义模块转换输出。首个有用成果是一份商定的允许/拒绝矩阵,以及对应范围内可复现的原生证据。
