mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	settings: Add two realm settings to restrict direct messages.
Fixes #24467.
This commit is contained in:
		@@ -22,7 +22,7 @@ import orjson
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django.db import IntegrityError, transaction
 | 
			
		||||
from django.db.models import F
 | 
			
		||||
from django.db.models import F, Q
 | 
			
		||||
from django.utils.html import escape
 | 
			
		||||
from django.utils.timezone import now as timezone_now
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
@@ -39,6 +39,8 @@ from zerver.lib.alert_words import get_alert_word_automaton
 | 
			
		||||
from zerver.lib.cache import cache_with_key, user_profile_delivery_email_cache_key
 | 
			
		||||
from zerver.lib.create_user import create_user
 | 
			
		||||
from zerver.lib.exceptions import (
 | 
			
		||||
    DirectMessageInitiationError,
 | 
			
		||||
    DirectMessagePermissionError,
 | 
			
		||||
    JsonableError,
 | 
			
		||||
    MarkdownRenderingError,
 | 
			
		||||
    StreamDoesNotExistError,
 | 
			
		||||
@@ -80,6 +82,7 @@ from zerver.lib.string_validation import check_stream_name
 | 
			
		||||
from zerver.lib.timestamp import timestamp_to_datetime
 | 
			
		||||
from zerver.lib.topic import participants_for_topic
 | 
			
		||||
from zerver.lib.url_preview.types import UrlEmbedData
 | 
			
		||||
from zerver.lib.user_groups import is_any_user_in_group, is_user_in_group
 | 
			
		||||
from zerver.lib.user_message import UserMessageLite, bulk_insert_ums
 | 
			
		||||
from zerver.lib.users import (
 | 
			
		||||
    check_can_access_user,
 | 
			
		||||
@@ -104,7 +107,6 @@ from zerver.models import (
 | 
			
		||||
)
 | 
			
		||||
from zerver.models.clients import get_client
 | 
			
		||||
from zerver.models.groups import SystemGroups
 | 
			
		||||
from zerver.models.realms import PrivateMessagePolicyEnum
 | 
			
		||||
from zerver.models.recipients import get_direct_message_group_user_ids
 | 
			
		||||
from zerver.models.scheduled_jobs import NotificationTriggers
 | 
			
		||||
from zerver.models.streams import get_stream, get_stream_by_id_in_realm
 | 
			
		||||
@@ -1525,19 +1527,61 @@ def validate_stream_id_with_pm_notification(
 | 
			
		||||
    return stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_private_message_policy(
 | 
			
		||||
    realm: Realm, sender: UserProfile, user_profiles: Sequence[UserProfile]
 | 
			
		||||
def check_can_send_direct_message(
 | 
			
		||||
    realm: Realm, sender: UserProfile, recipient_users: Sequence[UserProfile], recipient: Recipient
 | 
			
		||||
) -> None:
 | 
			
		||||
    if realm.private_message_policy == PrivateMessagePolicyEnum.DISABLED:
 | 
			
		||||
        if sender.is_bot or (
 | 
			
		||||
            len(user_profiles) == 1 and (user_profiles[0].is_bot or user_profiles[0] == sender)
 | 
			
		||||
        ):
 | 
			
		||||
            # We allow direct messages only between users and bots or to oneself,
 | 
			
		||||
            # to avoid breaking the tutorial as well as automated
 | 
			
		||||
            # notifications from system bots to users.
 | 
			
		||||
    if sender.is_bot:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if all(user_profile.is_bot or user_profile.id == sender.id for user_profile in recipient_users):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    direct_message_permission_group = realm.direct_message_permission_group
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
        not hasattr(direct_message_permission_group, "named_user_group")
 | 
			
		||||
        or direct_message_permission_group.named_user_group.name != SystemGroups.EVERYONE
 | 
			
		||||
    ):
 | 
			
		||||
        user_ids = [recipient_user.id for recipient_user in recipient_users] + [sender.id]
 | 
			
		||||
        if not is_any_user_in_group(direct_message_permission_group, user_ids):
 | 
			
		||||
            is_nobody_group = (
 | 
			
		||||
                direct_message_permission_group.named_user_group.name == SystemGroups.NOBODY
 | 
			
		||||
            )
 | 
			
		||||
            raise DirectMessagePermissionError(is_nobody_group)
 | 
			
		||||
 | 
			
		||||
    direct_message_initiator_group = realm.direct_message_initiator_group
 | 
			
		||||
    if (
 | 
			
		||||
        not hasattr(direct_message_initiator_group, "named_user_group")
 | 
			
		||||
        or direct_message_initiator_group.named_user_group.name != SystemGroups.EVERYONE
 | 
			
		||||
    ):
 | 
			
		||||
        if is_user_in_group(direct_message_initiator_group, sender):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        raise JsonableError(_("Direct messages are disabled in this organization."))
 | 
			
		||||
        # TODO: This check is inefficient; we should in the future be able to cache
 | 
			
		||||
        # on the Huddle object whether the conversation already exists, likely in the
 | 
			
		||||
        # form of a `first_message_id` field, and be able to save doing this check in the
 | 
			
		||||
        # common case that this is not the first message in a conversation.
 | 
			
		||||
        if recipient.type == Recipient.PERSONAL:
 | 
			
		||||
            recipient_user_profile = recipient_users[0]
 | 
			
		||||
            previous_messages_exist = (
 | 
			
		||||
                Message.objects.filter(
 | 
			
		||||
                    realm=realm,
 | 
			
		||||
                    recipient__type=Recipient.PERSONAL,
 | 
			
		||||
                )
 | 
			
		||||
                .filter(
 | 
			
		||||
                    Q(sender=sender, recipient=recipient)
 | 
			
		||||
                    | Q(sender=recipient_user_profile, recipient_id=sender.recipient_id)
 | 
			
		||||
                )
 | 
			
		||||
                .exists()
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            assert recipient.type == Recipient.DIRECT_MESSAGE_GROUP
 | 
			
		||||
            previous_messages_exist = Message.objects.filter(
 | 
			
		||||
                realm=realm,
 | 
			
		||||
                recipient=recipient,
 | 
			
		||||
            ).exists()
 | 
			
		||||
        if not previous_messages_exist:
 | 
			
		||||
            raise DirectMessageInitiationError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_sender_can_access_recipients(
 | 
			
		||||
@@ -1692,8 +1736,6 @@ def check_message(
 | 
			
		||||
 | 
			
		||||
        check_sender_can_access_recipients(realm, sender, user_profiles)
 | 
			
		||||
 | 
			
		||||
        check_private_message_policy(realm, sender, user_profiles)
 | 
			
		||||
 | 
			
		||||
        recipients_for_user_creation_events = get_recipients_for_user_creation_events(
 | 
			
		||||
            realm, sender, user_profiles
 | 
			
		||||
        )
 | 
			
		||||
@@ -1709,6 +1751,8 @@ def check_message(
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            assert isinstance(e.messages[0], str)
 | 
			
		||||
            raise JsonableError(e.messages[0])
 | 
			
		||||
 | 
			
		||||
        check_can_send_direct_message(realm, sender, user_profiles, recipient)
 | 
			
		||||
    else:
 | 
			
		||||
        # This is defensive code--Addressee already validates
 | 
			
		||||
        # the message type.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user