Odoo 20 权限迁移:如何验收多公司隔离?

返回
Kevin Riedl

11 分钟 阅读 · 2026年11月5日
最近审核

下一篇
图片在你的设备上生成,不连接 Instagram。文章链接会复制到剪贴板,供链接贴纸使用。

下文虚构示例中,用户和记录都没有变,一次简单粗暴的权限改写却把“拒绝”变成了“允许”。业务政策没有改变,授权组合方式变了。

只有获批角色与公司矩阵在升级数据库上通过允许和拒绝两类用例,才能接受 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 数据库的安全评估。

2026年11月5日执行的虚构政策用例
用例预期观察结果
已激活 A 中的本人记录允许允许
已激活 A 中的他人记录拒绝拒绝
B 已授权但未激活拒绝 B 记录拒绝
B 已授权且已激活允许本人 B 记录允许
无成员资格却激活 B拒绝上下文上下文错误
增加宽泛组授权他人的 A 记录变为可读允许
宽泛组加全局公司限制未激活 B 仍被拒绝拒绝
省略全局公司限制错误配置暴露未激活 B允许
互相矛盾的仅 A、仅 B 全局限制合法 A 记录被阻止拒绝
无公司归属的共享记录严格政策拒绝;明确共享政策允许拒绝/允许
只有全局限制,没有组授权拒绝拒绝
模型中的只读角色允许读;拒绝写和删除允许/拒绝/拒绝
简单转换旧分层政策旧拒绝变为非预期允许拒绝/允许
当前公司为 A 或 B 时,B 记录均可见公司值不同:100.00 与 120.00100.00/120.00
可见 A 记录关联 B 记录访问通过;示例关系检查失败允许/不一致
已激活公司输入为空回退到成员资格 A/BB 记录可读

下载可运行权限模型和已记录结果。使用 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 的软件质量保障服务时,可准备一个受影响角色、两个公司上下文和自定义模块转换输出。首个有用成果是一份商定的允许/拒绝矩阵,以及对应范围内可复现的原生证据。

Odoo 20 权限迁移常见问题

菜单隐藏就证明底层访问被拒绝了吗?
不能。验收关注身份能访问的记录及操作。应使用受控记录测试正常用户路径和相关直接入口,保存返回数据与最终状态。菜单截图可以记录界面,却不能替代受保护操作确实遵守获批政策的证据。
allowed_company_ids 为空就能拒绝全部公司吗?
不能。在所检查的 20.0 环境实现中,空列表会回退到用户的公司。应指定明确允许的上下文并验证结果。如果应用需要无访问状态,须将它定义为政策结果并测试,不能依赖空上下文列表来实现。
没有公司归属的记录应怎样验收?
先决定哪些记录有意共享,哪些必须归属某家公司。模型分别测试严格公司政策与明确共享政策,结果不同。应为各获批类别准备代表记录及关系赋值。公司字段缺失,不应悄悄变成共享敏感业务数据的指令。
继承组和额外组应怎样处理?
审查被测身份的实际组集,包括通过其他角色获得的组。下载模型直接接收这个集合,不计算继承。应测试用户真正持有的组合,特别是受限角色与宽范围审核角色并存。单独测试任一组可能漏掉组合效果。
16 个模型用例能证明真实数据库安全吗?
不能。它们只在一个使用标准库的小型 Python 程序中展示特定政策及上下文结果,没有执行 Odoo、解释原生域或测试自定义方法。可用反例改善验收设计,再针对准确升级版本和获批业务政策运行独立的 NOT_RUN 矩阵。
服务账户和定时任务有什么不同?
记录每项自动化使用的身份和公司上下文,再测试获批操作。不能用人工管理员浏览替代该执行路径。加入应处理记录和一条不得触碰的受控记录,并检查实际修改。宽泛凭据需要明确业务理由和验收范围。
与 Odoo 19 不同就必须阻止发布吗?
未解释的差异应先阻止受影响范围,直到查明原因。有意修正政策可以获批,包括收紧旧的宽泛授权。记录旧预期、新目标规则及批准人,按获批目标比较升级系统,而不是只复制旧行为。

构建产品,而不只是 backlog

如果这篇文章对应的是一个真实产品决策,Wavect 可以用高级创始人级判断帮你界定范围、构建、加固或领导软件工作。

可选服务路径:

只收重要内容

关注与你相关的内容

每当我们发布新文章,你会收到一封简短邮件。你可以关注整个博客,也可以只选感兴趣的主题。

你希望接收哪些内容?
选择主题

免费、双重确认、不使用跟踪像素。

返回
Kevin Riedl

11 分钟 阅读 · 2026年11月5日
最近审核

下一篇

获取下一篇关于交付与 QA的一线笔记

有新文章时发送一封简短邮件,不使用跟踪像素,也不发送填充内容。

免费、双重确认、不使用跟踪像素。