框架深潜

Odoo 邀请注册为什么不只是“发一封设密码邮件”:token 认领、模板用户复制与重置链路边界讲透

很多人把 Odoo 的 auth_signup 理解成“用户点链接,填个密码,账号就开好了”。但源码里的真实重点,其实是 signup token 绑定的是 partner 还是现有 user、未邀请注册是否被允许、Template User 复制了什么,以及 reset password 和 invite 流程为什么共用一套 token 机制却不是一回事。

框架
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 的 auth_signup 不是一个“漂亮的注册页面模块”,而是一套把外部点击动作落回到 partner / user 生命周期的账户认领机制。

/home/ubuntu/odoo-temp/addons/auth_signup/models/res_users.pycontrollers/main.py 来看,最关键的不是页面长什么样,而是下面这几件事:

  1. signup token 实际上是先指向 res.partner,再决定要不要落成 res.users
  2. 同一个 token 机制,同时服务于“首次认领账号”和“重置已有账号密码”。
  3. 未受邀用户能不能自己开户,取决于 invitation_scopeb2b 还是 b2c
  4. 首次开户时复制哪一个 Template User,决定了新账号的默认角色轮廓。

所以,auth_signup 的本质不是“开放一个注册入口”,而是定义 Odoo 账户到底如何被认领、激活和找回。

为什么 Odoo 先找 partner,而不是直接找 user

signup(values, token=None) 这段逻辑非常能说明问题。

如果传入 token,Odoo 会先调用:

res.partner._signup_retrieve_partner(token, check_validity=True)

也就是说,token 首先对应的是一个 partner 语义,而不是简单的“某个用户名字符串”。

这有两个直接后果:

1. 邀请注册是“认领业务主体”

在很多业务场景里,partner 早就存在了:

  • 客户联系人已经在系统里;
  • 员工档案已录入;
  • 供应商联系人已建档;
  • 门户用户将来要看的订单 / 发票 / 项目,也都挂在 partner 上。

所以邀请链接不是“凭空创建一个陌生账户”,而是在说:

你现在来认领这条已经存在的业务身份。

2. token 是否落成新用户,要看 partner 下面有没有 user

源码里接着判断 partner.user_ids

  • 如果已有 user:这是“设密 / 重置 / 激活已有账号”路径;
  • 如果还没有 user:这是“第一次从 partner 落成 user”路径。

这条分叉非常关键,因为它解释了为什么你看到的页面很像,但背后并不是同一件事。

邀请、注册、重置为什么“共用机制但不是一回事”

很多团队上线时会把这三件事混在一起:

  • 邀请用户入驻;
  • 公开开放注册;
  • 忘记密码找回。

Odoo 确实复用了很多相同基础设施:

  • 都可能生成 signup token;
  • 都会发邮件;
  • 都可能走 do_signup()
  • 最终都可能写入密码。

但它们的业务前提完全不同。

邀请注册

管理员先创建 / 选定主体,然后发链接。重点是:谁有资格认领这个身份

开放注册

访客自己发起。重点是:系统是否允许未受邀开户

忘记密码

账号已存在,只是重建密码。重点是:邮箱所有权与找回安全

源码把它们放进一套框架里处理,但并没有把三者混成同一个概念。实施时如果你也把它们混了,后面权限与安全边界一定会乱。

invitation_scope 才是真正的“开放程度开关”

_get_signup_invitation_scope() 读取的是:

auth_signup.invitation_scope

默认值是 b2b

_signup_create_user() 里,若 values 里没有 partner_id,Odoo 会认为这是未受邀自注册;这时如果 scope 不是 b2c,就直接抛:

Signup is not allowed for uninvited users

这条边界非常清晰:

  • b2b:默认偏邀请制;
  • b2c:允许公开注册。

所以很多人以为“装了 auth_signup 就自动开放注册”,其实不对。模块提供了能力,但是否开放,要靠配置明确授权。

Template User 为什么是整条链路里最危险也最容易忽略的角色

当 partner 还没有 user 时,Odoo 会走 _create_user_from_template()

这里会读取:

base.template_portal_user_id

然后以这个模板用户为蓝本执行 copy(values)

这说明首次开户不是“生成一个空白用户”,而是复制一个预定义角色轮廓,再覆盖 login / partner_id / name / email 等关键字段

这个设计很实用,因为它让 Odoo 可以快速给新用户带上:

  • 基础组权限;
  • 默认公司;
  • 默认语言或其它继承值。

但风险也恰恰在这里:

如果 Template User 选错,所有新用户的默认安全边界都会一起偏掉

常见问题包括:

  • 模板用户权限太大,新开户用户天然越权;
  • 模板太“空”,新用户首登后一堆页面打不开;
  • 多公司字段继承混乱,导致一开始就看到不该看的公司;
  • 门户与内部用户模板混用,造成角色错位。

所以 auth_signup 里最不该被当成“系统默认值随便用”的,其实就是 Template User。

为什么 token 用完后要立刻失效

signup(token=...) 的 token 路径里,Odoo 会先执行:

partner.write({'signup_type': False})

这一步很值得注意。它意味着 token 不是长期凭证,而是一次性认领凭证

安全上这非常合理,因为如果链接被长期复用:

  • 邀请链接会变成半永久后门;
  • 重置密码链接可能被旧邮件再次打开;
  • 首次设密完成后,链接残留就会增加劫持风险。

所以 Odoo 的意图很明确:

token 用来打开一次“账号认领窗口”,不是长期替代登录。

为什么现有用户和新用户写入的字段不一样

源码对这两种情况做了非常明确的分流。

partner 已有 user:更像“更新现有账号”

如果 partner_user 已存在,Odoo 会主动把 loginname 从提交值里弹掉,不允许随便覆盖这些关键身份字段,然后只写允许更新的值,比如新密码等。

这说明邀请 / 重置并不是“用户拿到链接后想改什么都行”,而是:

  • 身份主语已经确定;
  • 链接只是允许你完成激活或重建口令。

partner 还没有 user:才是真正的“首次落库”

这时 Odoo 会把 namepartner_idemail、company 信息等补齐,再调用模板复制逻辑。

也就是说,新建用户和更新旧用户在源码里从来不是一条含糊的流程,而是两类完全不同的对象转换。

reset password 为什么不是单独一套系统

reset_password(login) 的逻辑看似简单,但非常典型:

  1. 先按 login 查;
  2. 不行再按 email 查;
  3. 没找到报错;
  4. 找到多条也报错;
  5. 只有唯一命中,才继续 action_reset_password()

而真正发邮件时,_action_reset_password(signup_type="reset") 仍然会走 partner.signup_prepare(...) 生成 token,再发带链接的邮件。

这说明“忘记密码”本质上也是一种受控的账号再认领

区别只是:

  • 邀请设密是为了首次激活;
  • reset 是为了恢复已有账户控制权。

框架一样,语义不同。

控制器层最重要的边界:页面公开,不代表账号一定公开

/web/signup/web/reset_password 都是 public route,但能不能真正完成动作,取决于 qcontext 里的配置和 token 状态。

例如:

  • 没有 token 且未开启 signup_enabled/web/signup 直接 404;
  • 没有 token 且未开启 reset_password_enabled/web/reset_password 直接 404;
  • token 无效,则页面会标记 invalid_token

这说明 Odoo 的思路不是“把入口藏起来”,而是:

  • 页面可访问;
  • 但真正的业务动作必须满足配置与 token 有效性。

这是一种很典型的平台式安全设计:公开路由 ≠ 公开权限

实施里最值得提前做的 5 个决定

1. 你的系统到底是邀请制还是开放注册

不要模糊处理。

  • ToB / 内部系统:大多数时候应该留在 b2b
  • 真正面向陌生访客自助入驻:才考虑 b2c

2. Template User 到底是谁

这不是一个 UI 小配置,而是新账户默认安全轮廓。

3. 邮件是否可靠可达

如果出站邮件不稳定,用户视角里就是“系统没反应”。邀请 / 重置链路会显得极不可信。

4. login 的唯一口径是什么

是邮箱、员工编号、还是客户编号?如果口径不统一,reset_password 的唯一命中就可能出问题。

5. token 的有效期和业务节奏是否匹配

链接太短,用户来不及操作;太长,又会增加残留风险。你要让安全与实际使用场景对得上。

最后一句

很多人以为 auth_signup 的关键是“注册表单”。

其实从源码看,它真正管理的是:

  • 谁可以认领一个 partner 身份;
  • 认领时是新建 user 还是更新旧 user;
  • 默认复制什么角色模板;
  • 何时用 token 打开一次性设密窗口;
  • 何时只允许受邀,何时允许陌生人自己开户。

所以,Odoo 的邀请注册从来不是“发封邮件就完了”,而是一条标准化的身份认领链路。把这条链路看懂了,你才知道系统到底是在开户、激活,还是恢复控制权。

DISCUSSION

评论区

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