JavaScriptの`User`モデルで`user.groups`の使用を非推奨にする

これは 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.groupscurrentUser.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_usersanonymous_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_groupssettings.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) {
    // ユーザーはこのセクションで選択されたグループの少なくとも1つに属しています。
  }
}

Martinさん、

これらの変更は、PRがマージされた時点で反映されますか?それとも、PRのマージ後の次のアップデートで反映されますか?

なぜこれが重要なのか?これは何も壊しません。単にブラウザのコンソールに警告が表示され、開発者にコードの調整を促すだけです。

この機能に依存しているテーマコンポーネントがあることを把握しており、確認したかったのです。もし単なる警告で、しばらくの間は動作するのだとしたら、それほど気にしません。

通常、非推奨とされたコードは、次の拡張サポートリリースまでサポートされ続けます。そうでなければ、そのコードを使用しているフォーラムは、問題が発生する前に警告を確認する機会を失うことになりかねません。

PRはマージされたので、非推奨になりました:

ただ、Moinが言っている通り、現時点ではブラウザのコンソールに非推奨の警告が表示されるだけです :slight_smile: 非推奨を導入した直後は、コアおよび公式プラグインやテーマ内のすべての該当箇所を修正する必要があります。その後しばらく非推奨の状態を維持し、サードパーティ製テーマ(顧客のテーマを含む)の修正を段階的に行ったり、他の人に修正を委ねたりできるようにします。

user.groupsの恒久的な削除には、すべての呼び出し箇所を把握できたと確信できるまで、まだ相当な時間がかかります。また、その場合でも、旧サイトを呼び出しているコードが残っているサイトには、まず管理者向けの警告を表示することになります。