mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-26 19:47:05 +03:00
Fill in for remote invites (out of band, outlier membership)
This commit is contained in:
parent
f069659343
commit
53232e6df5
3 changed files with 145 additions and 4 deletions
|
@ -42,11 +42,17 @@ import attr
|
|||
from prometheus_client import Counter
|
||||
|
||||
import synapse.metrics
|
||||
from synapse.api.constants import EventContentFields, EventTypes, RelationTypes
|
||||
from synapse.api.constants import (
|
||||
EventContentFields,
|
||||
EventTypes,
|
||||
Membership,
|
||||
RelationTypes,
|
||||
)
|
||||
from synapse.api.errors import PartialStateConflictError
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.events import EventBase, relation_from_event
|
||||
from synapse.events import EventBase, StrippedStateEvent, relation_from_event
|
||||
from synapse.events.snapshot import EventContext
|
||||
from synapse.events.utils import parse_stripped_state_event
|
||||
from synapse.logging.opentracing import trace
|
||||
from synapse.storage._base import db_to_json, make_in_list_sql_clause
|
||||
from synapse.storage.database import (
|
||||
|
@ -1312,11 +1318,12 @@ class PersistEventsStore:
|
|||
txn.execute_batch(
|
||||
f"""
|
||||
INSERT INTO sliding_sync_membership_snapshots
|
||||
(room_id, user_id, membership_event_id, membership, event_stream_ordering, {", ".join(insert_keys)})
|
||||
(room_id, user_id, membership_event_id, membership, event_stream_ordering, has_known_state, {", ".join(insert_keys)})
|
||||
VALUES (
|
||||
?, ?, ?,
|
||||
(SELECT membership FROM room_memberships WHERE event_id = ?),
|
||||
(SELECT stream_ordering FROM events WHERE event_id = ?),
|
||||
?,
|
||||
{", ".join("?" for _ in insert_values)}
|
||||
)
|
||||
ON CONFLICT (room_id, user_id)
|
||||
|
@ -1333,6 +1340,7 @@ 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()
|
||||
|
@ -2304,6 +2312,7 @@ class PersistEventsStore:
|
|||
)
|
||||
|
||||
for event in events:
|
||||
# Sanity check that we're working with persisted events
|
||||
assert event.internal_metadata.stream_ordering is not None
|
||||
|
||||
# We update the local_current_membership table only if the event is
|
||||
|
@ -2318,6 +2327,16 @@ class PersistEventsStore:
|
|||
and event.internal_metadata.is_outlier()
|
||||
and event.internal_metadata.is_out_of_band_membership()
|
||||
):
|
||||
# The only sort of out-of-band-membership events we expect to see here
|
||||
# are remote invites/knocks and LEAVE events corresponding to
|
||||
# rejected/retracted invites and rescinded knocks.
|
||||
assert event.type == EventTypes.Member
|
||||
assert event.membership in (
|
||||
Membership.INVITE,
|
||||
Membership.KNOCK,
|
||||
Membership.LEAVE,
|
||||
)
|
||||
|
||||
self.db_pool.simple_upsert_txn(
|
||||
txn,
|
||||
table="local_current_membership",
|
||||
|
@ -2329,6 +2348,115 @@ class PersistEventsStore:
|
|||
},
|
||||
)
|
||||
|
||||
# Update the `sliding_sync_membership_snapshots` table
|
||||
#
|
||||
raw_stripped_state_events = None
|
||||
if event.membership == Membership.INVITE:
|
||||
invite_room_state = event.unsigned.get("invite_room_state")
|
||||
raw_stripped_state_events = invite_room_state
|
||||
elif event.membership == Membership.KNOCK:
|
||||
knock_room_state = event.unsigned.get("knock_room_state")
|
||||
raw_stripped_state_events = knock_room_state
|
||||
|
||||
insert_values = {
|
||||
"membership_event_id": event.event_id,
|
||||
"membership": event.membership,
|
||||
"event_stream_ordering": event.internal_metadata.stream_ordering,
|
||||
}
|
||||
if raw_stripped_state_events is not None:
|
||||
stripped_state_map: MutableStateMap[StrippedStateEvent] = {}
|
||||
if isinstance(raw_stripped_state_events, list):
|
||||
for raw_stripped_event in raw_stripped_state_events:
|
||||
stripped_state_event = parse_stripped_state_event(
|
||||
raw_stripped_event
|
||||
)
|
||||
if stripped_state_event is not None:
|
||||
stripped_state_map[
|
||||
(
|
||||
stripped_state_event.type,
|
||||
stripped_state_event.state_key,
|
||||
)
|
||||
] = stripped_state_event
|
||||
|
||||
# If there is some stripped state, we assume the remote server passed *all*
|
||||
# of the potential stripped state events for the room.
|
||||
create_stripped_event = stripped_state_map.get(
|
||||
(EventTypes.Create, "")
|
||||
)
|
||||
# Sanity check that we at-least have the create event
|
||||
if create_stripped_event is not None:
|
||||
# Find the room_type
|
||||
insert_values["room_type"] = (
|
||||
create_stripped_event.content.get(
|
||||
EventContentFields.ROOM_TYPE
|
||||
)
|
||||
if create_stripped_event is not None
|
||||
else None
|
||||
)
|
||||
|
||||
# Find whether the room is_encrypted
|
||||
encryption_stripped_event = stripped_state_map.get(
|
||||
(EventTypes.RoomEncryption, "")
|
||||
)
|
||||
encryption = (
|
||||
encryption_stripped_event.content.get(
|
||||
EventContentFields.ENCRYPTION_ALGORITHM
|
||||
)
|
||||
if encryption_stripped_event is not None
|
||||
else None
|
||||
)
|
||||
insert_values["is_encrypted"] = encryption is not None
|
||||
|
||||
# Find the room_name
|
||||
room_name_stripped_event = stripped_state_map.get(
|
||||
(EventTypes.Name, "")
|
||||
)
|
||||
insert_values["room_name"] = (
|
||||
room_name_stripped_event.content.get(
|
||||
EventContentFields.ROOM_NAME
|
||||
)
|
||||
if room_name_stripped_event is not None
|
||||
else None
|
||||
)
|
||||
|
||||
else:
|
||||
# No strip state provided
|
||||
insert_values["has_known_state"] = False
|
||||
insert_values["room_type"] = None
|
||||
insert_values["room_name"] = None
|
||||
insert_values["is_encrypted"] = False
|
||||
else:
|
||||
if event.membership == Membership.LEAVE:
|
||||
# Inherit the meta data from the remote invite/knock. When using
|
||||
# sliding sync filters, this will prevent the room from
|
||||
# disappearing/appearing just because you left the room.
|
||||
pass
|
||||
elif event.membership in (Membership.INVITE, Membership.KNOCK):
|
||||
# No strip state provided
|
||||
insert_values["has_known_state"] = False
|
||||
insert_values["room_type"] = None
|
||||
insert_values["room_name"] = None
|
||||
insert_values["is_encrypted"] = False
|
||||
else:
|
||||
# We don't know how to handle this type of membership yet
|
||||
#
|
||||
# FIXME: We should use `assert_never` here but for some reason
|
||||
# the exhaustive matching doesn't recognize the `Never` here.
|
||||
# assert_never(event.membership)
|
||||
raise AssertionError(
|
||||
f"Unexpected out-of-band membership {event.membership} ({event.event_id}) that we don't know how to handle yet"
|
||||
)
|
||||
|
||||
self.db_pool.simple_upsert_txn(
|
||||
txn,
|
||||
table="sliding_sync_membership_snapshots",
|
||||
keyvalues={
|
||||
"room_id": event.room_id,
|
||||
"user_id": event.state_key,
|
||||
},
|
||||
values=insert_values,
|
||||
)
|
||||
|
||||
def _handle_event_relations(
|
||||
self, txn: LoggingTransaction, event: EventBase
|
||||
) -> None:
|
||||
|
|
|
@ -46,7 +46,14 @@ CREATE TABLE IF NOT EXISTS sliding_sync_joined_rooms(
|
|||
-- to find all membership for a given user and shares the same semantics as
|
||||
-- `local_current_membership`. And we get to avoid some table maintenance; if we only
|
||||
-- stored non-joins, we would have to delete the row for the user when the user joins
|
||||
-- the room.
|
||||
-- the room. Stripped state doesn't include the `m.room.tombstone` event, so we just
|
||||
-- assume that the room doesn't have a tombstone.
|
||||
--
|
||||
-- For remote invite/knocks where the server is not participating in the room, we will
|
||||
-- use stripped state events to populate this table. We assume that if any stripped
|
||||
-- state is given, it will include all possible stripped state events types. For
|
||||
-- example, if stripped state is given but `m.room.encryption` isn't included, we will
|
||||
-- assume that the room is not encrypted.
|
||||
--
|
||||
-- We don't include `bump_stamp` here because we can just use the `stream_ordering` from
|
||||
-- the membership event itself as the `bump_stamp`.
|
||||
|
@ -57,6 +64,11 @@ CREATE TABLE IF NOT EXISTS sliding_sync_membership_snapshots(
|
|||
membership TEXT NOT NULL,
|
||||
-- `stream_ordering` of the `membership_event_id`
|
||||
event_stream_ordering BIGINT NOT NULL REFERENCES events(stream_ordering),
|
||||
-- For remote invites/knocks that don't include any stripped state, we want to be
|
||||
-- able to distinguish between a room with `None` as valid value for some state and
|
||||
-- room where the state is completely unknown. Basically, this should be True unless
|
||||
-- no stripped state was provided for a remote invite/knock (False).
|
||||
has_known_state BOOLEAN DEFAULT 0 NOT NULL,
|
||||
-- `m.room.create` -> `content.type` (according to the current state at the time of
|
||||
-- the membership)
|
||||
room_type TEXT,
|
||||
|
|
|
@ -1407,6 +1407,7 @@ class SlidingSyncPrePopulatedTablesTestCase(HomeserverTestCase):
|
|||
)
|
||||
|
||||
# TODO: Test remote invite
|
||||
# TODO: Test rejection of a remote invite
|
||||
|
||||
# TODO Test for non-join membership changing
|
||||
|
||||
|
|
Loading…
Reference in a new issue