Sliding sync: Ignore invites from ignored users (#17729)
`m.ignored_user_list` in account data
This commit is contained in:
parent
61b7c31772
commit
af998e6c66
|
@ -0,0 +1 @@
|
|||
Ignore invites from ignored users in Sliding Sync.
|
|
@ -224,15 +224,31 @@ class SlidingSyncRoomLists:
|
|||
user_id
|
||||
)
|
||||
|
||||
# Remove invites from ignored users
|
||||
ignored_users = await self.store.ignored_users(user_id)
|
||||
if ignored_users:
|
||||
# TODO: It would be nice to avoid these copies
|
||||
room_membership_for_user_map = dict(room_membership_for_user_map)
|
||||
# Make a copy so we don't run into an error: `dictionary changed size during
|
||||
# iteration`, when we remove items
|
||||
for room_id in list(room_membership_for_user_map.keys()):
|
||||
room_for_user_sliding_sync = room_membership_for_user_map[room_id]
|
||||
if (
|
||||
room_for_user_sliding_sync.membership == Membership.INVITE
|
||||
and room_for_user_sliding_sync.sender in ignored_users
|
||||
):
|
||||
room_membership_for_user_map.pop(room_id, None)
|
||||
|
||||
changes = await self._get_rewind_changes_to_current_membership_to_token(
|
||||
sync_config.user, room_membership_for_user_map, to_token=to_token
|
||||
)
|
||||
if changes:
|
||||
# TODO: It would be nice to avoid these copies
|
||||
room_membership_for_user_map = dict(room_membership_for_user_map)
|
||||
for room_id, change in changes.items():
|
||||
if change is None:
|
||||
# Remove rooms that the user joined after the `to_token`
|
||||
room_membership_for_user_map.pop(room_id)
|
||||
room_membership_for_user_map.pop(room_id, None)
|
||||
continue
|
||||
|
||||
existing_room = room_membership_for_user_map.get(room_id)
|
||||
|
@ -926,6 +942,18 @@ class SlidingSyncRoomLists:
|
|||
excluded_rooms=self.rooms_to_exclude_globally,
|
||||
)
|
||||
|
||||
# Remove invites from ignored users
|
||||
ignored_users = await self.store.ignored_users(user_id)
|
||||
if ignored_users:
|
||||
room_for_user_list = [
|
||||
room_for_user
|
||||
for room_for_user in room_for_user_list
|
||||
if not (
|
||||
room_for_user.membership == Membership.INVITE
|
||||
and room_for_user.sender in ignored_users
|
||||
)
|
||||
]
|
||||
|
||||
# If the user has never joined any rooms before, we can just return an empty list
|
||||
if not room_for_user_list:
|
||||
return {}, set(), set()
|
||||
|
|
|
@ -29,7 +29,7 @@ from synapse.api.constants import (
|
|||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.events import EventBase, StrippedStateEvent, make_event_from_dict
|
||||
from synapse.events.snapshot import EventContext
|
||||
from synapse.rest.client import devices, login, receipts, room, sync
|
||||
from synapse.rest.client import account_data, devices, login, receipts, room, sync
|
||||
from synapse.server import HomeServer
|
||||
from synapse.types import (
|
||||
JsonDict,
|
||||
|
@ -413,6 +413,7 @@ class SlidingSyncTestCase(SlidingSyncBase):
|
|||
sync.register_servlets,
|
||||
devices.register_servlets,
|
||||
receipts.register_servlets,
|
||||
account_data.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
|
@ -670,6 +671,116 @@ class SlidingSyncTestCase(SlidingSyncBase):
|
|||
exact=True,
|
||||
)
|
||||
|
||||
def test_ignored_user_invites_initial_sync(self) -> None:
|
||||
"""
|
||||
Make sure we ignore invites if they are from one of the `m.ignored_user_list` on
|
||||
initial sync.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
|
||||
# Create a room that user1 is already in
|
||||
room_id1 = self.helper.create_room_as(user1_id, tok=user1_tok)
|
||||
|
||||
# Create a room that user2 is already in
|
||||
room_id2 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
|
||||
# User1 is invited to room_id2
|
||||
self.helper.invite(room_id2, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
# Sync once before we ignore to make sure the rooms can show up
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, _ = self.do_sync(sync_body, tok=user1_tok)
|
||||
# room_id2 shows up because we haven't ignored the user yet
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
{room_id1, room_id2},
|
||||
exact=True,
|
||||
)
|
||||
|
||||
# User1 ignores user2
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/_matrix/client/v3/user/{user1_id}/account_data/{AccountDataTypes.IGNORED_USER_LIST}",
|
||||
content={"ignored_users": {user2_id: {}}},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
# Sync again (initial sync)
|
||||
response_body, _ = self.do_sync(sync_body, tok=user1_tok)
|
||||
# The invite for room_id2 should no longer show up because user2 is ignored
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
{room_id1},
|
||||
exact=True,
|
||||
)
|
||||
|
||||
def test_ignored_user_invites_incremental_sync(self) -> None:
|
||||
"""
|
||||
Make sure we ignore invites if they are from one of the `m.ignored_user_list` on
|
||||
incremental sync.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
|
||||
# Create a room that user1 is already in
|
||||
room_id1 = self.helper.create_room_as(user1_id, tok=user1_tok)
|
||||
|
||||
# Create a room that user2 is already in
|
||||
room_id2 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
|
||||
# User1 ignores user2
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/_matrix/client/v3/user/{user1_id}/account_data/{AccountDataTypes.IGNORED_USER_LIST}",
|
||||
content={"ignored_users": {user2_id: {}}},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
# Initial sync
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, from_token = self.do_sync(sync_body, tok=user1_tok)
|
||||
# User1 only has membership in room_id1 at this point
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
{room_id1},
|
||||
exact=True,
|
||||
)
|
||||
|
||||
# User1 is invited to room_id2 after the initial sync
|
||||
self.helper.invite(room_id2, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
# Sync again (incremental sync)
|
||||
response_body, _ = self.do_sync(sync_body, since=from_token, tok=user1_tok)
|
||||
# The invite for room_id2 doesn't show up because user2 is ignored
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
{room_id1},
|
||||
exact=True,
|
||||
)
|
||||
|
||||
def test_sort_list(self) -> None:
|
||||
"""
|
||||
Test that the `lists` are sorted by `stream_ordering`
|
||||
|
|
Loading…
Reference in New Issue