跳至内容

第3章 用户、用户组与公司上下文

3.1 核心关系图

res.partner  <--- _inherits / partner_id ---  res.users
                                                   |
                                                   | res_groups_users_rel
                                                   v
                                              res.groups
                                             /    |     \
                                    implied_ids   |      +--> menu/view access
                                                 +----------> ACL / record rules

res.users ---- company_id ------> res.company       当前主公司
          ---- company_ids -----> res.company       允许切换的公司集合

res.users 与 hr.employee 不是同一个概念;安装 hr 后通过用户/员工关系连接。

3.2 res.users 的设计

res.users 使用委托继承 _inherits = {'res.partner': 'partner_id'},源码起点为 odoo/addons/base/models/res_users.py:318-399。这意味着姓名、邮箱、地址等联系人属性来自关联的 res.partner,但用户并不等于员工。

重要字段:

字段 类型 作用 权限设计注意点
partner_id Many2one 委托继承的联系人 删除受 restrict 约束;不要把联系人权限等同用户权限
login Char 登录标识 数据库唯一
password / new_password Char 凭证维护入口 不属于业务授权配置
active Boolean 是否启用 归档不是完整的授权审计
groups_id Many2many 用户实际拥有的组 包含直接和物化后的隐含组,原表不保存来源
share computed 外部共享用户标识 本质上由是否拥有 base.group_user 计算
company_id Many2one 默认/主公司 必须属于 company_ids
company_ids Many2many 用户可用公司 不等于本次请求已启用公司
action_id Many2one 登录后的 Home Action 只是入口,不授予目标模型权限

新内部用户默认复制模板用户 base.default_user 的组,见 res_users.py:357-363odoo/addons/base/security/base_groups.xml:25-32,50-62。给模板用户新增组时,源码还会把新增项同步给现有内部用户;删除组没有对称的批量移除行为,见 res_users.py:711-756。因此生产系统中修改模板用户不仅影响以后新建用户,也可能立即扩大现有用户权限,必须纳入变更预览和审批。

3.3 内部、门户和公共用户

三种基本用户类型由以下组表达:

类型 典型组 用途
内部用户 base.group_user 后台员工用户
门户用户 base.group_portal 已登录的外部伙伴
公共用户 base.group_public 未登录网站访问共享身份

这三个组位于 base.module_category_user_type,用户类型要求互斥;约束会连同隐含组一起检查,源码见 res_users.py:605-637。公共用户是共享身份,不能把某个访问者的个体授权简单存到 Public user 上。

share 不是第四种用户类型。它按用户是否拥有内部用户组计算,用于区分内部与外部共享用户,见 res_users.py:529-534

3.4 res.groups 的重要字段

模型定义在 odoo/addons/base/models/res_users.py:175-200

字段 作用
name 组名;同一分类下唯一
users 组中的用户,groups_id 的反向关系
category_id 应用/权限分类,也影响用户表单展示方式
model_access 该组关联的 ACL
rule_groups 该组关联的非全局记录规则
menu_access 该组关联的菜单
view_access 该组关联的视图记录
share 用于共享数据的组标记
implied_ids 当前组直接隐含的组
trans_implied_ids 传递闭包计算结果

组应表示稳定、可复用的技术能力,如“销售用户”“销售经理”“会计开票”。不要为每个员工、每张单据、每次临时授权都建一个组,否则组数量、规则 SQL 和运维复杂度会迅速失控。

3.5 隐含组不是纯运行时继承

implied_ids 表示“拥有 A 组就自动拥有 B 组”。Odoo 会计算传递闭包,并把隐含组实际写入 res_groups_users_rel

销售经理
  implied_ids -> 销售用户
                    implied_ids -> 内部用户

最终用户的 groups_id 中会实际存在三个组。

核心源码:

  • res_users.py:1441-1455implied_ids 和传递闭包。
  • res_users.py:1468-1496:递归 SQL 物化隐含组。
  • res_users.py:1557-1589:用户 create/write 时补全隐含组。

这带来两个后果:

  1. ACL 与规则查询很快,因为用户组已经物化。
  2. 原生关系表不记录“直接授予、由哪个岗位授予、由哪条继承路径得到”,所以撤销和权限溯源困难。

修改 implied_ids 时还要注意删除语义。普通 write 主要负责补加;设置项使用 _remove_group() 才会判断其他继承路径后清理用户关系,见 res_users.py:1510-1527。增强模块若需要可靠来源,不能只依赖原生 M2M 表。

3.6 用户表单中的权限字段为什么找不到数据库列

用户表单上常见的权限复选框和单选项,其字段名类似:

in_group_42
sel_groups_10_11_12

它们是动态生成的虚拟字段,不是数据库列:

  • res_users.py:1591-1610 说明命名规则。
  • res_users.py:1648-1779 动态重写 base.user_groups_view
  • res_users.py:1953-1979 把虚拟字段转换回 groups_id 的 M2M 命令。

category_id 和组之间的 implied 关系会影响显示成 checkbox 还是互斥 selection。排查“用户表单为什么只允许选一个权限级别”时,要看分类与组包含关系,而不是寻找 sel_groups_* 数据库字段。

3.7 root、Access Rights 和 Settings 的区别

身份/组 含义 是否自动绕过 ACL/规则
UID 1 / env.su=True 超级用户模式
base.group_erp_manager Access Rights 管理权限
base.group_system Settings 管理权限

Settings -> Access Rights -> Internal User 的继承链定义于 base/security/base_groups.xml:10-28env.is_admin()env.is_system() 会识别这些组,但它们不等于 env.su,见 odoo/api.py:656-668

3.8 用户读取和修改自己的特例

普通内部用户未必拥有通用的 res.users 管理权限,但仍需要修改自己的语言、时区、签名和当前公司。Odoo 为“当前用户自己的记录”定义了白名单:

  • SELF_READABLE_FIELDS:包括 logingroups_idcompany_id、语言、时区、头像等。
  • SELF_WRITEABLE_FIELDS:只包括签名、Home Action、当前公司、邮箱、姓名、头像、语言和时区等较安全字段。

定义见 odoo/addons/base/models/res_users.py:337-355,特殊 read/write 处理见 res_users.py:645-665,720-788。满足“只操作本人且字段全部在白名单”时,模型内部会以最小白名单 sudo 完成操作。

groups_id 可供用户读取以判断界面能力,但不在本人可写白名单中。增强 res.users 时若向这些属性追加字段,必须分别评估信息泄露和自助修改风险;不能因为字段只是“偏好设置”就同时加入读写白名单。

老赵解读

中国企业常把“部门、岗位、职级、角色、公司”都塞进用户组,短期配置快,长期一定会形成组爆炸。原生组更适合表达稳定的技术能力;组织归属、岗位任职、临时代理和数据范围应当保留各自语义,再投影成必要的原生组。这样才能回答“为什么有权、权从哪里来、何时失效”。

额定值
0 0

目前没有任何评论。

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