Skip to content

Commit df0077d

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into token-registration
To get the flattened tests/rest/client directory of matrix-org#10667
2 parents 2e59dda + ecd823d commit df0077d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+539
-312
lines changed

changelog.d/10593.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reject Client-Server `/keys/query` requests which provide `device_ids` incorrectly.

changelog.d/10624.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clean up some of the federation event authentication code for clarity.

changelog.d/10640.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clean up some of the federation event authentication code for clarity.

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`.

changelog.d/10664.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Simplify tests for device admin rest API.

changelog.d/10667.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Flatten the `tests.synapse.rests` package by moving the contents of `v1` and `v2_alpha` into the parent.

mypy.ini

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ files =
9090
tests/test_utils,
9191
tests/handlers/test_password_providers.py,
9292
tests/handlers/test_room_summary.py,
93-
tests/rest/client/v1/test_login.py,
94-
tests/rest/client/v2_alpha/test_auth.py,
93+
tests/handlers/test_sync.py,
94+
tests/rest/client/test_login.py,
95+
tests/rest/client/test_auth.py,
9596
tests/util/test_itertools.py,
9697
tests/util/test_stream_change_cache.py
9798

synapse/api/errors.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ def error_dict(self):
147147
return cs_error(self.msg, self.errcode)
148148

149149

150+
class InvalidAPICallError(SynapseError):
151+
"""You called an existing API endpoint, but fed that endpoint
152+
invalid or incomplete data."""
153+
154+
def __init__(self, msg: str):
155+
super().__init__(HTTPStatus.BAD_REQUEST, msg, Codes.BAD_JSON)
156+
157+
150158
class ProxiedRequestError(SynapseError):
151159
"""An error from a general matrix endpoint, eg. from a proxied Matrix API call.
152160

synapse/federation/federation_server.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,9 +1005,7 @@ async def _process_incoming_pdus_in_room_inner(
10051005
async with lock:
10061006
logger.info("handling received PDU: %s", event)
10071007
try:
1008-
await self.handler.on_receive_pdu(
1009-
origin, event, sent_to_us_directly=True
1010-
)
1008+
await self.handler.on_receive_pdu(origin, event)
10111009
except FederationError as e:
10121010
# XXX: Ideally we'd inform the remote we failed to process
10131011
# the event, but we can't return an error in the transaction

synapse/handlers/federation.py

Lines changed: 249 additions & 190 deletions
Large diffs are not rendered by default.

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/rest/client/keys.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
# limitations under the License.
1616

1717
import logging
18+
from typing import Any
1819

19-
from synapse.api.errors import SynapseError
20+
from synapse.api.errors import InvalidAPICallError, SynapseError
2021
from synapse.http.servlet import (
2122
RestServlet,
2223
parse_integer,
@@ -163,6 +164,19 @@ async def on_POST(self, request):
163164
device_id = requester.device_id
164165
timeout = parse_integer(request, "timeout", 10 * 1000)
165166
body = parse_json_object_from_request(request)
167+
168+
device_keys = body.get("device_keys")
169+
if not isinstance(device_keys, dict):
170+
raise InvalidAPICallError("'device_keys' must be a JSON object")
171+
172+
def is_list_of_strings(values: Any) -> bool:
173+
return isinstance(values, list) and all(isinstance(v, str) for v in values)
174+
175+
if any(not is_list_of_strings(keys) for keys in device_keys.values()):
176+
raise InvalidAPICallError(
177+
"'device_keys' values must be a list of strings",
178+
)
179+
166180
result = await self.e2e_keys_handler.query_devices(
167181
body, timeout, user_id, device_id
168182
)

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)