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

Commit 1e571cd

Browse files
authored
Fix appservices being unable to handle to_device messages for multiple users (#16251)
1 parent b1d71c6 commit 1e571cd

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

changelog.d/16251.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug where appservices using MSC2409 to receive to_device messages, would only get messages for one user.

synapse/storage/databases/main/deviceinbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def get_device_messages_txn(
349349
table="devices",
350350
column="user_id",
351351
iterable=user_ids_to_query,
352-
keyvalues={"user_id": user_id, "hidden": False},
352+
keyvalues={"hidden": False},
353353
retcols=("device_id",),
354354
)
355355

tests/handlers/test_appservice.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,18 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
422422
"exclusive_as_user", "password", self.exclusive_as_user_device_id
423423
)
424424

425+
self.exclusive_as_user_2_device_id = "exclusive_as_device_2"
426+
self.exclusive_as_user_2 = self.register_user("exclusive_as_user_2", "password")
427+
self.exclusive_as_user_2_token = self.login(
428+
"exclusive_as_user_2", "password", self.exclusive_as_user_2_device_id
429+
)
430+
431+
self.exclusive_as_user_3_device_id = "exclusive_as_device_3"
432+
self.exclusive_as_user_3 = self.register_user("exclusive_as_user_3", "password")
433+
self.exclusive_as_user_3_token = self.login(
434+
"exclusive_as_user_3", "password", self.exclusive_as_user_3_device_id
435+
)
436+
425437
def _notify_interested_services(self) -> None:
426438
# This is normally set in `notify_interested_services` but we need to call the
427439
# internal async version so the reactor gets pushed to completion.
@@ -849,6 +861,119 @@ def test_application_services_receive_bursts_of_to_device(self) -> None:
849861
for count in service_id_to_message_count.values():
850862
self.assertEqual(count, number_of_messages)
851863

864+
@unittest.override_config(
865+
{"experimental_features": {"msc2409_to_device_messages_enabled": True}}
866+
)
867+
def test_application_services_receive_local_to_device_for_many_users(self) -> None:
868+
"""
869+
Test that when a user sends a to-device message to many users
870+
in an application service's user namespace, the
871+
application service will receive all of them.
872+
"""
873+
interested_appservice = self._register_application_service(
874+
namespaces={
875+
ApplicationService.NS_USERS: [
876+
{
877+
"regex": "@exclusive_as_user:.+",
878+
"exclusive": True,
879+
},
880+
{
881+
"regex": "@exclusive_as_user_2:.+",
882+
"exclusive": True,
883+
},
884+
{
885+
"regex": "@exclusive_as_user_3:.+",
886+
"exclusive": True,
887+
},
888+
],
889+
},
890+
)
891+
892+
# Have local_user send a to-device message to exclusive_as_users
893+
message_content = {"some_key": "some really interesting value"}
894+
chan = self.make_request(
895+
"PUT",
896+
"/_matrix/client/r0/sendToDevice/m.room_key_request/3",
897+
content={
898+
"messages": {
899+
self.exclusive_as_user: {
900+
self.exclusive_as_user_device_id: message_content
901+
},
902+
self.exclusive_as_user_2: {
903+
self.exclusive_as_user_2_device_id: message_content
904+
},
905+
self.exclusive_as_user_3: {
906+
self.exclusive_as_user_3_device_id: message_content
907+
},
908+
}
909+
},
910+
access_token=self.local_user_token,
911+
)
912+
self.assertEqual(chan.code, 200, chan.result)
913+
914+
# Have exclusive_as_user send a to-device message to local_user
915+
for user_token in [
916+
self.exclusive_as_user_token,
917+
self.exclusive_as_user_2_token,
918+
self.exclusive_as_user_3_token,
919+
]:
920+
chan = self.make_request(
921+
"PUT",
922+
"/_matrix/client/r0/sendToDevice/m.room_key_request/4",
923+
content={
924+
"messages": {
925+
self.local_user: {self.local_user_device_id: message_content}
926+
}
927+
},
928+
access_token=user_token,
929+
)
930+
self.assertEqual(chan.code, 200, chan.result)
931+
932+
# Check if our application service - that is interested in exclusive_as_user - received
933+
# the to-device message as part of an AS transaction.
934+
# Only the local_user -> exclusive_as_user to-device message should have been forwarded to the AS.
935+
#
936+
# The uninterested application service should not have been notified at all.
937+
self.send_mock.assert_called_once()
938+
(
939+
service,
940+
_events,
941+
_ephemeral,
942+
to_device_messages,
943+
_otks,
944+
_fbks,
945+
_device_list_summary,
946+
) = self.send_mock.call_args[0]
947+
948+
# Assert that this was the same to-device message that local_user sent
949+
self.assertEqual(service, interested_appservice)
950+
951+
# Assert expected number of messages
952+
self.assertEqual(len(to_device_messages), 3)
953+
954+
for device_msg in to_device_messages:
955+
self.assertEqual(device_msg["type"], "m.room_key_request")
956+
self.assertEqual(device_msg["sender"], self.local_user)
957+
self.assertEqual(device_msg["content"], message_content)
958+
959+
self.assertEqual(to_device_messages[0]["to_user_id"], self.exclusive_as_user)
960+
self.assertEqual(
961+
to_device_messages[0]["to_device_id"],
962+
self.exclusive_as_user_device_id,
963+
)
964+
965+
self.assertEqual(to_device_messages[1]["to_user_id"], self.exclusive_as_user_2)
966+
self.assertEqual(
967+
to_device_messages[1]["to_device_id"],
968+
self.exclusive_as_user_2_device_id,
969+
)
970+
971+
self.assertEqual(to_device_messages[2]["to_user_id"], self.exclusive_as_user_3)
972+
self.assertEqual(
973+
to_device_messages[2]["to_device_id"],
974+
self.exclusive_as_user_3_device_id,
975+
)
976+
852977
def _register_application_service(
853978
self,
854979
namespaces: Optional[Dict[str, Iterable[Dict]]] = None,

0 commit comments

Comments
 (0)