带有 group_names 参数的 API 邀请请求,非管理员的群组所有者会收到 403 错误

摘要

非管理员用户如果是 Discourse 组的所有者 (owner),可以通过 Web UI 的邀请对话框成功邀请新成员并将该组分配给他们。然而,通过 REST API(/invites.json,全局范围 API 密钥,正确的 Api-Username)执行完全相同的操作时,只有当请求负载中包含 group_names/group_ids 时才会返回 403 invalid_access。无论 group_names 是作为纯字符串(例如 "group_names": "my-group")还是作为数组(例如 "group_names": ["my-group"])传递,这两种格式都会产生相同的 403 错误。

关键的是,授予用户版主 (Moderator) 权限并不能修复 API 调用——只有授予完全的管理员 (Admin) 权限才可以,尽管版主权限对于其他所有测试场景(Web UI、非分组 API 邀请)都是足够的。

环境

  • Discourse 版本:[请填写 — Admin → Upgrade 页面]
  • API 密钥类型:全局范围,Api-Username = 测试用户的准确用户名

复现步骤

  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
    • POST /invites.json,包含 group_names=["<group_name>"](数组格式)→ 403 invalid_access(失败相同,两种格式均已测试)。
  4. <userid> 提升为版主。重复步骤 3 中的分组 API 调用(两种格式)→ 仍然是 403 invalid_access
  5. <userid> 提升为管理员。重复步骤 3 中的分组 API 调用,使用相同的密钥和相同的负载 → 成功。

预期行为

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

实际行为

除非操作者是完整的管理员,否则 API 会对任何分组邀请(无论是字符串还是数组参数格式)返回 403。组所有权(已通过 Web UI 验证有效)和版主/工作人员状态(已通过直接提升测试验证)对于 API 路径来说都不够,尽管其中任何一个通过 Web UI 或对于未分组的 API 情况都是足够的。

给 Discourse 团队的问题

API 身份验证的带组邀请代码路径是否明确要求 user.admin?,而不是像 Web UI / 会话身份验证路径那样检查组所有权或 user.staff??如果是这样,这是有意的(未记录的 API 特定限制),还是 API 守护进程检查 (guardian check) 在此操作上与 Web 守护进程检查不一致的 bug?

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