跳至内容

第14章 推荐实施路线与源码索引

14.1 适合一人或小团队的五阶段路线

阶段 0:学习、测试与只读权限解释器

目标:不改变 Odoo 最终授权,只把原生结果解释清楚。

交付物:

  • 本教材与 odoo_permission_lab 全部实验通过。
  • 用户、组、隐含组、ACL、全局规则、组规则关系图。
  • 给定 user/model/operation 的只读解释结果。
  • 菜单/动作/按钮“仅入口”风险扫描。
  • sudo()、公开方法、controller 的代码检查清单。

完成标准:解释器不 sudo 泄露业务记录;对官方 ACL/rule 测试语义一致;不修改 _compute_domain()

这个阶段适合先做成开源能力证明和内容资产,也能在真实咨询诊断中直接使用。

阶段 1:角色模板、来源和原生组投影

目标:先解决“全局岗位授权、来源可追踪、可预览和可撤销”,不宣称已经支持按公司/组织隔离的岗位。

交付物:

  • 1A:稳定 role 身份、不可变 role version、assignment、来源和差异预览,不改变最终授权。
  • 1B:仅对 managed allowlist 中的全局、低风险原生组做确定性投影。
  • 现有手工关系的基线迁移、直接写边界和漂移检测。
  • implied groups 展开后的 SoD 静态检查,以及传递闭包增删 reconcile 测试。
  • 变更预览、审批后生效、撤销和基础审计。
  • 原生手工组与增强模块管理组的边界。

此阶段只支持长期、立即生效的全局 assignment。可以预留有效期字段作为未来模型结构,但不能用它投影未来生效或定时到期的组;公司/组织字段此时只能标记为“声明范围,未运行时强制”。临时、代理、紧急和 company-scoped 角色均不在阶段 1 生效。

完成标准:多来源授权撤销正确;幂等 reconcile;同事务和跨 worker 缓存失效正确;不会扩大未映射 ACL;任何界面都不会把声明范围误报为已强制范围。

阶段 2:选择一个业务包做数据范围和 capability

建议只选一个:销售订单或费用报销。费用更适合验证本人、部门经理、直属上级、自批限制;销售更适合验证本人/全部、团队范围、折扣审批和多公司。

交付物:

  • 组织树和首批结构化 scope。
  • 该业务模型及关联行/报表的 adapter。
  • 3 至 5 个高价值 capability。
  • 在该业务包内真正强制 company/org-scoped assignment。
  • 对候选投影组展开 implied groups、ACL、规则和动作,形成完整授权面清单;优先创建窄技术组。
  • 直接 RPC、导出、报表、多公司的完整测试。
  • SQL 性能基线。

完成标准:原生 domain 始终保留;增强层按 OR_i(Assignment_i 的 capability + company + scope + limit) 计算后再与原生结果 AND;无有效 assignment 默认拒绝已 enforce 的能力;组织调动和撤销在提交后的新请求生效;候选组的每项传递授权都已受 adapter/capability 强制或书面接受为全局权限,主单、行、分析模型及组带出的其他模型/动作均无旁路。

阶段 3:审批、临时授权、SoD 和审计

目标:从权限配置工具升级为可治理的授权流程。

交付物:

  • 多级审批、临时、代理、紧急授权。
  • 静态/动态 SoD。
  • 周期性复核。
  • 追加式审计和拒绝事件外部日志。
  • 高风险角色和 capability 告警。

完成标准:运行时决策按精确时间拒绝过期 assignment;撤销提交后的新请求立即拒绝;临时授权不向未受保护的授权面投影扩权组;不能自批;异常回滚时拒绝审计仍可用;敏感字段脱敏。

阶段 4:外部组织与审批集成

目标:在核心模型稳定后接企业微信、钉钉、飞书或客户已有 IAM。

交付物:

  • 稳定 ID 映射、dry-run、幂等增量同步。
  • 冲突与停用人工复核。
  • 回调验签、防重放和审批版本校验。
  • Excel 草稿导入与差异预览。

完成标准:外部平台故障不导致权限被清空;重复回调不重复授权;名称变化不破坏身份映射。

14.2 何时才适合产品化

权限增强很容易过早平台化。建议先从咨询、诊断和一个受控业务包中验证重复问题,再扩大通用层。至少观察:

  • 是否有多家客户反复提出同一种岗位/范围/审批问题。
  • 是否完成至少两次有边界的付费交付。
  • 是否能明确哪些是通用内核、哪些是客户特例。
  • 是否有测试、文档、迁移和持续维护负责人。
  • 是否有真实的性能数据和升级兼容记录。

在达到重复验证前,优先沉淀模型适配器、测试矩阵、诊断报告和实施清单,不承诺“大而全中国式权限平台”。

14.3 模块边界建议

起步时不要拆太多仓库。一个可行演进是:

odoocc_auth                 核心角色、assignment、解释、审计
odoocc_auth_hr              只有确实依赖 hr 组织/员工时再拆
odoocc_auth_sale            销售 adapter 与 capability
odoocc_auth_expense         费用 adapter 与 capability
odoocc_auth_connector_*     外部平台稳定后再拆

MVP 可以只有核心模块加一个业务 adapter,等依赖与维护边界真实出现再拆分。

增强模块自身也是高敏感资源,应至少分离:

  • 权限配置管理员。
  • 授权审批人。
  • 权限审计员,只读且不能授权。
  • 系统技术管理员。

普通用户不能直接 write 自己的 assignment、role、scope 或审计事件。对这些模型谨慎使用 groupless ACL 和 sudo 关系命令。

14.4 权限核心模型速查表

模型 关键字段 核心职责
res.users groups_id, company_id, company_ids, share, action_id 运行时主体
res.groups users, implied_ids, category_id, model_access, rule_groups 技术能力集合
ir.module.category name, sequence, parent_id 组的应用分类与用户表单组织
ir.model model, name, state 模型元数据,ACL/rule 的目标
ir.model.fields model_id, name, ttype, relation 字段元数据;不是 Python 字段 groups 的完整替代配置
ir.model.access model_id, group_id, 四个 perm_*, active 模型 CRUD grant
ir.rule model_id, groups, global, domain_force, 四个 perm_* 记录范围
ir.ui.menu parent_id, groups_id, action 导航入口
ir.ui.view model, arch_db, inherit_id, mode, groups_id 页面架构与节点裁剪
ir.actions.act_window res_model, domain, context, groups_id 打开模型工作台
ir.actions.server model_id, state, code, groups_id 受额外执行检查的服务器动作
ir.actions.report model, report_name, groups_id, attachment_use 报表入口和渲染配置
res.company 公司主数据 多公司上下文,不等于任意组织节点

14.5 开发前安全设计模板

为每个新业务模型先填写:

模型:
用户类型:Internal / Portal / Public

ACL:
  哪些组有 R/W/C/U?为什么?

记录范围:
  全局强制规则:
  各组 read 规则:
  各组 write 规则:
  create 后新记录必须满足:
  unlink 范围:

字段:
  敏感字段:
  Python groups:
  create payload 加固:
  状态/额度字段写策略:

业务方法:
  公开方法:
  capability:
  状态前置条件:
  公司/组织条件:
  SoD:

旁路:
  Controller / portal token:
  Report / export / import:
  Attachments / related lines / analysis models:
  sudo / SQL:

测试:
  允许案例:
  拒绝案例:
  多公司:
  缓存与撤销:
  性能基线:

14.6 代码审查清单

  • [ ] 所有新模型都有明确组 ACL,没有意外 groupless grant。
  • [ ] ACL 矩阵按并集分析过,没有把 False 当 deny。
  • [ ] 全局规则和组规则分别列出,并写出最终公式。
  • [ ] 所有 perm_* = False 都按“不参与操作”复核过。
  • [ ] 强制公司/合规规则不会被组规则 OR 放宽。
  • [ ] create 验证敏感字段、初始状态、负责人和公司。
  • [ ] write 验证变更后的业务不变量,不只依赖旧记录规则。
  • [ ] Python 字段 groups 与视图节点 groups 没有混淆。
  • [ ] 每个 type="object" 公开方法都有服务端校验。
  • [ ] 窗口/报表 groups 没有被当成最终授权。
  • [ ] controller 对每个 docid 做访问或 token 校验。
  • [ ] sudo() 都有明确理由、最小范围并位于授权之后。
  • [ ] 原始 SQL 有等价权限和缓存/约束处理。
  • [ ] 主模型、关联行、附件、分析和报表模型都已覆盖。
  • [ ] 多公司 rule 与 _check_company 分别测试。
  • [ ] 组、部门、岗位、有效期变化会正确失效缓存。
  • [ ] 正向、反向、边界、批量和直接 RPC 测试齐全。

14.7 四周源码学习计划

第一周:用户、组和 ACL

  • res_users.py:175-415,1142-1209,1434-1610
  • 画组继承图并在 shell 观察 groups_id
  • ir_model.py:2006-2149
  • 完成实验 1、2,自己写三组 ACL 真值表。

第二周:记录规则和 CRUD

  • 逐行读 ir_rule.py:12-201
  • models.py:4370-4428,4476-4488,4618-4666,4895-5240
  • 完成实验 3 至 6、9。
  • 阅读销售和费用规则并写出最终组合 domain。

第三周:字段、视图、动作和按钮

  • fields.py:950-956models.py:3693-3782
  • ir_ui_menu.py:76-152ir_ui_view.py:989-1060
  • /web/action/load/web/dataset/call_button
  • 完成实验 7、8、10,写一个 HttpCase 验证直达 RPC/报表。

第四周:多公司、sudo、缓存和增强设计

  • api.py:529-746models.py:6215-6297
  • 做两公司规则、非法 context、sudo 回归。
  • 为一个真实业务模型做权限矩阵和 SQL 计划。
  • 只设计阶段 0/1 的增强 MVP,不急着实现全局动态规则。

14.8 复习题

  1. 用户没有模型 read ACL,但有一条匹配全部记录的 read rule,能否读取?
  2. 用户同时拥有两个组,一个 ACL read=True,另一个 read=False,最终能否 read?
  3. 两条全局规则和两条适用组规则如何组合?
  4. 规则 perm_write=False 是否禁止 write?
  5. 为什么用户能 search 到 0 条,却在 browse(id).read() 时收到权限错误?
  6. [('owner_id', '=', user.id)] write rule 能否阻止把 owner 改给别人?
  7. Python 字段 groups= 和 XML 节点 groups 有何不同?
  8. 按钮只给 Manager 显示,普通用户是否一定不能调用按钮方法?
  9. 窗口动作 domain 是否是数据权限?
  10. 服务器动作 groups 与窗口动作 groups 有何关键差异?
  11. 报表 Print 菜单隐藏后,为什么还要检查直达报表 URL?
  12. sudo() 是否会把 env.uid 改为 1?
  13. check_company=True 是否能代替公司记录规则?
  14. 为什么不能把岗位、部门、公司全部表示成 res.groupsres.company
  15. 多个业务岗位的 capability、公司和数据范围应怎样组合,才能避免跨岗位拼接放权?

14.9 参考答案

  1. 不能。ACL 先检查,规则不能授予模型能力。
  2. 能。ACL 是 grant 并集,False 不是 deny。
  3. Global1 AND Global2 AND (Group1 OR Group2)
  4. 不禁止;表示 write 时不应用该规则。
  5. search 把规则编入 SQL 静默过滤;显式 recordset 访问会检查禁止记录并抛错。
  6. 不能保证。write rule 检查旧记录,需在 write/业务方法中验证 owner 变更。
  7. Python 字段 groups 是 ORM 字段访问边界;XML groups 只裁剪该视图节点。
  8. 不一定。公开方法可直接 RPC,方法内必须复核。
  9. 不是,只是该入口的默认过滤。
  10. 服务器动作 run() 会再次检查允许组/写权限;窗口动作直达加载没有同等通用强制。
  11. 标准报表 groups 主要过滤绑定入口,直达 route 不统一强制该组。
  12. 不会。它保留 uid,只把环境设为 su;UID 1 是约定超级用户。
  13. 不能。前者管关联一致性,后者管记录可见性。
  14. 它们是技术能力、任职组织和法人账套三个不同维度,生命周期和组合方式不同。
  15. 先在每个 assignment 内把 capability、公司、scope、额度和 SoD 绑定为一个 AND 分支,再对完整分支做 OR;最后与原生权限和合规/业务不变量做 AND。不能分别合并 capability 与 scope。

14.10 核心源码索引

版本与 Environment

主题 源码位置
Odoo 18.0 版本 odoo/release.py:11-15
Environment 构造与切换 odoo/api.py:529-634
同事务 Environment 与共享 cache odoo/api.py:563-585,981-1025
superuser/admin/system odoo/api.py:656-676
当前公司与启用公司 odoo/api.py:678-746
sudo/with_user/with_company odoo/models.py:6226-6297

用户和组

主题 源码位置
res.groups 字段 odoo/addons/base/models/res_users.py:175-200
res.users 字段 odoo/addons/base/models/res_users.py:318-415
用户类型互斥 odoo/addons/base/models/res_users.py:605-637
用户本人可读写字段特例 odoo/addons/base/models/res_users.py:640-788
模板用户新增组同步到现有内部用户 odoo/addons/base/models/res_users.py:711-757
has_groups/has_group odoo/addons/base/models/res_users.py:1142-1209
隐含组物化 odoo/addons/base/models/res_users.py:1434-1589
动态用户组表单 odoo/addons/base/models/res_users.py:1591-1979
基础组继承链 odoo/addons/base/security/base_groups.xml:10-92

ACL、规则与 ORM

主题 源码位置
ir.model.access odoo/addons/base/models/ir_model.py:2006-2149
ir.rule 字段/求值/组合/缓存 odoo/addons/base/models/ir_rule.py:12-201
Odoo 18 统一访问入口 odoo/models.py:4370-4474
unlink odoo/models.py:4476-4616
write odoo/models.py:4618-4893
create odoo/models.py:4895-5240
公司一致性 odoo/models.py:4270-4368
搜索附加规则 odoo/models.py:5525-5538,5735-5763

字段、菜单、视图和动作

主题 源码位置
字段 groups 判定 odoo/fields.py:950-956
字段属性访问与 cache miss 检查 odoo/fields.py:1215-1266
fields_get/check_field_access_rights odoo/models.py:3693-3782
菜单字段与可见性 odoo/addons/base/models/ir_ui_menu.py:19-44,76-152
视图记录组约束 odoo/addons/base/models/ir_ui_view.py:422-428
视图记录访问检查及主要调用 odoo/addons/base/models/ir_ui_view.py:672-688,2176-2183
视图节点组编码与 ACL 后处理 odoo/addons/base/models/ir_ui_view.py:989-1154
视图缓存与加载 odoo/addons/base/models/ir_ui_view.py:2553-2776
动作基类与 Web 安全字段 odoo/addons/base/models/ir_actions.py:52-73,218-252
动作绑定过滤 odoo/addons/base/models/ir_actions.py:149-215
窗口动作字段 odoo/addons/base/models/ir_actions.py:302-327
服务器动作字段和 run odoo/addons/base/models/ir_actions.py:503-588,936-1011
报表动作字段/查找 odoo/addons/base/models/ir_actions_report.py:141-176,623-657
报表缓存附件/文档上下文 odoo/addons/base/models/ir_actions_report.py:248-261,774-814,1082-1097
QWeb 节点 groups odoo/addons/base/models/ir_qweb.py:1847-1864
顶层 template groups 转换 odoo/tools/convert.py:450-514
嵌入式动作可见性 odoo/addons/base/models/ir_embedded_actions.py:8-29,71-91

Web 入口

主题 源码位置
菜单 Web 加载入口 addons/web/controllers/home.py:76-88; addons/web/models/ir_ui_menu.py:12-88
动作加载 addons/web/controllers/action.py:22-51
动作结果清洗 addons/web/controllers/utils.py:23-47
按钮调用 addons/web/controllers/dataset.py:32-43
按钮 object/action 前端分支 addons/web/static/src/webclient/actions/action_service.js:1399-1443
前端组判断 addons/web/static/src/core/user.js:51-78,106-110
报表路由 addons/web/controllers/report.py:23-50
公开方法名检查 odoo/models.py:151-158
Controller auth 语义 odoo/http.py:661-701
HTTP CSRF 实际校验 odoo/http.py:2067-2078
Portal token 参考 addons/portal/controllers/portal.py:373-392

官方测试与业务示例

主题 源码位置
字段、ACL、规则基础测试 odoo/addons/base/tests/test_acl.py:13-303
search/read/write/unlink 表现 odoo/addons/base/tests/test_orm.py:55-95
视图 groups 测试 odoo/addons/base/tests/test_views.py:2328-2368
服务器动作组测试 odoo/addons/base/tests/test_ir_actions.py:394-428
sudo/with_user 测试 odoo/addons/test_access_rights/tests/test_feedback.py:23-87
ACL 拒绝提示测试 odoo/addons/test_access_rights/tests/test_feedback.py:89-158
全局/组规则测试 odoo/addons/base/tests/test_acl.py:194-303
多公司一致性测试 odoo/addons/test_new_api/tests/test_company_checks.py:46-212
销售规则 addons/sale/security/ir_rules.xml:5-97
费用规则 addons/hr_expense/security/ir_rule.xml:4-89

14.11 官方文档入口

官方文档适合建立概念;遇到版本差异时,以当前部署版本源码和自动化测试为准。

14.12 术语表

术语 本文含义
Authentication 身份认证,确认“你是谁”
Authorization 授权,确认“你能做什么”
ACL 模型级 CRUD grant,ir.model.access
Record Rule 记录范围 domain,ir.rule
Global Rule 无 groups 的规则,与其他全局规则 AND
Group Rule 有 groups 的规则,适用规则之间 OR
Capability CRUD 之外的业务操作,如审批、过账
Scope 本人、部门、公司等数据范围
SoD Separation of Duties,职责分离
Assignment 某用户在组织/公司/有效期内获得角色的生效授权
Projection 把业务角色确定性映射到原生 groups 等运行时结构
su 超级用户模式标志,不等同 Settings 组
allowed_company_ids 本次请求启用的公司上下文

14.13 最终心智模型

遇到任何权限需求,都把自然语言拆成下面一句:

谁(用户/角色/公司/组织)
在什么时间和来源下
对什么模型的哪些记录
能执行哪种 CRUD 或业务 capability
能访问哪些字段
入口如何展示
失败和变更如何解释、审计与回收

然后按固定顺序落地:

原生组 -> ACL 能力上限 -> 全局规则 -> 组规则
-> 字段 groups -> 业务方法 -> UI 裁剪
-> 多公司/sudo/controller 复核 -> 自动化测试 -> 审计解释

只要始终区分“能力、范围、字段、业务动作、入口”这五个维度,Odoo 权限系统就不再是一堆零散勾选,而是一套可以推导、测试和逐步增强的授权模型。

老赵解读

实现路线应从可验证的最小闭环开始:选一个模型、少量角色和几种典型数据范围,做出授权、执行、解释、到期和审计,再进入真实场景验证。只有同类问题在多个客户中重复出现,边界、测试和维护责任都清楚后,才适合产品化。权限增强的竞争力最终不是配置项多,而是让管理员看得懂、业务跑得通、审计说得清。

额定值
0 0

目前没有任何评论。

成为第一个发表评论的人。