diff --git a/synapse/handlers/sliding_sync.py b/synapse/handlers/sliding_sync.py index 40b5c84f29..dd4ad5f4ee 100644 --- a/synapse/handlers/sliding_sync.py +++ b/synapse/handlers/sliding_sync.py @@ -11,7 +11,7 @@ if TYPE_CHECKING or HAS_PYDANTIC_V2: else: from pydantic import Extra -from synapse.api.constants import AccountDataTypes, Membership +from synapse.api.constants import AccountDataTypes, EventTypes, Membership from synapse.events import EventBase from synapse.rest.client.models import SlidingSyncBody from synapse.types import JsonMapping, Requester, RoomStreamToken, StreamToken, UserID @@ -576,8 +576,23 @@ class SlidingSyncHandler: space_child_room_ids ) - if filters.is_encrypted: - raise NotImplementedError() + # Filter for encrypted rooms + if filters.is_encrypted is not None: + # Make a copy so we don't run into an error: `Set changed size during iteration` + for room_id in list(filtered_room_id_set): + # TODO: Is there a good method to look up all rooms at once? (N+1 query problem) + is_encrypted = ( + await self.storage_controllers.state.get_current_state_event( + room_id, EventTypes.RoomEncryption, "" + ) + ) + + # If we're looking for encrypted rooms, filter out rooms that are not + # encrypted and vice versa + if (filters.is_encrypted and not is_encrypted) or ( + not filters.is_encrypted and is_encrypted + ): + filtered_room_id_set.remove(room_id) if filters.is_invite: raise NotImplementedError() diff --git a/tests/handlers/test_sliding_sync.py b/tests/handlers/test_sliding_sync.py index 24b37d8c8a..606d365660 100644 --- a/tests/handlers/test_sliding_sync.py +++ b/tests/handlers/test_sliding_sync.py @@ -668,59 +668,35 @@ class FilterRoomsTestCase(HomeserverTestCase): # https://github.com/element-hq/synapse/pull/17187#discussion_r1619492779) from synapse.handlers.sliding_sync import SlidingSyncConfig - filters = SlidingSyncConfig.SlidingSyncList.Filters( + # Try with `is_dm=True` + # ----------------------------- + truthy_filters = SlidingSyncConfig.SlidingSyncList.Filters( is_dm=True, ) # Try filtering the rooms - filtered_room_ids = self.get_success( + truthy_filtered_room_ids = self.get_success( self.sliding_sync_handler.filter_rooms( - UserID.from_string(user1_id), {room_id, dm_room_id}, filters + UserID.from_string(user1_id), {room_id, dm_room_id}, truthy_filters ) ) - self.assertEqual(filtered_room_ids, {dm_room_id}) + self.assertEqual(truthy_filtered_room_ids, {dm_room_id}) - def test_filter_non_dm_rooms(self) -> None: - """ - Test `filter.is_dm` for non-DM rooms - """ - user1_id = self.register_user("user1", "pass") - user1_tok = self.login(user1_id, "pass") - user2_id = self.register_user("user2", "pass") - user2_tok = self.login(user2_id, "pass") - - # Create a normal room - room_id = self.helper.create_room_as( - user1_id, - is_public=False, - tok=user1_tok, - ) - - # Create a DM room - dm_room_id = self._create_dm_room( - inviter_user_id=user1_id, - inviter_tok=user1_tok, - invitee_user_id=user2_id, - invitee_tok=user2_tok, - ) - - # TODO: Better way to avoid the circular import? (see - # https://github.com/element-hq/synapse/pull/17187#discussion_r1619492779) - from synapse.handlers.sliding_sync import SlidingSyncConfig - - filters = SlidingSyncConfig.SlidingSyncList.Filters( + # Try with `is_dm=True` + # ----------------------------- + falsy_filters = SlidingSyncConfig.SlidingSyncList.Filters( is_dm=False, ) # Try filtering the rooms - filtered_room_ids = self.get_success( + falsy_filtered_room_ids = self.get_success( self.sliding_sync_handler.filter_rooms( - UserID.from_string(user1_id), {room_id, dm_room_id}, filters + UserID.from_string(user1_id), {room_id, dm_room_id}, falsy_filters ) ) - self.assertEqual(filtered_room_ids, {room_id}) + self.assertEqual(falsy_filtered_room_ids, {room_id}) def test_filter_space_rooms(self) -> None: """ @@ -894,3 +870,68 @@ class FilterRoomsTestCase(HomeserverTestCase): ) self.assertEqual(filtered_room_ids, {room_id1}) + + def test_filter_encrypted_rooms(self) -> None: + """ + Test `filter.is_encrypted` for encrypted rooms + """ + user1_id = self.register_user("user1", "pass") + user1_tok = self.login(user1_id, "pass") + + # Create a normal room + room_id = self.helper.create_room_as( + user1_id, + is_public=False, + tok=user1_tok, + ) + + # Create an encrypted room + encrypted_room_id = self.helper.create_room_as( + user1_id, + is_public=False, + tok=user1_tok, + ) + self.helper.send_state( + encrypted_room_id, + EventTypes.RoomEncryption, + {"algorithm": "m.megolm.v1.aes-sha2"}, + tok=user1_tok, + ) + + # TODO: Better way to avoid the circular import? (see + # https://github.com/element-hq/synapse/pull/17187#discussion_r1619492779) + from synapse.handlers.sliding_sync import SlidingSyncConfig + + # Try with `is_encrypted=True` + # ----------------------------- + truthy_filters = SlidingSyncConfig.SlidingSyncList.Filters( + is_encrypted=True, + ) + + # Try filtering the rooms + truthy_filtered_room_ids = self.get_success( + self.sliding_sync_handler.filter_rooms( + UserID.from_string(user1_id), + {room_id, encrypted_room_id}, + truthy_filters, + ) + ) + + self.assertEqual(truthy_filtered_room_ids, {encrypted_room_id}) + + # Try with `is_encrypted=False` + # ----------------------------- + falsy_filters = SlidingSyncConfig.SlidingSyncList.Filters( + is_encrypted=False, + ) + + # Try filtering the rooms + falsy_filtered_room_ids = self.get_success( + self.sliding_sync_handler.filter_rooms( + UserID.from_string(user1_id), + {room_id, encrypted_room_id}, + falsy_filters, + ) + ) + + self.assertEqual(falsy_filtered_room_ids, {room_id})