إلغاء استخدام `user.groups` في نموذج `User` بلغة JavaScript

هذا مرتبط بكل من The road to stable, then permanent, for granular_anonymous_and_logged_in_groups_permissions و Granular group-based permissions for anonymous and logged in users

في النواة (Core) وكذلك في العديد من القوالب (Themes) والإضافات (Plugins)، أصبح نمطًا شائعًا جدًا استخدام ما يلي:

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;
}

ومع ذلك، فإن هذه ليست طريقة فعالة للتحقق من صلاحيات المستخدم. يمكن أن يكون المستخدم عضوًا في مجموعات غير مرئية له، وبالتالي لا يتم تسلسلها (serialized) إلى العميل (client)، مما يجعلها غير قابلة للاستخدام بشكل متسق أو دقيق للتحقق الأمني.

لجعل هذا أوضح، سنقوم بإعادة تسمية currentUser.groups/user.groups إلى currentUser.visibleGroups/user.visibleGroups في نموذج User وإلغاء استخدام الخاصية القديمة. أول طلب دمج (PR) للقيام بذلك هو DEV: Deprecate calling user.groups on client directly - Pull Request #42711 - discourse/discourse - GitHub .

توجد بدائل متعددة إذا كنت بحاجة إلى التحقق من صلاحية المستخدم بناءً على قائمة معرّفات المجموعات (Group IDs) على العميل في جافاسكربت:

للإضافات (Plugins)

قم بتمديد مُسلسل (serializer) current_user بخاصية جديدة، وتحقق من صلاحيات المستخدم باستخدام scope.in_any_groups? على جانب الخادم، والذي يغطي أيضًا المجموعات الوهمية (pseudogroups) مثل 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 على العميل.

للقوالب (Themes) والمكونات (Components)

لإعدادات القالب من النوع list مع list_type: group، يمكنك استخدام 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_ للإعداد)، وهو قيمة منطقية (boolean) تُحسب على جانب الخادم بناءً على عضويات المستخدم في المجموعات.

يعمل هذا أيضًا مع إعدادات الكائنات (object settings) التي تحتوي على type: groups. أضف resolve_group_membership: true إلى الخاصية groups:

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) {
    // المستخدم عضو في مجموعة واحدة على الأقل من المجموعات المحددة لهذا القسم.
  }
}