API 邀请包含 group_names 参数时,除非用户是管理员,否则组所有者会收到 403 错误

摘要

一个非管理员用户,如果同时是某个 Discourse 组的所有者,可以通过 Web UI 的邀请对话框成功邀请新成员,并将该组分配给他们。然而,通过 REST API(/invites.json,使用全局范围的 API 密钥和正确的 Api-Username)执行完全相同的操作时,如果请求负载(payload)中包含了 group_names/group_ids,则会返回 403 invalid_access 错误。关键在于,将该用户提升为版主(Moderator)状态并不能修复 API 调用——只有授予完全的管理员(Admin)权限才能成功,尽管在测试过的其他所有场景(Web UI、无组 API 邀请)中,版主权限都是足够的。

环境

  • Discourse 版本:2026.7.1
  • API 密钥类型:全局范围,CheriArchives = 测试用户的准确用户名

重现步骤

  1. 创建一个非管理员、非版主用户(<userid>),并将其设为组 <group_name>所有者
  2. 在 Web UI 中,以 <userid> 身份操作:邀请时不指定组 → 成功。邀请时指定 <group_name> → 成功。
  3. 通过 API,以 <userid> 身份操作(全局范围密钥,Api-Username=<userid>):
    • POST /invites.json,不包含 group_names → 成功。
    • POST /invites.json,包含 group_names=<group_name>(即该用户拥有的同一组) → 403 invalid_access
  4. <userid> 提升为版主(Moderator)。重复步骤 3 中的带组 API 调用 → 仍然返回 403 invalid_access
  5. <userid> 提升为管理员(Admin)。重复步骤 3 中的带组 API 调用,使用相同的密钥和相同的负载 → 成功。

预期行为

既然仅拥有组的所有者权限就足以让非工作人员用户通过 Web UI 在邀请时分配该组,那么通过 Api-Username 以同一用户身份认证的 API 也应该尊重相同的权限。至少,版主(属于工作人员)不应该在这里表现出与无权限用户完全相同的行为;版主与无角色用户一样失败,而只有管理员成功,这表明 API 代码路径特别检查了 user.admin?,而不是检查组的所有权,甚至不是一般的工作人员状态(user.staff?,这对版主来说为 true)。

实际行为

除非操作者是完整的管理员,否则 API 在任何带组的邀请中都会返回 403。组的所有权(已通过 Web UI 验证有效)和版主/工作人员状态(已通过直接提升测试验证)对于 API 路径来说都不够,尽管这两者之一通过 Web UI 或对于无组的 API 情况都是足够的。

给 Discourse 团队的问题

API 认证的“带组邀请”代码路径是否明确要求 user.admin?,而不是像 Web UI / 会话认证路径那样检查组的所有权或 user.staff??如果是这样,这是有意的(未记录的 API 特定限制),还是一个 bug,即 API 的守卫(guardian)检查与该操作的 Web 守卫检查存在分歧?

如有需要,我很乐意提供完整的请求/响应日志(密钥已脱敏)。