mirror of
https://github.com/element-hq/synapse
synced 2024-10-02 08:02:41 +00:00
Add better support for leave/ban
This commit is contained in:
parent
57ba0336bd
commit
c81f3006a5
4 changed files with 399 additions and 23 deletions
|
@ -775,11 +775,17 @@ class SlidingSyncHandler:
|
|||
)
|
||||
)
|
||||
|
||||
timeline_events, new_room_key = await self.store.paginate_room_events(
|
||||
room_id=room_id,
|
||||
# We're going to paginate backwards from the `to_token`
|
||||
from_key=to_token.room_key,
|
||||
to_key=(
|
||||
from_bound = to_token.room_key
|
||||
# People shouldn't see past their leave/ban event
|
||||
if rooms_for_user_membership_at_to_token.membership in (
|
||||
Membership.LEAVE,
|
||||
Membership.BAN,
|
||||
):
|
||||
from_bound = (
|
||||
rooms_for_user_membership_at_to_token.event_pos.to_room_stream_token()
|
||||
)
|
||||
|
||||
# Determine whether we should limit the timeline to the token range.
|
||||
#
|
||||
# We should return historical messages (before token range) in the
|
||||
|
@ -789,10 +795,16 @@ class SlidingSyncHandler:
|
|||
# - When users `newly_joined`
|
||||
# - TODO: For an incremental sync where we haven't sent it down this
|
||||
# connection before
|
||||
to_bound = (
|
||||
from_token.room_key
|
||||
if from_token is not None and not newly_joined
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
timeline_events, new_room_key = await self.store.paginate_room_events(
|
||||
room_id=room_id,
|
||||
from_key=from_bound,
|
||||
to_key=to_bound,
|
||||
direction=Direction.BACKWARDS,
|
||||
# We add one so we can determine if there are enough events to saturate
|
||||
# the limit or not (see `limited`)
|
||||
|
@ -867,10 +879,10 @@ class SlidingSyncHandler:
|
|||
# Figure out any stripped state events for invite/knocks. This allows the
|
||||
# potential joiner to identify the room.
|
||||
stripped_state: List[JsonDict] = []
|
||||
if rooms_for_user_membership_at_to_token.membership in {
|
||||
if rooms_for_user_membership_at_to_token.membership in (
|
||||
Membership.INVITE,
|
||||
Membership.KNOCK,
|
||||
}:
|
||||
):
|
||||
invite_or_knock_event = await self.store.get_event(
|
||||
rooms_for_user_membership_at_to_token.event_id
|
||||
)
|
||||
|
|
|
@ -1551,6 +1551,9 @@ class StreamWorkerStore(EventsWorkerStore, SQLBaseStore):
|
|||
) -> Tuple[List[EventBase], RoomStreamToken]:
|
||||
"""Returns list of events before or after a given token.
|
||||
|
||||
When Direction.FORWARDS: from_key < x <= to_key
|
||||
When Direction.BACKWARDS: from_key >= x > to_key
|
||||
|
||||
Args:
|
||||
room_id
|
||||
from_key: The token used to stream from
|
||||
|
@ -1567,6 +1570,23 @@ class StreamWorkerStore(EventsWorkerStore, SQLBaseStore):
|
|||
and `to_key`).
|
||||
"""
|
||||
|
||||
# We can bail early if we're looking forwards, and our `to_key` is already
|
||||
# before our `from_key`.
|
||||
if (
|
||||
direction == Direction.FORWARDS
|
||||
and to_key is not None
|
||||
and to_key.is_before_or_eq(from_key)
|
||||
):
|
||||
return [], from_key
|
||||
# Or vice-versa, if we're looking backwards and our `from_key` is already before
|
||||
# our `to_key`.
|
||||
elif (
|
||||
direction == Direction.BACKWARDS
|
||||
and to_key is not None
|
||||
and from_key.is_before_or_eq(to_key)
|
||||
):
|
||||
return [], from_key
|
||||
|
||||
rows, token = await self.db_pool.runInteraction(
|
||||
"paginate_room_events",
|
||||
self._paginate_room_events_txn,
|
||||
|
|
|
@ -31,6 +31,7 @@ from synapse.api.constants import (
|
|||
AccountDataTypes,
|
||||
EventContentFields,
|
||||
EventTypes,
|
||||
HistoryVisibility,
|
||||
ReceiptTypes,
|
||||
RelationTypes,
|
||||
)
|
||||
|
@ -1831,10 +1832,11 @@ class SlidingSyncTestCase(unittest.HomeserverTestCase):
|
|||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
|
||||
def test_rooms_invite_sync(self) -> None:
|
||||
def test_rooms_invite_shared_history_initial_sync(self) -> None:
|
||||
"""
|
||||
Test that `rooms` we are invited to have some stripped `invite_state` and that
|
||||
we can't see any timeline events because we haven't joined the room yet.
|
||||
we can't see any timeline events because the history visiblity is `shared` and
|
||||
we haven't joined the room yet.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
|
@ -1844,6 +1846,16 @@ class SlidingSyncTestCase(unittest.HomeserverTestCase):
|
|||
user2 = UserID.from_string(user2_id)
|
||||
|
||||
room_id1 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
# Ensure we're testing with a room with `shared` history visibility which means
|
||||
# history visible until you actually join the room.
|
||||
history_visibility_response = self.helper.get_state(
|
||||
room_id1, EventTypes.RoomHistoryVisibility, tok=user2_tok
|
||||
)
|
||||
self.assertEqual(
|
||||
history_visibility_response.get("history_visibility"),
|
||||
HistoryVisibility.SHARED,
|
||||
)
|
||||
|
||||
self.helper.send(room_id1, "activity before1", tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before2", tok=user2_tok)
|
||||
self.helper.invite(room_id1, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
@ -1868,12 +1880,21 @@ class SlidingSyncTestCase(unittest.HomeserverTestCase):
|
|||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# Should not see anything (except maybe the invite event) because we haven't
|
||||
# joined yet (`filter_events_for_client(...)` is doing the work here)
|
||||
# joined yet (history visibility is `shared`) (`filter_events_for_client(...)`
|
||||
# is doing the work here)
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
[],
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
)
|
||||
# No "live" events in a initial sync (no `from_token` to define the "live"
|
||||
# range) and no events returned in the timeline anyway so nothing could be
|
||||
# "live".
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["num_live"],
|
||||
0,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# Even though we don't get any timeline events because they are filtered out,
|
||||
# there is still more to paginate
|
||||
self.assertEqual(
|
||||
|
@ -1914,3 +1935,326 @@ class SlidingSyncTestCase(unittest.HomeserverTestCase):
|
|||
channel.json_body["rooms"][room_id1]["invite_state"],
|
||||
)
|
||||
|
||||
|
||||
def test_rooms_invite_world_readable_history_initial_sync(self) -> None:
|
||||
"""
|
||||
Test that `rooms` we are invited to have some stripped `invite_state` and that
|
||||
we can't see any timeline events because the history visiblity is `shared` and
|
||||
we haven't joined the room yet.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user1 = UserID.from_string(user1_id)
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
user2 = UserID.from_string(user2_id)
|
||||
|
||||
room_id1 = self.helper.create_room_as(user2_id, tok=user2_tok,
|
||||
extra_content={
|
||||
"preset": "public_chat",
|
||||
"initial_state": [
|
||||
{
|
||||
"content": {"history_visibility": HistoryVisibility.WORLD_READABLE},
|
||||
"state_key": "",
|
||||
"type": EventTypes.RoomHistoryVisibility,
|
||||
}
|
||||
],
|
||||
},)
|
||||
# Ensure we're testing with a room with `world_readable` history visibility
|
||||
# which means events are visible to anyone even without membership.
|
||||
history_visibility_response = self.helper.get_state(
|
||||
room_id1, EventTypes.RoomHistoryVisibility, tok=user2_tok
|
||||
)
|
||||
self.assertEqual(
|
||||
history_visibility_response.get("history_visibility"),
|
||||
HistoryVisibility.WORLD_READABLE,
|
||||
)
|
||||
|
||||
self.helper.send(room_id1, "activity before1", tok=user2_tok)
|
||||
event_response2 = self.helper.send(room_id1, "activity before2", tok=user2_tok)
|
||||
use1_invite_response = self.helper.invite(room_id1, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
event_response3 = self.helper.send(room_id1, "activity after3", tok=user2_tok)
|
||||
event_response4 = self.helper.send(room_id1, "activity after4", tok=user2_tok)
|
||||
|
||||
# Make the Sliding Sync request
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
self.sync_endpoint,
|
||||
{
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 1]],
|
||||
"required_state": [],
|
||||
# Large enough to see the latest events and before the invite
|
||||
"timeline_limit": 4,
|
||||
}
|
||||
}
|
||||
},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# Should see the last 4 events in the room
|
||||
self.assertEqual(
|
||||
[
|
||||
event["event_id"]
|
||||
for event in channel.json_body["rooms"][room_id1]["timeline"]
|
||||
],
|
||||
[
|
||||
event_response2["event_id"],
|
||||
use1_invite_response["event_id"],
|
||||
event_response3["event_id"],
|
||||
event_response4["event_id"],
|
||||
],
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
)
|
||||
# No "live" events in a initial sync (no `from_token` to define the "live"
|
||||
# range)
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["num_live"],
|
||||
0,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# There is still more to paginate
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["limited"],
|
||||
True,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# We should have some `stripped_state` so the potential joiner can identify the
|
||||
# room (we don't care about the order).
|
||||
self.assertCountEqual(
|
||||
channel.json_body["rooms"][room_id1]["invite_state"],
|
||||
[
|
||||
{
|
||||
"content": {"creator": user2_id, "room_version": "10"},
|
||||
"sender": user2_id,
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
},
|
||||
{
|
||||
"content": {"join_rule": "public"},
|
||||
"sender": user2_id,
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules",
|
||||
},
|
||||
{
|
||||
"content": {"displayname": user2.localpart, "membership": "join"},
|
||||
"sender": user2_id,
|
||||
"state_key": user2_id,
|
||||
"type": "m.room.member",
|
||||
},
|
||||
{
|
||||
"content": {"displayname": user1.localpart, "membership": "invite"},
|
||||
"sender": user2_id,
|
||||
"state_key": user1_id,
|
||||
"type": "m.room.member",
|
||||
},
|
||||
],
|
||||
channel.json_body["rooms"][room_id1]["invite_state"],
|
||||
)
|
||||
|
||||
def test_rooms_ban_initial_sync(self) -> None:
|
||||
"""
|
||||
Test that `rooms` we are banned from in an intial sync only allows us to see
|
||||
timeline events up to the ban event.
|
||||
"""
|
||||
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")
|
||||
|
||||
room_id1 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before1", tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before2", tok=user2_tok)
|
||||
self.helper.join(room_id1, user1_id, tok=user1_tok)
|
||||
|
||||
event_response3 = self.helper.send(room_id1, "activity after3", tok=user2_tok)
|
||||
event_response4 = self.helper.send(room_id1, "activity after4", tok=user2_tok)
|
||||
user1_ban_response = self.helper.ban(
|
||||
room_id1, src=user2_id, targ=user1_id, tok=user2_tok
|
||||
)
|
||||
|
||||
self.helper.send(room_id1, "activity after5", tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity after6", tok=user2_tok)
|
||||
|
||||
# Make the Sliding Sync request
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
self.sync_endpoint,
|
||||
{
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 1]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 3,
|
||||
}
|
||||
}
|
||||
},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# We should see events before the ban but not after
|
||||
self.assertEqual(
|
||||
[
|
||||
event["event_id"]
|
||||
for event in channel.json_body["rooms"][room_id1]["timeline"]
|
||||
],
|
||||
[
|
||||
event_response3["event_id"],
|
||||
event_response4["event_id"],
|
||||
user1_ban_response["event_id"],
|
||||
],
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
)
|
||||
# No "live" events in a initial sync (no `from_token` to define the "live"
|
||||
# range)
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["num_live"],
|
||||
0,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# There are more events to paginate to
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["limited"],
|
||||
True,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
|
||||
def test_rooms_ban_incremental_sync1(self) -> None:
|
||||
"""
|
||||
Test that `rooms` we are banned from during the next incremental sync only
|
||||
allows us to see timeline events up to the ban event.
|
||||
"""
|
||||
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")
|
||||
|
||||
room_id1 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before1", tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before2", tok=user2_tok)
|
||||
self.helper.join(room_id1, user1_id, tok=user1_tok)
|
||||
|
||||
from_token = self.event_sources.get_current_token()
|
||||
|
||||
event_response3 = self.helper.send(room_id1, "activity after3", tok=user2_tok)
|
||||
event_response4 = self.helper.send(room_id1, "activity after4", tok=user2_tok)
|
||||
# The ban is within the token range (between the `from_token` and the sliding
|
||||
# sync request)
|
||||
user1_ban_response = self.helper.ban(
|
||||
room_id1, src=user2_id, targ=user1_id, tok=user2_tok
|
||||
)
|
||||
|
||||
self.helper.send(room_id1, "activity after5", tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity after6", tok=user2_tok)
|
||||
|
||||
# Make the Sliding Sync request
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
self.sync_endpoint
|
||||
+ f"?pos={self.get_success(
|
||||
from_token.to_string(self.store)
|
||||
)}",
|
||||
{
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 1]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 4,
|
||||
}
|
||||
}
|
||||
},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# We should see events before the ban but not after
|
||||
self.assertEqual(
|
||||
[
|
||||
event["event_id"]
|
||||
for event in channel.json_body["rooms"][room_id1]["timeline"]
|
||||
],
|
||||
[
|
||||
event_response3["event_id"],
|
||||
event_response4["event_id"],
|
||||
user1_ban_response["event_id"],
|
||||
],
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
)
|
||||
# All live events in the incremental sync
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["num_live"],
|
||||
3,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# There aren't anymore events to paginate to in this range
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["limited"],
|
||||
False,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
|
||||
def test_rooms_ban_incremental_sync2(self) -> None:
|
||||
"""
|
||||
Test that `rooms` we are banned from before the incremental sync doesn't return
|
||||
any events in the timeline.
|
||||
"""
|
||||
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")
|
||||
|
||||
room_id1 = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.send(room_id1, "activity before1", tok=user2_tok)
|
||||
self.helper.join(room_id1, user1_id, tok=user1_tok)
|
||||
|
||||
self.helper.send(room_id1, "activity after2", tok=user2_tok)
|
||||
# The ban is before we get our `from_token`
|
||||
self.helper.ban(room_id1, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
self.helper.send(room_id1, "activity after3", tok=user2_tok)
|
||||
|
||||
from_token = self.event_sources.get_current_token()
|
||||
|
||||
self.helper.send(room_id1, "activity after4", tok=user2_tok)
|
||||
|
||||
# Make the Sliding Sync request
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
self.sync_endpoint
|
||||
+ f"?pos={self.get_success(
|
||||
from_token.to_string(self.store)
|
||||
)}",
|
||||
{
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 1]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 4,
|
||||
}
|
||||
}
|
||||
},
|
||||
access_token=user1_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# Nothing to see for this banned user in the room in the token range
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
[],
|
||||
channel.json_body["rooms"][room_id1]["timeline"],
|
||||
)
|
||||
# No events returned in the timeline so nothing is "live"
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["num_live"],
|
||||
0,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
# There aren't anymore events to paginate to in this range
|
||||
self.assertEqual(
|
||||
channel.json_body["rooms"][room_id1]["limited"],
|
||||
False,
|
||||
channel.json_body["rooms"][room_id1],
|
||||
)
|
||||
|
|
|
@ -261,9 +261,9 @@ class RestHelper:
|
|||
targ: str,
|
||||
expect_code: int = HTTPStatus.OK,
|
||||
tok: Optional[str] = None,
|
||||
) -> None:
|
||||
) -> JsonDict:
|
||||
"""A convenience helper: `change_membership` with `membership` preset to "ban"."""
|
||||
self.change_membership(
|
||||
return self.change_membership(
|
||||
room=room,
|
||||
src=src,
|
||||
targ=targ,
|
||||
|
|
Loading…
Reference in a new issue