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

Check appservice user interest against the local users instead of all users (get_users_in_room mis-use) #13958

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
99a623d
Check appservice user interest against the local users instead of all…
MadLittleMods Sep 29, 2022
806b255
Remove non-appservice usages split out to other PRs
MadLittleMods Sep 29, 2022
5f0f815
Add changelog
MadLittleMods Sep 29, 2022
a8be41b
Revert back to using our own `_matches_user_in_member_list` thing
MadLittleMods Sep 29, 2022
72985df
Rename variables to 'local' to be obvious our intention
MadLittleMods Sep 29, 2022
92b9da2
Fix tests
MadLittleMods Sep 30, 2022
5d3c6a3
Wrapping happens at 88 chars
MadLittleMods Sep 30, 2022
76435c7
Add actual homeserver tests for local/remote interesting to appservic…
MadLittleMods Sep 30, 2022
4451998
Clarify interested/control and lints
MadLittleMods Sep 30, 2022
1218f03
Revert mock
MadLittleMods Sep 30, 2022
7bd3803
Add test descriptions
MadLittleMods Sep 30, 2022
33f718c
Revert "Clarify interested/control and lints"
MadLittleMods Sep 30, 2022
cf8299b
Revert "Add test descriptions"
MadLittleMods Sep 30, 2022
3de90e6
Revert "Revert mock"
MadLittleMods Sep 30, 2022
ab33cd6
Revert "Add actual homeserver tests for local/remote interesting to a…
MadLittleMods Sep 30, 2022
3223512
Move tests over from #14000
MadLittleMods Oct 4, 2022
d913ceb
Merge branch 'develop' into madlittlemods/13942-appservice-get_users_…
MadLittleMods Oct 4, 2022
4f29e75
Merge branch 'develop' into madlittlemods/13942-appservice-get_users_…
MadLittleMods Oct 24, 2022
2665aa0
Update changelog
MadLittleMods Oct 24, 2022
39e2ead
Add specific test to make sure local interesting user events are pick…
MadLittleMods Oct 25, 2022
33a5b70
Update upgrade notes
MadLittleMods Oct 25, 2022
92400ff
move comma
anoadragon453 Oct 27, 2022
426ef5c
Merge branch 'develop' of github.com:matrix-org/synapse into madlittl…
anoadragon453 Oct 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add actual homeserver tests for local/remote interesting to appservic…
…e users

See #13958 (comment)
  • Loading branch information
MadLittleMods committed Sep 30, 2022
commit 76435c7915c3a8d8c5b7ac345573bced20343aaf
23 changes: 7 additions & 16 deletions synapse/appservice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,14 @@ async def _matches_user_in_member_list(
Returns:
True if this service would like to know about this room.
"""
# We can use `get_local_users_in_room(...)` here because an application
# service can only act on behalf of users of the server it's on.
#
# In the future, we can consider re-using
# `store.get_app_service_users_in_room` which is very similar to this
# function but has a slightly worse performance than this because we
# have an early escape-hatch if we find a single user that the
# appservice is interested in. The juice would be worth the squeeze if
# `store.get_app_service_users_in_room` was used in more places besides
# an experimental MSC. But for now we can avoid doing more work and
# barely using it later.
local_user_ids = await store.get_local_users_in_room(
# We need to get all users (local and remote) as an application service can be
# interested in anyone.
member_list = await store.get_users_in_room(
room_id, on_invalidate=cache_context.invalidate
)

# check joined member events
for user_id in local_user_ids:
for user_id in member_list:
if self.is_interested_in_user(user_id):
return True
return False
Expand All @@ -200,9 +191,9 @@ def is_interested_in_user(
"""
Returns whether the application is interested in a given user ID.

The appservice is considered to be interested in a user if either: the
user ID is in the appservice's user namespace, or if the user is the
appservice's configured sender_localpart.
The appservice is considered to be interested in a user if either: the user ID
is in the appservice's user namespace, or if the user is the appservice's
configured sender_localpart. The user can be local or remote.

Args:
user_id: The ID of the user to check.
Expand Down
8 changes: 4 additions & 4 deletions tests/appservice/test_appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def setUp(self):

self.store = Mock()
self.store.get_aliases_for_room = simple_async_mock([])
self.store.get_local_users_in_room = simple_async_mock([])
self.store.get_users_in_room = simple_async_mock([])

@defer.inlineCallbacks
def test_regex_user_id_prefix_match(self):
Expand Down Expand Up @@ -129,7 +129,7 @@ def test_regex_alias_match(self):
self.store.get_aliases_for_room = simple_async_mock(
["#irc_foobar:matrix.org", "#athing:matrix.org"]
)
self.store.get_local_users_in_room = simple_async_mock([])
self.store.get_users_in_room = simple_async_mock([])
self.assertTrue(
(
yield defer.ensureDeferred(
Expand Down Expand Up @@ -184,7 +184,7 @@ def test_regex_alias_no_match(self):
self.store.get_aliases_for_room = simple_async_mock(
["#xmpp_foobar:matrix.org", "#athing:matrix.org"]
)
self.store.get_local_users_in_room = simple_async_mock([])
self.store.get_users_in_room = simple_async_mock([])
self.assertFalse(
(
yield defer.ensureDeferred(
Expand All @@ -203,7 +203,7 @@ def test_regex_multiple_matches(self):
self.service.namespaces[ApplicationService.NS_USERS].append(_regex("@irc_.*"))
self.event.sender = "@irc_foobar:matrix.org"
self.store.get_aliases_for_room = simple_async_mock(["#irc_barfoo:matrix.org"])
self.store.get_local_users_in_room = simple_async_mock([])
self.store.get_users_in_room = simple_async_mock([])
self.assertTrue(
(
yield defer.ensureDeferred(
Expand Down
139 changes: 136 additions & 3 deletions tests/handlers/test_appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import synapse.rest.admin
import synapse.storage
from synapse.api.constants import EduTypes
from synapse.api.constants import EduTypes, EventTypes
from synapse.appservice import (
ApplicationService,
TransactionOneTimeKeyCounts,
Expand All @@ -36,7 +36,7 @@
from synapse.util.stringutils import random_string

from tests import unittest
from tests.test_utils import make_awaitable, simple_async_mock
from tests.test_utils import event_injection, make_awaitable, simple_async_mock
from tests.unittest import override_config
from tests.utils import MockClock

Expand Down Expand Up @@ -386,7 +386,8 @@ class ApplicationServicesHandlerSendEventsTestCase(unittest.HomeserverTestCase):
receipts.register_servlets,
]

def prepare(self, reactor, clock, hs):
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer):
self.hs = hs
# Mock the ApplicationServiceScheduler's _TransactionController's send method so that
# we can track any outgoing ephemeral events
self.send_mock = simple_async_mock()
Expand All @@ -412,6 +413,138 @@ def prepare(self, reactor, clock, hs):
"exclusive_as_user", "password", self.exclusive_as_user_device_id
)

def _notify_interested_services(self):
# This is normally set in `notify_interested_services` but we need to call the
# internal async version so the reactor gets pushed to completion.
self.hs.get_application_service_handler().current_max += 1
self.get_success(
self.hs.get_application_service_handler()._notify_interested_services(
RoomStreamToken(
None, self.hs.get_application_service_handler().current_max
)
)
)

def test_match_local_room_members(self):
# Register an application service that's interested in local and remote user
interested_appservice = self._register_application_service(
namespaces={
ApplicationService.NS_USERS: [
{
"regex": "@local_as_user:test",
"exclusive": True,
},
],
},
)

alice = self.register_user("alice", "pass")
alice_access_token = self.login("alice", "pass")
room_id = self.helper.create_room_as(room_creator=alice, tok=alice_access_token)

# Join the interesting user to the room
self.get_success(
event_injection.inject_member_event(
self.hs, room_id, "@local_as_user:test", "join"
)
)
# Kick the appservice into checking this membership event to get it out of the
# way
self._notify_interested_services()
# We don't care about the interesting user join event (this test is making sure
# the next thing works)
self.send_mock.reset_mock()

# Send a message from an uninteresting user
self.helper.send_event(
room_id,
type=EventTypes.Message,
content={
"msgtype": "m.text",
"body": "message from uninteresting user",
},
tok=alice_access_token,
)
# Kick the appservice into checking this new event
self._notify_interested_services()

self.send_mock.assert_called_once()
(
service,
events,
_ephemeral,
_to_device_messages,
_otks,
_fbks,
_device_list_summary,
) = self.send_mock.call_args[0]

# Even though the message came from an uninsteresting user, it should still
# notify us because the interesting user is joined to the room.
self.assertEqual(service, interested_appservice)
self.assertEqual(events[0]["type"], "m.room.message")
self.assertEqual(events[0]["sender"], alice)

def test_match_remote_room_members(self):
# Register an application service that's interested in a remote user
interested_appservice = self._register_application_service(
namespaces={
ApplicationService.NS_USERS: [
{
"regex": "@interesting_user:remote",
"exclusive": True,
},
],
},
)

alice = self.register_user("alice", "pass")
alice_access_token = self.login("alice", "pass")
room_id = self.helper.create_room_as(room_creator=alice, tok=alice_access_token)

# Join the interesting user to the room
self.get_success(
event_injection.inject_member_event(
self.hs, room_id, "@interesting_user:remote", "join"
)
)
# Kick the appservice into checking this membership event to get it out of the
# way
self._notify_interested_services()
# We don't care about the interesting user join event (this test is making sure
# the next thing works)
self.send_mock.reset_mock()

# Send a message from an uninteresting user
self.helper.send_event(
room_id,
type=EventTypes.Message,
content={
"msgtype": "m.text",
"body": "message from uninteresting user",
},
tok=alice_access_token,
)
# Kick the appservice into checking this new event
self._notify_interested_services()

self.send_mock.assert_called_once()
(
service,
events,
_ephemeral,
_to_device_messages,
_otks,
_fbks,
_device_list_summary,
) = self.send_mock.call_args[0]

# Even though the message came from an uninsteresting user, it should still
# notify us because the interesting user is joined to the room.
self.assertEqual(service, interested_appservice)
self.assertEqual(events[0]["type"], "m.room.message")
self.assertEqual(events[0]["sender"], alice)

def test_sending_read_receipt_batches_to_application_services(self):
"""Tests that a large batch of read receipts are sent correctly to
interested application services.
Expand Down