Descontinuando o uso de `user.groups` no modelo `User` em JavaScript

Isso está relacionado tanto a The road to stable, then permanent, for granular_anonymous_and_logged_in_groups_permissions quanto a Granular group-based permissions for anonymous and logged in users

No core, bem como em muitos temas e plugins, um padrão como este se tornou bastante comum:

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

No entanto, esta não é uma maneira eficaz de verificar as permissões do usuário. Um usuário pode ser membro de grupos que não são visíveis para ele, portanto, não são serializados para o cliente e, assim, não podem ser usados de forma consistente ou precisa para verificações de segurança.

Para tornar isso mais óbvio, estamos renomeando currentUser.groups/user.groups para currentUser.visibleGroups/user.visibleGroups no modelo User e descontinuando a propriedade antiga. O PR inicial para fazer isso é DEV: Deprecate calling user.groups on client directly - Pull Request #42711 - discourse/discourse - GitHub .

Há várias alternativas se você precisar verificar a permissão de um usuário com base em uma lista de IDs de grupos no cliente em JavaScript:

Para plugins

Estenda o serializador current_user com um novo atributo e verifique as permissões do usuário com scope.in_any_groups? no lado do servidor, o que também cobre grupos pseudos como logged_in_users e 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) }

Então, você pode fazer this.currentUser.has_some_permission no cliente.

Para temas e componentes

Para configurações de tema do tipo list com list_type: group, você pode usar resolve_group_membership: true:

copy_button_allowed_groups:
  default: "1|3"
  type: list
  list_type: group
  resolve_group_membership: true

Isso substituirá settings.copy_button_allowed_groups no cliente por settings.user_in_copy_button_allowed_groups (prefixando a configuração com user_in_), que é um booleano calculado no lado do servidor com base nas associações de grupos do usuário.

Isso também funciona para configurações de objeto com type: groups. Adicione resolve_group_membership: true à propriedade 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

Em seguida, o acesso fica assim:

for (const section of settings.menu_sections) {
  if (section.user_in_groups) {
    // O usuário está em pelo menos um grupo selecionado para esta seção.
  }
}