这与 The road to stable, then permanent, for granular_anonymous_and_logged_in_groups_permissions 和 Granular group-based permissions for anonymous and logged in users 这两个主题都有关。
在核心代码以及许多主题和插件中,以下这种模式已经变得相当常见:
const groupIds = this.currentUser.groups.map((g) => g.id);
const allowedGroupIds = this.siteSettings.some_group_setting.split("|").map((groupId) => parseInt(groupId, 10));
const hasPermission = allowedGroups.some((groupId) =>
userGroupIds.includes(groupId)
);
if (!hasPermission) {
return;
}
然而,这并不是检查用户权限的有效方法。用户可以属于对他们不可见的组,因此这些组不会被序列化到客户端,也就无法在安全检查中一致或准确地使用。
为了使这一点更加明确,我们将在 User 模型中将 currentUser.groups/user.groups 重命名为 currentUser.visibleGroups/user.visibleGroups,并弃用旧属性。实现此变更的初始 PR 是 DEV: Deprecate calling user.groups on client directly - Pull Request #42711 - discourse/discourse - GitHub 。
如果你需要在客户端的 JavaScript 中基于组 ID 列表来检查用户权限,有几种替代方案:
针对插件
扩展 current_user 序列化器,添加一个新属性,并在服务器端使用 scope.in_any_groups? 来检查用户权限,该方法还涵盖了 logged_in_users 和 anonymous_users 等伪组:
add_to_serializer(
:current_user,
:has_some_permission,
include_condition: -> do
SiteSetting.plugin_enabled
end,
) { scope.in_any_groups?(SiteSetting.group_list_setting_map) }
然后你可以在客户端使用 this.currentUser.has_some_permission。
针对主题和组件
对于具有 list_type: group 的 list 类型主题设置,你可以使用 resolve_group_membership: true:
copy_button_allowed_groups:
default: "1|3"
type: list
list_type: group
resolve_group_membership: true
这将在客户端将 settings.copy_button_allowed_groups 替换为 settings.user_in_copy_button_allowed_groups(在设置前添加 user_in_ 前缀),这是一个基于用户组成员身份在服务器端计算的布尔值。
对于具有 type: groups 的对象设置,这也适用。在 groups 属性中添加 resolve_group_membership: true:
menu_sections:
type: objects
default:
- name: section 1
groups:
- 1
- 3
schema:
name: menu section
properties:
name:
type: string
groups:
type: groups
resolve_group_membership: true
然后访问方式如下:
for (const section of settings.menu_sections) {
if (section.user_in_groups) {
// 用户属于该部分选定的至少一个组。
}
}