mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-29 03:28:34 +00:00
Fix rejecting invite when no_longer_in_room (and other non-join transitions)
This commit is contained in:
@@ -1199,7 +1199,7 @@ class PersistEventsStore:
|
||||
|
||||
# We handle `sliding_sync_membership_snapshots` before `current_state_events` so
|
||||
# we can gather the current state before it might be deleted if we are
|
||||
# `no_longer_in_room`.
|
||||
# last ones in the room and now we are `no_longer_in_room`.
|
||||
#
|
||||
# We do this regardless of whether the server is `no_longer_in_room` or not
|
||||
# because we still want a row if a local user was just left/kicked or got banned
|
||||
@@ -1211,11 +1211,6 @@ class PersistEventsStore:
|
||||
membership_event_id_to_user_id_map[event_id] = state_key[1]
|
||||
|
||||
if len(membership_event_id_to_user_id_map) > 0:
|
||||
# Map of values to insert/update in the `sliding_sync_membership_snapshots` table
|
||||
sliding_sync_membership_snapshots_insert_map: Dict[
|
||||
str, Optional[Union[str, bool]]
|
||||
] = {}
|
||||
|
||||
relevant_state_set = {
|
||||
(EventTypes.Create, ""),
|
||||
(EventTypes.RoomEncryption, ""),
|
||||
@@ -1256,55 +1251,80 @@ class PersistEventsStore:
|
||||
if state_key in relevant_state_set:
|
||||
current_state_map[state_key] = event_id
|
||||
|
||||
# Fetch the raw event JSON from the database
|
||||
(
|
||||
event_id_in_list_clause,
|
||||
event_id_args,
|
||||
) = make_in_list_sql_clause(
|
||||
self.database_engine,
|
||||
"event_id",
|
||||
current_state_map.values(),
|
||||
)
|
||||
txn.execute(
|
||||
f"""
|
||||
SELECT event_id, type, state_key, json FROM event_json
|
||||
INNER JOIN events USING (event_id)
|
||||
WHERE {event_id_in_list_clause}
|
||||
""",
|
||||
event_id_args,
|
||||
)
|
||||
# Map of values to insert/update in the `sliding_sync_membership_snapshots` table
|
||||
sliding_sync_membership_snapshots_insert_map: Dict[
|
||||
str, Optional[Union[str, bool]]
|
||||
] = {}
|
||||
if current_state_map:
|
||||
# We have current state to work from
|
||||
sliding_sync_membership_snapshots_insert_map["has_known_state"] = (
|
||||
True
|
||||
)
|
||||
|
||||
# Parse the raw event JSON
|
||||
for row in txn:
|
||||
event_id, event_type, state_key, json = row
|
||||
event_json = db_to_json(json)
|
||||
# Fetch the raw event JSON from the database
|
||||
(
|
||||
event_id_in_list_clause,
|
||||
event_id_args,
|
||||
) = make_in_list_sql_clause(
|
||||
self.database_engine,
|
||||
"event_id",
|
||||
current_state_map.values(),
|
||||
)
|
||||
txn.execute(
|
||||
f"""
|
||||
SELECT event_id, type, state_key, json FROM event_json
|
||||
INNER JOIN events USING (event_id)
|
||||
WHERE {event_id_in_list_clause}
|
||||
""",
|
||||
event_id_args,
|
||||
)
|
||||
|
||||
if event_type == EventTypes.Create:
|
||||
room_type = event_json.get("content", {}).get(
|
||||
EventContentFields.ROOM_TYPE
|
||||
)
|
||||
sliding_sync_membership_snapshots_insert_map["room_type"] = (
|
||||
room_type
|
||||
)
|
||||
elif event_type == EventTypes.RoomEncryption:
|
||||
encryption_algorithm = event_json.get("content", {}).get(
|
||||
EventContentFields.ENCRYPTION_ALGORITHM
|
||||
)
|
||||
is_encrypted = encryption_algorithm is not None
|
||||
sliding_sync_membership_snapshots_insert_map["is_encrypted"] = (
|
||||
is_encrypted
|
||||
)
|
||||
elif event_type == EventTypes.Name:
|
||||
room_name = event_json.get("content", {}).get(
|
||||
EventContentFields.ROOM_NAME
|
||||
)
|
||||
sliding_sync_membership_snapshots_insert_map["room_name"] = (
|
||||
room_name
|
||||
)
|
||||
else:
|
||||
raise AssertionError(
|
||||
f"Unexpected event (we should not be fetching extra events): ({event_type}, {state_key})"
|
||||
)
|
||||
# Parse the raw event JSON
|
||||
for row in txn:
|
||||
event_id, event_type, state_key, json = row
|
||||
event_json = db_to_json(json)
|
||||
|
||||
if event_type == EventTypes.Create:
|
||||
room_type = event_json.get("content", {}).get(
|
||||
EventContentFields.ROOM_TYPE
|
||||
)
|
||||
sliding_sync_membership_snapshots_insert_map[
|
||||
"room_type"
|
||||
] = room_type
|
||||
elif event_type == EventTypes.RoomEncryption:
|
||||
encryption_algorithm = event_json.get("content", {}).get(
|
||||
EventContentFields.ENCRYPTION_ALGORITHM
|
||||
)
|
||||
is_encrypted = encryption_algorithm is not None
|
||||
sliding_sync_membership_snapshots_insert_map[
|
||||
"is_encrypted"
|
||||
] = is_encrypted
|
||||
elif event_type == EventTypes.Name:
|
||||
room_name = event_json.get("content", {}).get(
|
||||
EventContentFields.ROOM_NAME
|
||||
)
|
||||
sliding_sync_membership_snapshots_insert_map[
|
||||
"room_name"
|
||||
] = room_name
|
||||
else:
|
||||
raise AssertionError(
|
||||
f"Unexpected event (we should not be fetching extra events): ({event_type}, {state_key})"
|
||||
)
|
||||
else:
|
||||
# We don't have any `current_state_events` anymore (previously
|
||||
# cleared out because of `no_longer_in_room`). This can happen if
|
||||
# one user is joined and another is invited (some non-join
|
||||
# membership). If the joined user leaves, we are `no_longer_in_room`
|
||||
# and `current_state_events` is cleared out. When the invited user
|
||||
# rejects the invite (leaves the room), we will end up here.
|
||||
#
|
||||
# In these cases, we should inherit the meta data from the previous
|
||||
# snapshot. When using sliding sync filters, this will prevent the
|
||||
# room from disappearing/appearing just because you left the room.
|
||||
#
|
||||
# Ideally, we could additionally assert that we're only here for
|
||||
# valid non-join membership transitions.
|
||||
assert delta_state.no_longer_in_room
|
||||
|
||||
# Update the `sliding_sync_membership_snapshots` table
|
||||
#
|
||||
@@ -1315,20 +1335,20 @@ class PersistEventsStore:
|
||||
txn.execute_batch(
|
||||
f"""
|
||||
INSERT INTO sliding_sync_membership_snapshots
|
||||
(room_id, user_id, membership_event_id, membership, event_stream_ordering, has_known_state, {", ".join(insert_keys)})
|
||||
(room_id, user_id, membership_event_id, membership, event_stream_ordering
|
||||
{"," + (", ".join(insert_keys)) if insert_keys else ""})
|
||||
VALUES (
|
||||
?, ?, ?,
|
||||
(SELECT membership FROM room_memberships WHERE event_id = ?),
|
||||
(SELECT stream_ordering FROM events WHERE event_id = ?),
|
||||
?,
|
||||
{", ".join("?" for _ in insert_values)}
|
||||
(SELECT stream_ordering FROM events WHERE event_id = ?)
|
||||
{"," + (", ".join("?" for _ in insert_values)) if insert_values else ""}
|
||||
)
|
||||
ON CONFLICT (room_id, user_id)
|
||||
DO UPDATE SET
|
||||
membership_event_id = EXCLUDED.membership_event_id,
|
||||
membership = EXCLUDED.membership,
|
||||
event_stream_ordering = EXCLUDED.event_stream_ordering,
|
||||
{", ".join(f"{key} = EXCLUDED.{key}" for key in insert_keys)}
|
||||
event_stream_ordering = EXCLUDED.event_stream_ordering
|
||||
{"," + (", ".join(f"{key} = EXCLUDED.{key}" for key in insert_keys)) if insert_keys else ""}
|
||||
""",
|
||||
[
|
||||
[
|
||||
@@ -1337,7 +1357,6 @@ class PersistEventsStore:
|
||||
membership_event_id,
|
||||
membership_event_id,
|
||||
membership_event_id,
|
||||
True, # has_known_state
|
||||
]
|
||||
+ list(insert_values)
|
||||
for membership_event_id, user_id in membership_event_id_to_user_id_map.items()
|
||||
|
||||
@@ -1553,6 +1553,82 @@ class SlidingSyncPrePopulatedTablesTestCase(HomeserverTestCase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_non_join_reject_invite_empty_room(self) -> None:
|
||||
"""
|
||||
In a room where no one is joined (`no_longer_in_room`), test rejecting an invite.
|
||||
"""
|
||||
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)
|
||||
|
||||
# User1 is invited to the room
|
||||
self.helper.invite(room_id1, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
# User2 leaves the room
|
||||
user2_leave_response = self.helper.leave(room_id1, user2_id, tok=user2_tok)
|
||||
user2_leave_event_pos = self.get_success(
|
||||
self.store.get_position_for_event(user2_leave_response["event_id"])
|
||||
)
|
||||
|
||||
# User1 rejects the invite
|
||||
user1_leave_response = self.helper.leave(room_id1, user1_id, tok=user1_tok)
|
||||
user1_leave_event_pos = self.get_success(
|
||||
self.store.get_position_for_event(user1_leave_response["event_id"])
|
||||
)
|
||||
|
||||
# No one is joined to the room
|
||||
sliding_sync_joined_rooms_results = self._get_sliding_sync_joined_rooms()
|
||||
self.assertIncludes(
|
||||
set(sliding_sync_joined_rooms_results.keys()),
|
||||
set(),
|
||||
exact=True,
|
||||
)
|
||||
|
||||
sliding_sync_membership_snapshots_results = (
|
||||
self._get_sliding_sync_membership_snapshots()
|
||||
)
|
||||
self.assertIncludes(
|
||||
set(sliding_sync_membership_snapshots_results.keys()),
|
||||
{
|
||||
(room_id1, user1_id),
|
||||
(room_id1, user2_id),
|
||||
},
|
||||
exact=True,
|
||||
)
|
||||
# Holds the info according to the current state when the user left
|
||||
self.assertEqual(
|
||||
sliding_sync_membership_snapshots_results.get((room_id1, user1_id)),
|
||||
_SlidingSyncMembershipSnapshotResult(
|
||||
room_id=room_id1,
|
||||
user_id=user1_id,
|
||||
membership_event_id=user1_leave_response["event_id"],
|
||||
membership=Membership.LEAVE,
|
||||
event_stream_ordering=user1_leave_event_pos.stream,
|
||||
has_known_state=True,
|
||||
room_type=None,
|
||||
room_name=None,
|
||||
is_encrypted=False,
|
||||
),
|
||||
)
|
||||
# Holds the info according to the current state when the left
|
||||
self.assertEqual(
|
||||
sliding_sync_membership_snapshots_results.get((room_id1, user2_id)),
|
||||
_SlidingSyncMembershipSnapshotResult(
|
||||
room_id=room_id1,
|
||||
user_id=user2_id,
|
||||
membership_event_id=user2_leave_response["event_id"],
|
||||
membership=Membership.LEAVE,
|
||||
event_stream_ordering=user2_leave_event_pos.stream,
|
||||
has_known_state=True,
|
||||
room_type=None,
|
||||
room_name=None,
|
||||
is_encrypted=False,
|
||||
),
|
||||
)
|
||||
|
||||
def test_membership_changing(self) -> None:
|
||||
"""
|
||||
Test latest snapshot evolves when membership changes (`sliding_sync_membership_snapshots`).
|
||||
|
||||
Reference in New Issue
Block a user