Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 000aa89

Browse files
authored
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.
1 parent b5fef60 commit 000aa89

File tree

7 files changed

+145
-11
lines changed

7 files changed

+145
-11
lines changed

changelog.d/10644.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Rooms with unsupported room versions are no longer returned via `/sync`.

mypy.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ files =
9090
tests/test_utils,
9191
tests/handlers/test_password_providers.py,
9292
tests/handlers/test_room_summary.py,
93+
tests/handlers/test_sync.py,
9394
tests/rest/client/v1/test_login.py,
9495
tests/rest/client/v2_alpha/test_auth.py,
9596
tests/util/test_itertools.py,

synapse/handlers/sync.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Copyright 2015, 2016 OpenMarket Ltd
2-
# Copyright 2018, 2019 New Vector Ltd
1+
# Copyright 2015-2021 The Matrix.org Foundation C.I.C.
32
#
43
# Licensed under the Apache License, Version 2.0 (the "License");
54
# you may not use this file except in compliance with the License.
@@ -31,6 +30,7 @@
3130

3231
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
3332
from synapse.api.filtering import FilterCollection
33+
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
3434
from synapse.events import EventBase
3535
from synapse.logging.context import current_context
3636
from synapse.logging.opentracing import SynapseTags, log_kv, set_tag, start_active_span
@@ -1843,6 +1843,9 @@ async def _get_all_rooms(
18431843
knocked = []
18441844

18451845
for event in room_list:
1846+
if event.room_version_id not in KNOWN_ROOM_VERSIONS:
1847+
continue
1848+
18461849
if event.membership == Membership.JOIN:
18471850
room_entries.append(
18481851
RoomSyncResultBuilder(

synapse/storage/databases/main/roommember.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,9 +386,10 @@ def _get_rooms_for_local_user_where_membership_is_txn(
386386
)
387387

388388
sql = """
389-
SELECT room_id, e.sender, c.membership, event_id, e.stream_ordering
389+
SELECT room_id, e.sender, c.membership, event_id, e.stream_ordering, r.room_version
390390
FROM local_current_membership AS c
391391
INNER JOIN events AS e USING (room_id, event_id)
392+
INNER JOIN rooms AS r USING (room_id)
392393
WHERE
393394
user_id = ?
394395
AND %s
@@ -397,7 +398,7 @@ def _get_rooms_for_local_user_where_membership_is_txn(
397398
)
398399

399400
txn.execute(sql, (user_id, *args))
400-
results = [RoomsForUser(**r) for r in self.db_pool.cursor_to_dict(txn)]
401+
results = [RoomsForUser(*r) for r in txn]
401402

402403
return results
403404

@@ -447,7 +448,8 @@ async def get_rooms_for_user_with_stream_ordering(
447448
448449
Returns:
449450
Returns the rooms the user is in currently, along with the stream
450-
ordering of the most recent join for that user and room.
451+
ordering of the most recent join for that user and room, along with
452+
the room version of the room.
451453
"""
452454
return await self.db_pool.runInteraction(
453455
"get_rooms_for_user_with_stream_ordering",

synapse/storage/roommember.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class RoomsForUser:
3030
membership: str
3131
event_id: str
3232
stream_ordering: int
33+
room_version_id: str
3334

3435

3536
@attr.s(slots=True, frozen=True, weakref_slot=False, auto_attribs=True)

tests/handlers/test_sync.py

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from typing import Optional
16+
17+
from synapse.api.constants import EventTypes, JoinRules
1518
from synapse.api.errors import Codes, ResourceLimitError
1619
from synapse.api.filtering import DEFAULT_FILTER_COLLECTION
20+
from synapse.api.room_versions import RoomVersions
1721
from synapse.handlers.sync import SyncConfig
22+
from synapse.rest import admin
23+
from synapse.rest.client import knock, login, room
24+
from synapse.server import HomeServer
1825
from synapse.types import UserID, create_requester
1926

2027
import tests.unittest
@@ -24,8 +31,14 @@
2431
class SyncTestCase(tests.unittest.HomeserverTestCase):
2532
"""Tests Sync Handler."""
2633

27-
def prepare(self, reactor, clock, hs):
28-
self.hs = hs
34+
servlets = [
35+
admin.register_servlets,
36+
knock.register_servlets,
37+
login.register_servlets,
38+
room.register_servlets,
39+
]
40+
41+
def prepare(self, reactor, clock, hs: HomeServer):
2942
self.sync_handler = self.hs.get_sync_handler()
3043
self.store = self.hs.get_datastore()
3144

@@ -68,12 +81,124 @@ def test_wait_for_sync_for_user_auth_blocking(self):
6881
)
6982
self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
7083

84+
def test_unknown_room_version(self):
85+
"""
86+
A room with an unknown room version should not break sync (and should be excluded).
87+
"""
88+
inviter = self.register_user("creator", "pass", admin=True)
89+
inviter_tok = self.login("@creator:test", "pass")
90+
91+
user = self.register_user("user", "pass")
92+
tok = self.login("user", "pass")
93+
94+
# Do an initial sync on a different device.
95+
requester = create_requester(user)
96+
initial_result = self.get_success(
97+
self.sync_handler.wait_for_sync_for_user(
98+
requester, sync_config=generate_sync_config(user, device_id="dev")
99+
)
100+
)
101+
102+
# Create a room as the user.
103+
joined_room = self.helper.create_room_as(user, tok=tok)
104+
105+
# Invite the user to the room as someone else.
106+
invite_room = self.helper.create_room_as(inviter, tok=inviter_tok)
107+
self.helper.invite(invite_room, targ=user, tok=inviter_tok)
108+
109+
knock_room = self.helper.create_room_as(
110+
inviter, room_version=RoomVersions.V7.identifier, tok=inviter_tok
111+
)
112+
self.helper.send_state(
113+
knock_room,
114+
EventTypes.JoinRules,
115+
{"join_rule": JoinRules.KNOCK},
116+
tok=inviter_tok,
117+
)
118+
channel = self.make_request(
119+
"POST",
120+
"/_matrix/client/r0/knock/%s" % (knock_room,),
121+
b"{}",
122+
tok,
123+
)
124+
self.assertEquals(200, channel.code, channel.result)
125+
126+
# The rooms should appear in the sync response.
127+
result = self.get_success(
128+
self.sync_handler.wait_for_sync_for_user(
129+
requester, sync_config=generate_sync_config(user)
130+
)
131+
)
132+
self.assertIn(joined_room, [r.room_id for r in result.joined])
133+
self.assertIn(invite_room, [r.room_id for r in result.invited])
134+
self.assertIn(knock_room, [r.room_id for r in result.knocked])
135+
136+
# Test a incremental sync (by providing a since_token).
137+
result = self.get_success(
138+
self.sync_handler.wait_for_sync_for_user(
139+
requester,
140+
sync_config=generate_sync_config(user, device_id="dev"),
141+
since_token=initial_result.next_batch,
142+
)
143+
)
144+
self.assertIn(joined_room, [r.room_id for r in result.joined])
145+
self.assertIn(invite_room, [r.room_id for r in result.invited])
146+
self.assertIn(knock_room, [r.room_id for r in result.knocked])
147+
148+
# Poke the database and update the room version to an unknown one.
149+
for room_id in (joined_room, invite_room, knock_room):
150+
self.get_success(
151+
self.hs.get_datastores().main.db_pool.simple_update(
152+
"rooms",
153+
keyvalues={"room_id": room_id},
154+
updatevalues={"room_version": "unknown-room-version"},
155+
desc="updated-room-version",
156+
)
157+
)
158+
159+
# Blow away caches (supported room versions can only change due to a restart).
160+
self.get_success(
161+
self.store.get_rooms_for_user_with_stream_ordering.invalidate_all()
162+
)
163+
self.store._get_event_cache.clear()
164+
165+
# The rooms should be excluded from the sync response.
166+
# Get a new request key.
167+
result = self.get_success(
168+
self.sync_handler.wait_for_sync_for_user(
169+
requester, sync_config=generate_sync_config(user)
170+
)
171+
)
172+
self.assertNotIn(joined_room, [r.room_id for r in result.joined])
173+
self.assertNotIn(invite_room, [r.room_id for r in result.invited])
174+
self.assertNotIn(knock_room, [r.room_id for r in result.knocked])
175+
176+
# The rooms should also not be in an incremental sync.
177+
result = self.get_success(
178+
self.sync_handler.wait_for_sync_for_user(
179+
requester,
180+
sync_config=generate_sync_config(user, device_id="dev"),
181+
since_token=initial_result.next_batch,
182+
)
183+
)
184+
self.assertNotIn(joined_room, [r.room_id for r in result.joined])
185+
self.assertNotIn(invite_room, [r.room_id for r in result.invited])
186+
self.assertNotIn(knock_room, [r.room_id for r in result.knocked])
187+
188+
189+
_request_key = 0
190+
71191

72-
def generate_sync_config(user_id: str) -> SyncConfig:
192+
def generate_sync_config(
193+
user_id: str, device_id: Optional[str] = "device_id"
194+
) -> SyncConfig:
195+
"""Generate a sync config (with a unique request key)."""
196+
global _request_key
197+
_request_key += 1
73198
return SyncConfig(
74-
user=UserID(user_id.split(":")[0][1:], user_id.split(":")[1]),
199+
user=UserID.from_string(user_id),
75200
filter_collection=DEFAULT_FILTER_COLLECTION,
76201
is_guest=False,
77-
request_key="request_key",
78-
device_id="device_id",
202+
request_key=("request_key", _request_key),
203+
device_id=device_id,
79204
)

tests/replication/slave/storage/test_events.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def test_invites(self):
150150
"invite",
151151
event.event_id,
152152
event.internal_metadata.stream_ordering,
153+
RoomVersions.V1.identifier,
153154
)
154155
],
155156
)

0 commit comments

Comments
 (0)