diff --git a/plugins/chat/config/locales/server.en.yml b/plugins/chat/config/locales/server.en.yml index ea2349f2b7fd..ce78276f8656 100644 --- a/plugins/chat/config/locales/server.en.yml +++ b/plugins/chat/config/locales/server.en.yml @@ -17,6 +17,7 @@ en: default_emoji_reactions: "Default emoji reactions for chat messages. Add up to 5 emojis for quick reaction." direct_message_enabled_groups: "Allow users within these groups to create user-to-user Personal Chats. Note: staff can always create Personal Chats, and users will be able to reply to Personal Chats initiated by users who have permission to create them." chat_message_flag_allowed_groups: "Users in these groups are allowed to flag chat messages." + chat_max_direct_message_users: "Users cannot add more than this number of other users when creating a new direct message. Set to 0 to only allow messages to oneself. Staff are exempt from this setting." errors: chat_default_channel: "The default chat channel must be a public channel." direct_message_enabled_groups_invalid: "You must specify at least one group for this setting. If you do not want anyone except staff to send direct messages, choose the staff group." @@ -56,6 +57,9 @@ en: actor_disallowed_dms: "You have chosen to prevent users from sending you private and direct messages, so you cannot create new direct messages." actor_preventing_target_user_from_dm: "You have chosen to prevent %{username} from sending you private and direct messages, so you cannot create new direct messages to them." user_cannot_send_direct_messages: "Sorry, you cannot send direct messages." + over_chat_max_direct_message_users: + one: "You can only create a direct message with yourself." + other: "You can't create a direct message with more than %{count} other users." reviewables: message_already_handled: "Thanks, but we've already reviewed this message and determined it does not need to be flagged again." actions: diff --git a/plugins/chat/config/settings.yml b/plugins/chat/config/settings.yml index 0ded290e0e38..feeb0f34421a 100644 --- a/plugins/chat/config/settings.yml +++ b/plugins/chat/config/settings.yml @@ -20,6 +20,11 @@ chat: client: true max: 3652 # 10 years min: 0 + chat_max_direct_message_users: + default: 20 + min: 0 + max: 100 + client: true chat_dm_retention_days: default: 0 client: true diff --git a/plugins/chat/lib/direct_message_channel_creator.rb b/plugins/chat/lib/direct_message_channel_creator.rb index 06315b8a476d..63342e3cfb70 100644 --- a/plugins/chat/lib/direct_message_channel_creator.rb +++ b/plugins/chat/lib/direct_message_channel_creator.rb @@ -11,6 +11,7 @@ def self.create!(acting_user:, target_users:) if direct_message chat_channel = ChatChannel.find_by!(chatable: direct_message) else + enforce_max_direct_message_users!(acting_user, target_users) ensure_actor_can_communicate!(acting_user, target_users) direct_message = DirectMessage.create!(user_ids: target_users.map(&:id)) chat_channel = direct_message.create_chat_channel! @@ -24,6 +25,20 @@ def self.create!(acting_user:, target_users:) private + def self.enforce_max_direct_message_users!(acting_user, target_users) + # We never want to prevent the actor from communicating with themself. + target_users = target_users.reject { |user| user.id == acting_user.id } + + if !acting_user.staff? && target_users.size > SiteSetting.chat_max_direct_message_users + raise NotAllowed.new( + I18n.t( + "chat.errors.over_chat_max_direct_message_users", + count: SiteSetting.chat_max_direct_message_users + 1, # +1 for the acting_user + ), + ) + end + end + def self.update_memberships(acting_user, target_users, chat_channel_id) sql_params = { acting_user_id: acting_user.id, diff --git a/plugins/chat/spec/lib/direct_message_channel_creator_spec.rb b/plugins/chat/spec/lib/direct_message_channel_creator_spec.rb index efdb0310a63d..1b88cce79838 100644 --- a/plugins/chat/spec/lib/direct_message_channel_creator_spec.rb +++ b/plugins/chat/spec/lib/direct_message_channel_creator_spec.rb @@ -178,6 +178,62 @@ }.by(1).and change { UserChatChannelMembership.count }.by(1) end + context "when number of users is over the limit" do + before { SiteSetting.chat_max_direct_message_users = 1 } + + it "raises an error" do + expect { + subject.create!(acting_user: user_1, target_users: [user_1, user_2, user_3]) + }.to raise_error( + Chat::DirectMessageChannelCreator::NotAllowed, + I18n.t("chat.errors.over_chat_max_direct_message_users", count: 2), + ) + end + + context "when acting user is staff" do + fab!(:admin) { Fabricate(:admin) } + + it "creates a new chat channel" do + expect { + subject.create!(acting_user: admin, target_users: [admin, user_1, user_2]) + }.to change { ChatChannel.count }.by(1) + end + end + + context "when limit is zero" do + before { SiteSetting.chat_max_direct_message_users = 0 } + + it "raises an error" do + expect { + subject.create!(acting_user: user_1, target_users: [user_1, user_2]) + }.to raise_error( + Chat::DirectMessageChannelCreator::NotAllowed, + I18n.t("chat.errors.over_chat_max_direct_message_users", count: 1), + ) + end + end + end + + context "when number of users is at the limit" do + before { SiteSetting.chat_max_direct_message_users = 0 } + + it "creates a new chat channel" do + expect { subject.create!(acting_user: user_1, target_users: [user_1]) }.to change { + ChatChannel.count + }.by(1) + end + end + + context "when number of users is under the limit" do + before { SiteSetting.chat_max_direct_message_users = 1 } + + it "creates a new chat channel" do + expect { subject.create!(acting_user: user_1, target_users: [user_1]) }.to change { + ChatChannel.count + }.by(1) + end + end + context "when the user is not a member of direct_message_enabled_groups" do before { SiteSetting.direct_message_enabled_groups = Group::AUTO_GROUPS[:trust_level_4] }