discourse/plugins/chat/app/serializers/chat_channel_serializer.rb
Martin Brennan 520d4f504b
FEATURE: Auto-remove users without permission from channel (#20344)
There are many situations that may cause users to lose permission to
send messages in a chat channel. Until now we have relied on security
checks in `Chat::ChatChannelFetcher` to remove channels which the
user may have a `UserChatChannelMembership` record for but which
they do not have access to.

This commit takes a more proactive approach. Now any of these following
`DiscourseEvent` triggers may cause `UserChatChannelMembership`
records to be deleted:

* `category_updated` - Permissions of the category changed
   (i.e. CategoryGroup records changed)
* `user_removed_from_group` - Means the user may not be able to access the
   channel based on `GroupUser` or also `chat_allowed_groups`
* `site_setting_changed` - The `chat_allowed_groups` was updated, some
   users may no longer be in groups that can access chat.
* `group_destroyed` - Means the user may not be able to access the
   channel based on `GroupUser` or also `chat_allowed_groups`

All of these are handled in a distinct service run in a background
job. Users removed are logged via `StaffActionLog` and then we
publish messages on a per-channel basis to users who had their
memberships deleted.

When the user has a channel they are kicked from open, we show
a dialog saying "You no longer have access to this channel".

When they click OK we redirect them either:

* To their first other public channel, if they have any followed
* The chat browse page if they don't

This is to save on tons of requests from kicked out users getting messages
from other channels.

When the user does not have the kicked channel open, we can just
silently yoink it out of their sidebar and turn off subscriptions.
2023-03-22 10:19:59 +10:00

144 lines
3.3 KiB
Ruby

# frozen_string_literal: true
class ChatChannelSerializer < ApplicationSerializer
attributes :id,
:auto_join_users,
:allow_channel_wide_mentions,
:chatable,
:chatable_id,
:chatable_type,
:chatable_url,
:description,
:title,
:slug,
:last_message_sent_at,
:status,
:archive_failed,
:archive_completed,
:archived_messages,
:total_messages,
:archive_topic_id,
:memberships_count,
:current_user_membership,
:meta,
:threading_enabled
def threading_enabled
SiteSetting.enable_experimental_chat_threaded_discussions && object.threading_enabled
end
def initialize(object, opts)
super(object, opts)
@opts = opts
@current_user_membership = opts[:membership]
end
def include_description?
object.description.present?
end
def memberships_count
object.user_count
end
def chatable_url
object.chatable_url
end
def title
object.name || object.title(scope.user)
end
def chatable
case object.chatable_type
when "Category"
BasicCategorySerializer.new(object.chatable, root: false).as_json
when "DirectMessage"
DirectMessageSerializer.new(object.chatable, scope: scope, root: false).as_json
when "Site"
nil
end
end
def archive
object.chat_channel_archive
end
def include_archive_status?
!object.direct_message_channel? && scope.is_staff? && archive.present?
end
def archive_completed
archive.complete?
end
def archive_failed
archive.failed?
end
def archived_messages
archive.archived_messages
end
def total_messages
archive.total_messages
end
def archive_topic_id
archive.destination_topic_id
end
def include_auto_join_users?
scope.can_edit_chat_channel?
end
def include_current_user_membership?
@current_user_membership.present?
end
def current_user_membership
@current_user_membership.chat_channel = object
BaseChatChannelMembershipSerializer.new(
@current_user_membership,
scope: scope,
root: false,
).as_json
end
def meta
{
message_bus_last_ids: {
new_messages: new_messages_message_bus_id,
new_mentions: new_mentions_message_bus_id,
kick: kick_message_bus_id,
channel_message_bus_last_id: MessageBus.last_id("/chat/#{object.id}"),
},
}
end
alias_method :include_archive_topic_id?, :include_archive_status?
alias_method :include_total_messages?, :include_archive_status?
alias_method :include_archived_messages?, :include_archive_status?
alias_method :include_archive_failed?, :include_archive_status?
alias_method :include_archive_completed?, :include_archive_status?
private
def new_messages_message_bus_id
@opts[:new_messages_message_bus_last_id] ||
MessageBus.last_id(Chat::Publisher.new_messages_message_bus_channel(object.id))
end
def new_mentions_message_bus_id
@opts[:new_mentions_message_bus_last_id] ||
MessageBus.last_id(Chat::Publisher.new_mentions_message_bus_channel(object.id))
end
def kick_message_bus_id
@opts[:kick_message_bus_last_id] ||
MessageBus.last_id(Chat::Publisher.kick_users_message_bus_channel(object.id))
end
end