mirror of
				https://github.com/matrix-org/synapse.git
				synced 2025-11-04 06:07:23 +00:00 
			
		
		
		
	Do not include rooms with an unknown room version in a sync response. (#10644)
A user will still see this room if it is in a local cache, but it will not reappear if clearing the cache and reloading.
This commit is contained in:
		
							parent
							
								
									b5fef6054a
								
							
						
					
					
						commit
						000aa89be6
					
				
							
								
								
									
										1
									
								
								changelog.d/10644.bugfix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								changelog.d/10644.bugfix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Rooms with unsupported room versions are no longer returned via `/sync`.
 | 
			
		||||
							
								
								
									
										1
									
								
								mypy.ini
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								mypy.ini
									
									
									
									
									
								
							@ -90,6 +90,7 @@ files =
 | 
			
		||||
  tests/test_utils,
 | 
			
		||||
  tests/handlers/test_password_providers.py,
 | 
			
		||||
  tests/handlers/test_room_summary.py,
 | 
			
		||||
  tests/handlers/test_sync.py,
 | 
			
		||||
  tests/rest/client/v1/test_login.py,
 | 
			
		||||
  tests/rest/client/v2_alpha/test_auth.py,
 | 
			
		||||
  tests/util/test_itertools.py,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
# Copyright 2015, 2016 OpenMarket Ltd
 | 
			
		||||
# Copyright 2018, 2019 New Vector Ltd
 | 
			
		||||
# Copyright 2015-2021 The Matrix.org Foundation C.I.C.
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
@ -31,6 +30,7 @@ from prometheus_client import Counter
 | 
			
		||||
 | 
			
		||||
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
 | 
			
		||||
from synapse.api.filtering import FilterCollection
 | 
			
		||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
 | 
			
		||||
from synapse.events import EventBase
 | 
			
		||||
from synapse.logging.context import current_context
 | 
			
		||||
from synapse.logging.opentracing import SynapseTags, log_kv, set_tag, start_active_span
 | 
			
		||||
@ -1843,6 +1843,9 @@ class SyncHandler:
 | 
			
		||||
        knocked = []
 | 
			
		||||
 | 
			
		||||
        for event in room_list:
 | 
			
		||||
            if event.room_version_id not in KNOWN_ROOM_VERSIONS:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if event.membership == Membership.JOIN:
 | 
			
		||||
                room_entries.append(
 | 
			
		||||
                    RoomSyncResultBuilder(
 | 
			
		||||
 | 
			
		||||
@ -386,9 +386,10 @@ class RoomMemberWorkerStore(EventsWorkerStore):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        sql = """
 | 
			
		||||
            SELECT room_id, e.sender, c.membership, event_id, e.stream_ordering
 | 
			
		||||
            SELECT room_id, e.sender, c.membership, event_id, e.stream_ordering, r.room_version
 | 
			
		||||
            FROM local_current_membership AS c
 | 
			
		||||
            INNER JOIN events AS e USING (room_id, event_id)
 | 
			
		||||
            INNER JOIN rooms AS r USING (room_id)
 | 
			
		||||
            WHERE
 | 
			
		||||
                user_id = ?
 | 
			
		||||
                AND %s
 | 
			
		||||
@ -397,7 +398,7 @@ class RoomMemberWorkerStore(EventsWorkerStore):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        txn.execute(sql, (user_id, *args))
 | 
			
		||||
        results = [RoomsForUser(**r) for r in self.db_pool.cursor_to_dict(txn)]
 | 
			
		||||
        results = [RoomsForUser(*r) for r in txn]
 | 
			
		||||
 | 
			
		||||
        return results
 | 
			
		||||
 | 
			
		||||
@ -447,7 +448,8 @@ class RoomMemberWorkerStore(EventsWorkerStore):
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Returns the rooms the user is in currently, along with the stream
 | 
			
		||||
            ordering of the most recent join for that user and room.
 | 
			
		||||
            ordering of the most recent join for that user and room, along with
 | 
			
		||||
            the room version of the room.
 | 
			
		||||
        """
 | 
			
		||||
        return await self.db_pool.runInteraction(
 | 
			
		||||
            "get_rooms_for_user_with_stream_ordering",
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ class RoomsForUser:
 | 
			
		||||
    membership: str
 | 
			
		||||
    event_id: str
 | 
			
		||||
    stream_ordering: int
 | 
			
		||||
    room_version_id: str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@attr.s(slots=True, frozen=True, weakref_slot=False, auto_attribs=True)
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,16 @@
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from synapse.api.constants import EventTypes, JoinRules
 | 
			
		||||
from synapse.api.errors import Codes, ResourceLimitError
 | 
			
		||||
from synapse.api.filtering import DEFAULT_FILTER_COLLECTION
 | 
			
		||||
from synapse.api.room_versions import RoomVersions
 | 
			
		||||
from synapse.handlers.sync import SyncConfig
 | 
			
		||||
from synapse.rest import admin
 | 
			
		||||
from synapse.rest.client import knock, login, room
 | 
			
		||||
from synapse.server import HomeServer
 | 
			
		||||
from synapse.types import UserID, create_requester
 | 
			
		||||
 | 
			
		||||
import tests.unittest
 | 
			
		||||
@ -24,8 +31,14 @@ import tests.utils
 | 
			
		||||
class SyncTestCase(tests.unittest.HomeserverTestCase):
 | 
			
		||||
    """Tests Sync Handler."""
 | 
			
		||||
 | 
			
		||||
    def prepare(self, reactor, clock, hs):
 | 
			
		||||
        self.hs = hs
 | 
			
		||||
    servlets = [
 | 
			
		||||
        admin.register_servlets,
 | 
			
		||||
        knock.register_servlets,
 | 
			
		||||
        login.register_servlets,
 | 
			
		||||
        room.register_servlets,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def prepare(self, reactor, clock, hs: HomeServer):
 | 
			
		||||
        self.sync_handler = self.hs.get_sync_handler()
 | 
			
		||||
        self.store = self.hs.get_datastore()
 | 
			
		||||
 | 
			
		||||
@ -68,12 +81,124 @@ class SyncTestCase(tests.unittest.HomeserverTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
 | 
			
		||||
 | 
			
		||||
    def test_unknown_room_version(self):
 | 
			
		||||
        """
 | 
			
		||||
        A room with an unknown room version should not break sync (and should be excluded).
 | 
			
		||||
        """
 | 
			
		||||
        inviter = self.register_user("creator", "pass", admin=True)
 | 
			
		||||
        inviter_tok = self.login("@creator:test", "pass")
 | 
			
		||||
 | 
			
		||||
def generate_sync_config(user_id: str) -> SyncConfig:
 | 
			
		||||
        user = self.register_user("user", "pass")
 | 
			
		||||
        tok = self.login("user", "pass")
 | 
			
		||||
 | 
			
		||||
        # Do an initial sync on a different device.
 | 
			
		||||
        requester = create_requester(user)
 | 
			
		||||
        initial_result = self.get_success(
 | 
			
		||||
            self.sync_handler.wait_for_sync_for_user(
 | 
			
		||||
                requester, sync_config=generate_sync_config(user, device_id="dev")
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Create a room as the user.
 | 
			
		||||
        joined_room = self.helper.create_room_as(user, tok=tok)
 | 
			
		||||
 | 
			
		||||
        # Invite the user to the room as someone else.
 | 
			
		||||
        invite_room = self.helper.create_room_as(inviter, tok=inviter_tok)
 | 
			
		||||
        self.helper.invite(invite_room, targ=user, tok=inviter_tok)
 | 
			
		||||
 | 
			
		||||
        knock_room = self.helper.create_room_as(
 | 
			
		||||
            inviter, room_version=RoomVersions.V7.identifier, tok=inviter_tok
 | 
			
		||||
        )
 | 
			
		||||
        self.helper.send_state(
 | 
			
		||||
            knock_room,
 | 
			
		||||
            EventTypes.JoinRules,
 | 
			
		||||
            {"join_rule": JoinRules.KNOCK},
 | 
			
		||||
            tok=inviter_tok,
 | 
			
		||||
        )
 | 
			
		||||
        channel = self.make_request(
 | 
			
		||||
            "POST",
 | 
			
		||||
            "/_matrix/client/r0/knock/%s" % (knock_room,),
 | 
			
		||||
            b"{}",
 | 
			
		||||
            tok,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEquals(200, channel.code, channel.result)
 | 
			
		||||
 | 
			
		||||
        # The rooms should appear in the sync response.
 | 
			
		||||
        result = self.get_success(
 | 
			
		||||
            self.sync_handler.wait_for_sync_for_user(
 | 
			
		||||
                requester, sync_config=generate_sync_config(user)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIn(joined_room, [r.room_id for r in result.joined])
 | 
			
		||||
        self.assertIn(invite_room, [r.room_id for r in result.invited])
 | 
			
		||||
        self.assertIn(knock_room, [r.room_id for r in result.knocked])
 | 
			
		||||
 | 
			
		||||
        # Test a incremental sync (by providing a since_token).
 | 
			
		||||
        result = self.get_success(
 | 
			
		||||
            self.sync_handler.wait_for_sync_for_user(
 | 
			
		||||
                requester,
 | 
			
		||||
                sync_config=generate_sync_config(user, device_id="dev"),
 | 
			
		||||
                since_token=initial_result.next_batch,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIn(joined_room, [r.room_id for r in result.joined])
 | 
			
		||||
        self.assertIn(invite_room, [r.room_id for r in result.invited])
 | 
			
		||||
        self.assertIn(knock_room, [r.room_id for r in result.knocked])
 | 
			
		||||
 | 
			
		||||
        # Poke the database and update the room version to an unknown one.
 | 
			
		||||
        for room_id in (joined_room, invite_room, knock_room):
 | 
			
		||||
            self.get_success(
 | 
			
		||||
                self.hs.get_datastores().main.db_pool.simple_update(
 | 
			
		||||
                    "rooms",
 | 
			
		||||
                    keyvalues={"room_id": room_id},
 | 
			
		||||
                    updatevalues={"room_version": "unknown-room-version"},
 | 
			
		||||
                    desc="updated-room-version",
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Blow away caches (supported room versions can only change due to a restart).
 | 
			
		||||
        self.get_success(
 | 
			
		||||
            self.store.get_rooms_for_user_with_stream_ordering.invalidate_all()
 | 
			
		||||
        )
 | 
			
		||||
        self.store._get_event_cache.clear()
 | 
			
		||||
 | 
			
		||||
        # The rooms should be excluded from the sync response.
 | 
			
		||||
        # Get a new request key.
 | 
			
		||||
        result = self.get_success(
 | 
			
		||||
            self.sync_handler.wait_for_sync_for_user(
 | 
			
		||||
                requester, sync_config=generate_sync_config(user)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertNotIn(joined_room, [r.room_id for r in result.joined])
 | 
			
		||||
        self.assertNotIn(invite_room, [r.room_id for r in result.invited])
 | 
			
		||||
        self.assertNotIn(knock_room, [r.room_id for r in result.knocked])
 | 
			
		||||
 | 
			
		||||
        # The rooms should also not be in an incremental sync.
 | 
			
		||||
        result = self.get_success(
 | 
			
		||||
            self.sync_handler.wait_for_sync_for_user(
 | 
			
		||||
                requester,
 | 
			
		||||
                sync_config=generate_sync_config(user, device_id="dev"),
 | 
			
		||||
                since_token=initial_result.next_batch,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertNotIn(joined_room, [r.room_id for r in result.joined])
 | 
			
		||||
        self.assertNotIn(invite_room, [r.room_id for r in result.invited])
 | 
			
		||||
        self.assertNotIn(knock_room, [r.room_id for r in result.knocked])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_request_key = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_sync_config(
 | 
			
		||||
    user_id: str, device_id: Optional[str] = "device_id"
 | 
			
		||||
) -> SyncConfig:
 | 
			
		||||
    """Generate a sync config (with a unique request key)."""
 | 
			
		||||
    global _request_key
 | 
			
		||||
    _request_key += 1
 | 
			
		||||
    return SyncConfig(
 | 
			
		||||
        user=UserID(user_id.split(":")[0][1:], user_id.split(":")[1]),
 | 
			
		||||
        user=UserID.from_string(user_id),
 | 
			
		||||
        filter_collection=DEFAULT_FILTER_COLLECTION,
 | 
			
		||||
        is_guest=False,
 | 
			
		||||
        request_key="request_key",
 | 
			
		||||
        device_id="device_id",
 | 
			
		||||
        request_key=("request_key", _request_key),
 | 
			
		||||
        device_id=device_id,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@ -150,6 +150,7 @@ class SlavedEventStoreTestCase(BaseSlavedStoreTestCase):
 | 
			
		||||
                    "invite",
 | 
			
		||||
                    event.event_id,
 | 
			
		||||
                    event.internal_metadata.stream_ordering,
 | 
			
		||||
                    RoomVersions.V1.identifier,
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user