mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 03:53:50 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			921 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			921 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from datetime import timedelta
 | |
| from typing import TYPE_CHECKING, Any
 | |
| from unittest import mock
 | |
| 
 | |
| import orjson
 | |
| from django.db import IntegrityError
 | |
| from django.utils.timezone import now as timezone_now
 | |
| 
 | |
| from zerver.actions.message_delete import do_delete_messages
 | |
| from zerver.actions.realm_settings import do_change_realm_permission_group_setting
 | |
| from zerver.actions.streams import do_change_stream_group_based_setting, do_deactivate_stream
 | |
| from zerver.actions.user_groups import check_add_user_group
 | |
| from zerver.lib.test_classes import ZulipTestCase
 | |
| from zerver.models import Message, NamedUserGroup, UserProfile
 | |
| from zerver.models.groups import SystemGroups
 | |
| from zerver.models.realms import get_realm
 | |
| from zerver.models.streams import get_stream
 | |
| 
 | |
| if TYPE_CHECKING:
 | |
|     from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
 | |
| 
 | |
| 
 | |
| class DeleteMessageTest(ZulipTestCase):
 | |
|     def test_do_delete_messages_with_empty_list(self) -> None:
 | |
|         realm = get_realm("zulip")
 | |
|         inital_count = Message.objects.count()
 | |
|         do_delete_messages(realm, [], acting_user=None)
 | |
|         final_count = Message.objects.count()
 | |
|         self.assertEqual(inital_count, final_count)
 | |
| 
 | |
|     def test_do_delete_private_messages_with_acting_user(self) -> None:
 | |
|         realm = get_realm("zulip")
 | |
|         cordelia = self.example_user("cordelia")
 | |
|         hamlet = self.example_user("hamlet")
 | |
| 
 | |
|         acting_user = self.example_user("iago")
 | |
|         msg_id = self.send_personal_message(cordelia, hamlet, "Hello!")
 | |
|         message = Message.objects.get(id=msg_id)
 | |
| 
 | |
|         with self.capture_send_event_calls(expected_num_events=1) as events:
 | |
|             do_delete_messages(realm, [message], acting_user=acting_user)
 | |
| 
 | |
|         self.assert_length(events, 1)
 | |
|         event = events[0]["event"]
 | |
|         self.assertIn("type", event)
 | |
|         self.assertEqual(event["type"], "delete_message")
 | |
|         self.assertIn(msg_id, event["message_ids"])
 | |
|         self.assertIn(acting_user.id, events[0]["users"])
 | |
| 
 | |
|     def test_do_delete_stream_messages_without_acting_user(self) -> None:
 | |
|         realm = get_realm("zulip")
 | |
|         cordelia = self.example_user("cordelia")
 | |
| 
 | |
|         stream_name = "Denmark-Test"
 | |
|         self.make_stream(stream_name)
 | |
|         self.subscribe(cordelia, stream_name)
 | |
| 
 | |
|         msg_id = self.send_stream_message(cordelia, stream_name, "Hello, Denmark!")
 | |
|         message = Message.objects.get(id=msg_id)
 | |
| 
 | |
|         with self.capture_send_event_calls(expected_num_events=2) as events:
 | |
|             do_delete_messages(realm, [message], acting_user=None)
 | |
| 
 | |
|         self.assert_length(events, 2)
 | |
|         self.assertIn("type", events[0]["event"])
 | |
|         self.assertEqual(events[1]["event"]["type"], "delete_message")
 | |
|         self.assertIn(msg_id, events[1]["event"]["message_ids"])
 | |
| 
 | |
|     def test_do_delete_messages_grouping_logic(self) -> None:
 | |
|         realm = get_realm("zulip")
 | |
|         cordelia = self.example_user("cordelia")
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         iago = self.example_user("iago")
 | |
| 
 | |
|         self.make_stream("stream1")
 | |
|         self.make_stream("stream2")
 | |
|         self.subscribe(cordelia, "stream1")
 | |
|         self.subscribe(hamlet, "stream1")
 | |
|         self.subscribe(cordelia, "stream2")
 | |
|         self.subscribe(hamlet, "stream2")
 | |
| 
 | |
|         msg_id_1 = self.send_stream_message(
 | |
|             cordelia, "stream1", topic_name="topic1", content="test1"
 | |
|         )
 | |
|         msg_id_2 = self.send_stream_message(hamlet, "stream1", topic_name="topic1", content="test2")
 | |
|         msg_id_3 = self.send_stream_message(
 | |
|             cordelia, "stream1", topic_name="topic2", content="test3"
 | |
|         )
 | |
|         msg_id_4 = self.send_stream_message(hamlet, "stream2", topic_name="topic3", content="test4")
 | |
|         dm_id_1 = self.send_personal_message(cordelia, hamlet, "test5")
 | |
|         dm_id_2 = self.send_personal_message(hamlet, iago, "test6")
 | |
| 
 | |
|         messages = Message.objects.filter(
 | |
|             id__in=[msg_id_1, msg_id_2, msg_id_3, msg_id_4, dm_id_1, dm_id_2]
 | |
|         )
 | |
| 
 | |
|         stream1 = get_stream("stream1", realm)
 | |
|         stream2 = get_stream("stream2", realm)
 | |
| 
 | |
|         with self.capture_send_event_calls(expected_num_events=8) as events:
 | |
|             do_delete_messages(realm, messages, acting_user=None)
 | |
| 
 | |
|         expected_events: list[dict[str, Any]] = [
 | |
|             {
 | |
|                 "type": "delete_message",
 | |
|                 "message_ids": [dm_id_1],
 | |
|                 "message_type": "private",
 | |
|             },
 | |
|             {
 | |
|                 "type": "delete_message",
 | |
|                 "message_ids": [dm_id_2],
 | |
|                 "message_type": "private",
 | |
|             },
 | |
|             {
 | |
|                 "type": "stream",
 | |
|                 "op": "update",
 | |
|                 "property": "first_message_id",
 | |
|                 "value": msg_id_3,
 | |
|                 "stream_id": stream1.id,
 | |
|                 "name": "stream1",
 | |
|             },
 | |
|             {
 | |
|                 "type": "delete_message",
 | |
|                 "message_ids": [msg_id_1, msg_id_2],
 | |
|                 "stream_id": stream1.id,
 | |
|                 "topic": "topic1",
 | |
|                 "message_type": "stream",
 | |
|             },
 | |
|             {
 | |
|                 "type": "stream",
 | |
|                 "op": "update",
 | |
|                 "property": "first_message_id",
 | |
|                 "value": None,
 | |
|                 "stream_id": stream1.id,
 | |
|                 "name": "stream1",
 | |
|             },
 | |
|             {
 | |
|                 "type": "delete_message",
 | |
|                 "message_ids": [msg_id_3],
 | |
|                 "stream_id": stream1.id,
 | |
|                 "topic": "topic2",
 | |
|                 "message_type": "stream",
 | |
|             },
 | |
|             {
 | |
|                 "type": "stream",
 | |
|                 "op": "update",
 | |
|                 "property": "first_message_id",
 | |
|                 "value": None,
 | |
|                 "stream_id": stream2.id,
 | |
|                 "name": "stream2",
 | |
|             },
 | |
|             {
 | |
|                 "type": "delete_message",
 | |
|                 "message_ids": [msg_id_4],
 | |
|                 "stream_id": stream2.id,
 | |
|                 "topic": "topic3",
 | |
|                 "message_type": "stream",
 | |
|             },
 | |
|         ]
 | |
| 
 | |
|         actual_events = [event["event"] for event in events]
 | |
| 
 | |
|         self.assert_length(actual_events, len(expected_events))
 | |
| 
 | |
|         for actual, expected in zip(actual_events, expected_events, strict=True):
 | |
|             for key, value in expected.items():
 | |
|                 if key == "message_ids":
 | |
|                     self.assertEqual(set(actual[key]), set(value))
 | |
|                 else:
 | |
|                     self.assertEqual(actual[key], value)
 | |
| 
 | |
|     def test_delete_message_invalid_request_format(self) -> None:
 | |
|         self.login("iago")
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         msg_id = self.send_stream_message(hamlet, "Denmark")
 | |
|         result = self.client_delete(f"/json/messages/{msg_id + 1}", {"message_id": msg_id})
 | |
|         self.assert_json_error(result, "Invalid message(s)")
 | |
|         result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|     def test_delete_message_by_user(self) -> None:
 | |
|         def set_message_deleting_params(
 | |
|             can_delete_any_message_group: NamedUserGroup,
 | |
|             can_delete_own_message_group: NamedUserGroup,
 | |
|             message_content_delete_limit_seconds: int | str,
 | |
|         ) -> None:
 | |
|             self.login("iago")
 | |
|             result = self.client_patch(
 | |
|                 "/json/realm",
 | |
|                 {
 | |
|                     "can_delete_any_message_group": orjson.dumps(
 | |
|                         {"new": can_delete_any_message_group.id}
 | |
|                     ).decode(),
 | |
|                     "can_delete_own_message_group": orjson.dumps(
 | |
|                         {"new": can_delete_own_message_group.id}
 | |
|                     ).decode(),
 | |
|                     "message_content_delete_limit_seconds": orjson.dumps(
 | |
|                         message_content_delete_limit_seconds
 | |
|                     ).decode(),
 | |
|                 },
 | |
|             )
 | |
|             self.assert_json_success(result)
 | |
| 
 | |
|         def test_delete_message_by_admin(msg_id: int) -> "TestHttpResponse":
 | |
|             self.login("iago")
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_moderator(msg_id: int) -> "TestHttpResponse":
 | |
|             self.login("shiva")
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_sender(msg_id: int) -> "TestHttpResponse":
 | |
|             self.login("hamlet")
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_other_user(msg_id: int) -> "TestHttpResponse":
 | |
|             self.login("cordelia")
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         realm = get_realm("zulip")
 | |
| 
 | |
|         administrators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         members_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MEMBERS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         moderators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         everyone_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.EVERYONE, realm=realm, is_system_group=True
 | |
|         )
 | |
| 
 | |
|         # Test if message deleting is not allowed(default).
 | |
|         set_message_deleting_params(
 | |
|             administrators_system_group, administrators_system_group, "unlimited"
 | |
|         )
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         self.login_user(hamlet)
 | |
|         msg_id = self.send_stream_message(hamlet, "Denmark")
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id=msg_id)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_admin(msg_id=msg_id)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Test if message deleting is allowed.
 | |
|         # Test if time limit is None(no limit).
 | |
|         set_message_deleting_params(administrators_system_group, everyone_system_group, "unlimited")
 | |
|         msg_id = self.send_stream_message(hamlet, "Denmark")
 | |
|         message = Message.objects.get(id=msg_id)
 | |
|         message.date_sent -= timedelta(seconds=600)
 | |
|         message.save()
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id=msg_id)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Test if time limit is non-zero.
 | |
|         set_message_deleting_params(administrators_system_group, everyone_system_group, 240)
 | |
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark")
 | |
|         message = Message.objects.get(id=msg_id_1)
 | |
|         message.date_sent -= timedelta(seconds=120)
 | |
|         message.save()
 | |
| 
 | |
|         msg_id_2 = self.send_stream_message(hamlet, "Denmark")
 | |
|         message = Message.objects.get(id=msg_id_2)
 | |
|         message.date_sent -= timedelta(seconds=360)
 | |
|         message.save()
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id=msg_id_1)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id_1)
 | |
|         self.assert_json_success(result)
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id_2)
 | |
|         self.assert_json_error(result, "The time limit for deleting this message has passed")
 | |
| 
 | |
|         # No limit for admin.
 | |
|         result = test_delete_message_by_admin(msg_id=msg_id_2)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Test multiple delete requests with no latency issues
 | |
|         msg_id = self.send_stream_message(hamlet, "Denmark")
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|         self.assert_json_success(result)
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|         self.assert_json_error(result, "Invalid message(s)")
 | |
| 
 | |
|         # Test if message deletion is allowed when every member can delete any message.
 | |
|         set_message_deleting_params(members_system_group, administrators_system_group, "unlimited")
 | |
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark")
 | |
|         msg_id_2 = self.send_stream_message(hamlet, "Denmark")
 | |
|         msg_id_3 = self.send_stream_message(hamlet, "Denmark")
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id=msg_id_1)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id_2)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         result = test_delete_message_by_admin(msg_id=msg_id_3)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Test if there is no time limit to delete messages for users who can delete
 | |
|         # any message.
 | |
|         set_message_deleting_params(moderators_system_group, everyone_system_group, 240)
 | |
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark")
 | |
|         message = Message.objects.get(id=msg_id_1)
 | |
|         message.date_sent -= timedelta(seconds=120)
 | |
|         message.save()
 | |
| 
 | |
|         msg_id_2 = self.send_stream_message(hamlet, "Denmark")
 | |
|         message = Message.objects.get(id=msg_id_2)
 | |
|         message.date_sent -= timedelta(seconds=360)
 | |
|         message.save()
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id=msg_id_1)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id_1)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         result = test_delete_message_by_sender(msg_id=msg_id_2)
 | |
|         self.assert_json_error(result, "The time limit for deleting this message has passed")
 | |
| 
 | |
|         result = test_delete_message_by_moderator(msg_id=msg_id_2)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Test handling of 500 error caused by multiple delete requests due to latency.
 | |
|         # see issue #11219.
 | |
|         with (
 | |
|             mock.patch("zerver.views.message_edit.do_delete_messages") as m,
 | |
|             mock.patch("zerver.views.message_edit.validate_can_delete_message", return_value=None),
 | |
|             mock.patch("zerver.views.message_edit.access_message", return_value=(None, None)),
 | |
|         ):
 | |
|             m.side_effect = IntegrityError()
 | |
|             result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|             self.assert_json_error(result, "Message already deleted")
 | |
|             m.side_effect = Message.DoesNotExist()
 | |
|             result = test_delete_message_by_sender(msg_id=msg_id)
 | |
|             self.assert_json_error(result, "Message already deleted")
 | |
| 
 | |
|     def test_delete_message_sent_by_bots(self) -> None:
 | |
|         iago = self.example_user("iago")
 | |
|         shiva = self.example_user("shiva")
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         cordelia = self.example_user("cordelia")
 | |
| 
 | |
|         def set_message_deleting_params(
 | |
|             can_delete_any_message_group: NamedUserGroup,
 | |
|             can_delete_own_message_group: NamedUserGroup,
 | |
|             message_content_delete_limit_seconds: int | str,
 | |
|         ) -> None:
 | |
|             result = self.api_patch(
 | |
|                 iago,
 | |
|                 "/api/v1/realm",
 | |
|                 {
 | |
|                     "can_delete_any_message_group": orjson.dumps(
 | |
|                         {"new": can_delete_any_message_group.id}
 | |
|                     ).decode(),
 | |
|                     "can_delete_own_message_group": orjson.dumps(
 | |
|                         {"new": can_delete_own_message_group.id}
 | |
|                     ).decode(),
 | |
|                     "message_content_delete_limit_seconds": orjson.dumps(
 | |
|                         message_content_delete_limit_seconds
 | |
|                     ).decode(),
 | |
|                 },
 | |
|             )
 | |
|             self.assert_json_success(result)
 | |
| 
 | |
|         def test_delete_message_by_admin(msg_id: int) -> "TestHttpResponse":
 | |
|             result = self.api_delete(iago, f"/api/v1/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_moderator(msg_id: int) -> "TestHttpResponse":
 | |
|             result = self.api_delete(shiva, f"/api/v1/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_bot_owner(msg_id: int) -> "TestHttpResponse":
 | |
|             result = self.api_delete(hamlet, f"/api/v1/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         def test_delete_message_by_other_user(msg_id: int) -> "TestHttpResponse":
 | |
|             result = self.api_delete(cordelia, f"/api/v1/messages/{msg_id}")
 | |
|             return result
 | |
| 
 | |
|         realm = get_realm("zulip")
 | |
| 
 | |
|         administrators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         moderators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         everyone_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.EVERYONE, realm=realm, is_system_group=True
 | |
|         )
 | |
| 
 | |
|         set_message_deleting_params(
 | |
|             moderators_system_group, administrators_system_group, "unlimited"
 | |
|         )
 | |
| 
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         test_bot = self.create_test_bot("test-bot", hamlet)
 | |
|         msg_id_1 = self.send_stream_message(test_bot, "Denmark")
 | |
|         msg_id_2 = self.send_stream_message(test_bot, "Denmark")
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id_1)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_bot_owner(msg_id_1)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         # Admins and moderators can delete any message.
 | |
|         result = test_delete_message_by_moderator(msg_id_1)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         result = test_delete_message_by_admin(msg_id_2)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         msg_id = self.send_stream_message(test_bot, "Denmark")
 | |
|         set_message_deleting_params(administrators_system_group, everyone_system_group, "unlimited")
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_bot_owner(msg_id)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         msg_id = self.send_stream_message(test_bot, "Denmark")
 | |
|         set_message_deleting_params(administrators_system_group, everyone_system_group, 600)
 | |
| 
 | |
|         message = Message.objects.get(id=msg_id)
 | |
|         message.date_sent = timezone_now() - timedelta(seconds=700)
 | |
|         message.save()
 | |
| 
 | |
|         result = test_delete_message_by_other_user(msg_id)
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         result = test_delete_message_by_bot_owner(msg_id)
 | |
|         self.assert_json_error(result, "The time limit for deleting this message has passed")
 | |
| 
 | |
|         result = test_delete_message_by_admin(msg_id)
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|         # Check that the bot can also delete the messages sent by them
 | |
|         # depending on the realm permissions for message deletion.
 | |
|         set_message_deleting_params(administrators_system_group, administrators_system_group, 600)
 | |
|         msg_id = self.send_stream_message(test_bot, "Denmark")
 | |
|         result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
 | |
|         self.assert_json_error(result, "You don't have permission to delete this message")
 | |
| 
 | |
|         set_message_deleting_params(administrators_system_group, everyone_system_group, 600)
 | |
|         message = Message.objects.get(id=msg_id)
 | |
|         message.date_sent = timezone_now() - timedelta(seconds=700)
 | |
|         message.save()
 | |
| 
 | |
|         result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
 | |
|         self.assert_json_error(result, "The time limit for deleting this message has passed")
 | |
| 
 | |
|         message.date_sent = timezone_now() - timedelta(seconds=400)
 | |
|         message.save()
 | |
|         result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
 | |
|         self.assert_json_success(result)
 | |
| 
 | |
|     def test_delete_message_according_to_can_delete_any_message_group(self) -> None:
 | |
|         def check_delete_message_by_sender(
 | |
|             sender_name: str, error_msg: str | None = None, is_stream_message: bool = True
 | |
|         ) -> None:
 | |
|             sender = self.example_user(sender_name)
 | |
|             if is_stream_message:
 | |
|                 msg_id = self.send_stream_message(sender, "Verona")
 | |
|             else:
 | |
|                 msg_id = self.send_personal_message(sender, self.example_user("desdemona"))
 | |
| 
 | |
|             self.login_user(sender)
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             if error_msg is None:
 | |
|                 self.assert_json_success(result)
 | |
|             else:
 | |
|                 self.assert_json_error(result, error_msg)
 | |
| 
 | |
|         def check_delete_message_by_other_user(
 | |
|             sender_name: str,
 | |
|             other_user_name: str,
 | |
|             error_msg: str | None = None,
 | |
|             is_stream_message: bool = True,
 | |
|         ) -> None:
 | |
|             sender = self.example_user(sender_name)
 | |
|             other_user = self.example_user(other_user_name)
 | |
|             if is_stream_message:
 | |
|                 msg_id = self.send_stream_message(sender, "Verona")
 | |
|             else:
 | |
|                 msg_id = self.send_personal_message(sender, other_user)
 | |
|             self.login_user(other_user)
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             if error_msg is None:
 | |
|                 self.assert_json_success(result)
 | |
|             else:
 | |
|                 self.assert_json_error(result, error_msg)
 | |
| 
 | |
|         realm = get_realm("zulip")
 | |
|         stream = get_stream("Verona", realm)
 | |
|         iago = self.example_user("iago")
 | |
| 
 | |
|         administrators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         moderators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         everyone_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.EVERYONE, realm=realm, is_system_group=True
 | |
|         )
 | |
|         nobody_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.NOBODY, realm=realm, is_system_group=True
 | |
|         )
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_any_message_group",
 | |
|             administrators_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             everyone_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
| 
 | |
|         # Only admins can delete any message. Everyone else can only delete their
 | |
|         # own message.
 | |
|         check_delete_message_by_sender("shiva")
 | |
|         check_delete_message_by_other_user(
 | |
|             "hamlet", "shiva", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_other_user("hamlet", "iago")
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_any_message_group",
 | |
|             moderators_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             administrators_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
| 
 | |
|         # Admins and moderators can delete any message. No one else can delete any
 | |
|         # message.
 | |
| 
 | |
|         # Test deleting channel messages
 | |
|         check_delete_message_by_sender(
 | |
|             "cordelia", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_sender("shiva")
 | |
|         check_delete_message_by_other_user("iago", "shiva")
 | |
|         check_delete_message_by_other_user(
 | |
|             "hamlet", "cordelia", "You don't have permission to delete this message"
 | |
|         )
 | |
|         # Test deleting DMs
 | |
|         check_delete_message_by_sender(
 | |
|             "cordelia", "You don't have permission to delete this message", is_stream_message=False
 | |
|         )
 | |
|         check_delete_message_by_sender("shiva", is_stream_message=False)
 | |
|         check_delete_message_by_other_user("iago", "shiva", is_stream_message=False)
 | |
|         check_delete_message_by_other_user(
 | |
|             "hamlet",
 | |
|             "cordelia",
 | |
|             "You don't have permission to delete this message",
 | |
|             is_stream_message=False,
 | |
|         )
 | |
| 
 | |
|         # Check that guest cannot delete any message even when they are member
 | |
|         # of the group which is allowed to delete any message.
 | |
|         polonius = self.example_user("polonius")
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         user_group = check_add_user_group(
 | |
|             realm, "test-group", [hamlet, polonius], acting_user=hamlet
 | |
|         )
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_any_message_group",
 | |
|             user_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         # Test deleting channel messages
 | |
|         check_delete_message_by_other_user("cordelia", "hamlet")
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia", "polonius", "You don't have permission to delete this message"
 | |
|         )
 | |
|         # Test deleting DMs
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia",
 | |
|             "polonius",
 | |
|             "You don't have permission to delete this message",
 | |
|             is_stream_message=False,
 | |
|         )
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_any_message_group",
 | |
|             nobody_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             nobody_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
| 
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia", "shiva", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia",
 | |
|             "shiva",
 | |
|             "You don't have permission to delete this message",
 | |
|             is_stream_message=False,
 | |
|         )
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_any_message_group",
 | |
|             moderators_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         # Users in channel-level `can_delete_any_message_group` can delete
 | |
|         # any message in the channel.
 | |
|         check_delete_message_by_other_user("cordelia", "shiva")
 | |
|         check_delete_message_by_sender(
 | |
|             "polonius", "You don't have permission to delete this message"
 | |
|         )
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_any_message_group",
 | |
|             nobody_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_administer_channel_group",
 | |
|             moderators_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         # Channel administrators can't delete messages if they don't have
 | |
|         # the required permissions.
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia", "shiva", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia", "iago", "You don't have permission to delete this message"
 | |
|         )
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_any_message_group",
 | |
|             everyone_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         # Everyone is allowed to delete any message in the channel.
 | |
|         check_delete_message_by_other_user("cordelia", "polonius")
 | |
|         check_delete_message_by_other_user("cordelia", "iago")
 | |
| 
 | |
|         # Cannot delete DMs as organization-level permission is set to nobody.
 | |
|         check_delete_message_by_other_user(
 | |
|             "cordelia",
 | |
|             "iago",
 | |
|             "You don't have permission to delete this message",
 | |
|             is_stream_message=False,
 | |
|         )
 | |
|         check_delete_message_by_sender(
 | |
|             "iago", "You don't have permission to delete this message", is_stream_message=False
 | |
|         )
 | |
| 
 | |
|     def test_delete_message_according_to_can_delete_own_message_group(self) -> None:
 | |
|         def check_delete_message_by_sender(
 | |
|             sender_name: str, error_msg: str | None = None, is_stream_message: bool = True
 | |
|         ) -> None:
 | |
|             sender = self.example_user(sender_name)
 | |
|             if is_stream_message:
 | |
|                 msg_id = self.send_stream_message(sender, "Verona")
 | |
|             else:
 | |
|                 msg_id = self.send_personal_message(sender, self.example_user("desdemona"))
 | |
| 
 | |
|             self.login_user(sender)
 | |
|             result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|             if error_msg is None:
 | |
|                 self.assert_json_success(result)
 | |
|             else:
 | |
|                 self.assert_json_error(result, error_msg)
 | |
| 
 | |
|         realm = get_realm("zulip")
 | |
|         stream = get_stream("Verona", realm)
 | |
|         iago = self.example_user("iago")
 | |
| 
 | |
|         administrators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         moderators_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         everyone_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.EVERYONE, realm=realm, is_system_group=True
 | |
|         )
 | |
|         members_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.MEMBERS, realm=realm, is_system_group=True
 | |
|         )
 | |
|         nobody_system_group = NamedUserGroup.objects.get(
 | |
|             name=SystemGroups.NOBODY, realm=realm, is_system_group=True
 | |
|         )
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             administrators_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_any_message_group",
 | |
|             nobody_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         # Test deleting channel messages
 | |
|         check_delete_message_by_sender("shiva", "You don't have permission to delete this message")
 | |
|         check_delete_message_by_sender("iago")
 | |
|         # Test deleting DMs
 | |
|         check_delete_message_by_sender(
 | |
|             "shiva", "You don't have permission to delete this message", is_stream_message=False
 | |
|         )
 | |
|         check_delete_message_by_sender("iago", is_stream_message=False)
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             moderators_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         # Test deleting channel messages
 | |
|         check_delete_message_by_sender(
 | |
|             "cordelia", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_sender("shiva")
 | |
|         # Test deleting DMs
 | |
|         check_delete_message_by_sender(
 | |
|             "cordelia", "You don't have permission to delete this message", is_stream_message=False
 | |
|         )
 | |
|         check_delete_message_by_sender("shiva", is_stream_message=False)
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm,
 | |
|             "can_delete_own_message_group",
 | |
|             members_system_group,
 | |
|             acting_user=None,
 | |
|         )
 | |
|         check_delete_message_by_sender(
 | |
|             "polonius", "You don't have permission to delete this message"
 | |
|         )
 | |
|         check_delete_message_by_sender("cordelia")
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm, "can_delete_own_message_group", everyone_system_group, acting_user=None
 | |
|         )
 | |
|         # Test deleting channel messages
 | |
|         check_delete_message_by_sender("cordelia")
 | |
|         check_delete_message_by_sender("polonius")
 | |
|         # Test deleting DMs
 | |
|         check_delete_message_by_sender("cordelia", is_stream_message=False)
 | |
|         check_delete_message_by_sender("polonius", is_stream_message=False)
 | |
| 
 | |
|         do_change_realm_permission_group_setting(
 | |
|             realm, "can_delete_own_message_group", nobody_system_group, acting_user=None
 | |
|         )
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_own_message_group",
 | |
|             members_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         # Users in per-channel `can_delete_own_message_group` can delete their
 | |
|         # own messages.
 | |
|         check_delete_message_by_sender("hamlet")
 | |
|         check_delete_message_by_sender(
 | |
|             "polonius", "You don't have permission to delete this message"
 | |
|         )
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_own_message_group",
 | |
|             nobody_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_administer_channel_group",
 | |
|             moderators_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         # Channel administrators can't delete messages if they don't have
 | |
|         # the required permissions.
 | |
|         check_delete_message_by_sender("shiva", "You don't have permission to delete this message")
 | |
|         check_delete_message_by_sender("iago", "You don't have permission to delete this message")
 | |
| 
 | |
|         do_change_stream_group_based_setting(
 | |
|             stream,
 | |
|             "can_delete_own_message_group",
 | |
|             everyone_system_group,
 | |
|             acting_user=iago,
 | |
|         )
 | |
|         check_delete_message_by_sender("iago")
 | |
| 
 | |
|         # Cannot delete DMs as organization-level permission is set to nobody.
 | |
|         check_delete_message_by_sender(
 | |
|             "iago", "You don't have permission to delete this message", is_stream_message=False
 | |
|         )
 | |
| 
 | |
|     def test_delete_event_sent_after_transaction_commits(self) -> None:
 | |
|         """
 | |
|         Tests that `send_event_rollback_unsafe` is hooked to `transaction.on_commit`.
 | |
|         This is important, because we don't want to end up holding locks on message rows
 | |
|         for too long if the event queue runs into a problem.
 | |
|         """
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         self.send_stream_message(hamlet, "Denmark")
 | |
|         message = self.get_last_message()
 | |
| 
 | |
|         with (
 | |
|             self.capture_send_event_calls(expected_num_events=1),
 | |
|             mock.patch("zerver.tornado.django_api.queue_json_publish_rollback_unsafe") as m,
 | |
|         ):
 | |
|             m.side_effect = AssertionError(
 | |
|                 "Events should be sent only after the transaction commits."
 | |
|             )
 | |
|             do_delete_messages(hamlet.realm, [message], acting_user=None)
 | |
| 
 | |
|     def test_delete_message_in_unsubscribed_private_stream(self) -> None:
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         iago = self.example_user("iago")
 | |
|         self.assertEqual(iago.role, UserProfile.ROLE_REALM_ADMINISTRATOR)
 | |
|         self.login("hamlet")
 | |
| 
 | |
|         self.make_stream("privatestream", invite_only=True, history_public_to_subscribers=False)
 | |
|         self.subscribe(hamlet, "privatestream")
 | |
|         self.subscribe(iago, "privatestream")
 | |
|         msg_id = self.send_stream_message(
 | |
|             hamlet, "privatestream", topic_name="editing", content="before edit"
 | |
|         )
 | |
|         self.unsubscribe(iago, "privatestream")
 | |
|         self.logout()
 | |
|         self.login("iago")
 | |
|         result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|         self.assert_json_error(result, "Invalid message(s)")
 | |
|         self.assertTrue(Message.objects.filter(id=msg_id).exists())
 | |
| 
 | |
|         # Ensure iago can delete the message after resubscribing, to be certain
 | |
|         # it's the subscribed/unsubscribed status that's the decisive factor in the
 | |
|         # permission to do so.
 | |
|         self.subscribe(iago, "privatestream")
 | |
|         result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|         self.assert_json_success(result)
 | |
|         self.assertFalse(Message.objects.filter(id=msg_id).exists())
 | |
| 
 | |
|     def test_delete_message_from_archived_channel(self) -> None:
 | |
|         hamlet = self.example_user("hamlet")
 | |
|         self.login("hamlet")
 | |
| 
 | |
|         stream = self.make_stream("stream1")
 | |
|         self.subscribe(hamlet, "stream1")
 | |
|         msg_id = self.send_stream_message(
 | |
|             hamlet, "stream1", topic_name="editing", content="before edit"
 | |
|         )
 | |
|         do_deactivate_stream(stream, acting_user=hamlet)
 | |
| 
 | |
|         result = self.client_delete(f"/json/messages/{msg_id}")
 | |
|         self.assert_json_error(result, "Invalid message(s)")
 | |
| 
 | |
|     def test_update_first_message_id_on_stream_message_deletion(self) -> None:
 | |
|         realm = get_realm("zulip")
 | |
|         stream_name = "test"
 | |
|         cordelia = self.example_user("cordelia")
 | |
|         self.make_stream(stream_name)
 | |
|         self.subscribe(cordelia, stream_name)
 | |
|         message_ids = [self.send_stream_message(cordelia, stream_name) for _ in range(5)]
 | |
|         first_message_id = message_ids[0]
 | |
| 
 | |
|         message = Message.objects.get(id=message_ids[3])
 | |
|         do_delete_messages(realm, [message], acting_user=None)
 | |
|         stream = get_stream(stream_name, realm)
 | |
|         self.assertEqual(stream.first_message_id, first_message_id)
 | |
| 
 | |
|         first_message = Message.objects.get(id=first_message_id)
 | |
|         do_delete_messages(realm, [first_message], acting_user=None)
 | |
|         stream = get_stream(stream_name, realm)
 | |
|         self.assertEqual(stream.first_message_id, message_ids[1])
 | |
| 
 | |
|         all_messages = Message.objects.filter(id__in=message_ids)
 | |
|         with self.assert_database_query_count(27):
 | |
|             do_delete_messages(realm, all_messages, acting_user=None)
 | |
|         stream = get_stream(stream_name, realm)
 | |
|         self.assertEqual(stream.first_message_id, None)
 |