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

Commit

Permalink
Add rooms.room_version column (#6729)
Browse files Browse the repository at this point in the history
This is so that we don't have to rely on pulling it out from `current_state_events` table.
  • Loading branch information
erikjohnston authored Jan 27, 2020
1 parent d5275fc commit 8df862e
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 73 deletions.
1 change: 1 addition & 0 deletions changelog.d/6729.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Record room versions in the `rooms` table.
50 changes: 33 additions & 17 deletions synapse/federation/federation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import copy
import itertools
import logging
from typing import Dict, Iterable

from prometheus_client import Counter

Expand All @@ -29,6 +30,7 @@
FederationDeniedError,
HttpResponseException,
SynapseError,
UnsupportedRoomVersionError,
)
from synapse.api.room_versions import (
KNOWN_ROOM_VERSIONS,
Expand Down Expand Up @@ -385,6 +387,8 @@ def _try_destination_list(self, description, destinations, callback):
return res
except InvalidResponseError as e:
logger.warning("Failed to %s via %s: %s", description, destination, e)
except UnsupportedRoomVersionError:
raise
except HttpResponseException as e:
if not 500 <= e.code < 600:
raise e.to_synapse_error()
Expand All @@ -404,7 +408,13 @@ def _try_destination_list(self, description, destinations, callback):
raise SynapseError(502, "Failed to %s via any server" % (description,))

def make_membership_event(
self, destinations, room_id, user_id, membership, content, params
self,
destinations: Iterable[str],
room_id: str,
user_id: str,
membership: str,
content: dict,
params: Dict[str, str],
):
"""
Creates an m.room.member event, with context, without participating in the room.
Expand All @@ -417,21 +427,23 @@ def make_membership_event(
Note that this does not append any events to any graphs.
Args:
destinations (Iterable[str]): Candidate homeservers which are probably
destinations: Candidate homeservers which are probably
participating in the room.
room_id (str): The room in which the event will happen.
user_id (str): The user whose membership is being evented.
membership (str): The "membership" property of the event. Must be
one of "join" or "leave".
content (dict): Any additional data to put into the content field
of the event.
params (dict[str, str|Iterable[str]]): Query parameters to include in the
request.
room_id: The room in which the event will happen.
user_id: The user whose membership is being evented.
membership: The "membership" property of the event. Must be one of
"join" or "leave".
content: Any additional data to put into the content field of the
event.
params: Query parameters to include in the request.
Return:
Deferred[tuple[str, FrozenEvent, int]]: resolves to a tuple of
`(origin, event, event_format)` where origin is the remote
homeserver which generated the event, and event_format is one of
`synapse.api.room_versions.EventFormatVersions`.
Deferred[Tuple[str, FrozenEvent, RoomVersion]]: resolves to a tuple of
`(origin, event, room_version)` where origin is the remote
homeserver which generated the event, and room_version is the
version of the room.
Fails with a `UnsupportedRoomVersionError` if remote responds with
a room version we don't understand.
Fails with a ``SynapseError`` if the chosen remote server
returns a 300/400 code.
Expand All @@ -453,8 +465,12 @@ def send_request(destination):

# Note: If not supplied, the room version may be either v1 or v2,
# however either way the event format version will be v1.
room_version = ret.get("room_version", RoomVersions.V1.identifier)
event_format = room_version_to_event_format(room_version)
room_version_id = ret.get("room_version", RoomVersions.V1.identifier)
room_version = KNOWN_ROOM_VERSIONS.get(room_version_id)
if not room_version:
raise UnsupportedRoomVersionError()

event_format = room_version_to_event_format(room_version_id)

pdu_dict = ret.get("event", None)
if not isinstance(pdu_dict, dict):
Expand All @@ -478,7 +494,7 @@ def send_request(destination):
event_dict=pdu_dict,
)

return (destination, ev, event_format)
return (destination, ev, room_version)

return self._try_destination_list(
"make_" + membership, destinations, send_request
Expand Down
65 changes: 50 additions & 15 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@
StoreError,
SynapseError,
)
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion, RoomVersions
from synapse.crypto.event_signing import compute_event_signature
from synapse.event_auth import auth_types_for_event
from synapse.events import EventBase
from synapse.events import EventBase, room_version_to_event_format
from synapse.events.snapshot import EventContext
from synapse.events.validator import EventValidator
from synapse.logging.context import (
Expand Down Expand Up @@ -703,8 +703,20 @@ async def _process_received_pdu(

if not room:
try:
prev_state_ids = await context.get_prev_state_ids()
create_event = await self.store.get_event(
prev_state_ids[(EventTypes.Create, "")]
)

room_version_id = create_event.content.get(
"room_version", RoomVersions.V1.identifier
)

await self.store.store_room(
room_id=room_id, room_creator_user_id="", is_public=False
room_id=room_id,
room_creator_user_id="",
is_public=False,
room_version=KNOWN_ROOM_VERSIONS[room_version_id],
)
except StoreError:
logger.exception("Failed to store room.")
Expand Down Expand Up @@ -1186,7 +1198,7 @@ def do_invite_join(self, target_hosts, room_id, joinee, content):
"""
logger.debug("Joining %s to %s", joinee, room_id)

origin, event, event_format_version = yield self._make_and_verify_event(
origin, event, room_version = yield self._make_and_verify_event(
target_hosts,
room_id,
joinee,
Expand Down Expand Up @@ -1214,6 +1226,8 @@ def do_invite_join(self, target_hosts, room_id, joinee, content):
target_hosts.insert(0, origin)
except ValueError:
pass

event_format_version = room_version_to_event_format(room_version.identifier)
ret = yield self.federation_client.send_join(
target_hosts, event, event_format_version
)
Expand All @@ -1234,13 +1248,18 @@ def do_invite_join(self, target_hosts, room_id, joinee, content):

try:
yield self.store.store_room(
room_id=room_id, room_creator_user_id="", is_public=False
room_id=room_id,
room_creator_user_id="",
is_public=False,
room_version=room_version,
)
except Exception:
# FIXME
pass

yield self._persist_auth_tree(origin, auth_chain, state, event)
yield self._persist_auth_tree(
origin, auth_chain, state, event, room_version
)

# Check whether this room is the result of an upgrade of a room we already know
# about. If so, migrate over user information
Expand Down Expand Up @@ -1486,7 +1505,7 @@ def on_invite_request(self, origin, pdu):

@defer.inlineCallbacks
def do_remotely_reject_invite(self, target_hosts, room_id, user_id, content):
origin, event, event_format_version = yield self._make_and_verify_event(
origin, event, room_version = yield self._make_and_verify_event(
target_hosts, room_id, user_id, "leave", content=content
)
# Mark as outlier as we don't have any state for this event; we're not
Expand All @@ -1513,7 +1532,11 @@ def do_remotely_reject_invite(self, target_hosts, room_id, user_id, content):
def _make_and_verify_event(
self, target_hosts, room_id, user_id, membership, content={}, params=None
):
origin, event, format_ver = yield self.federation_client.make_membership_event(
(
origin,
event,
room_version,
) = yield self.federation_client.make_membership_event(
target_hosts, room_id, user_id, membership, content, params=params
)

Expand All @@ -1525,7 +1548,7 @@ def _make_and_verify_event(
assert event.user_id == user_id
assert event.state_key == user_id
assert event.room_id == room_id
return origin, event, format_ver
return origin, event, room_version

@defer.inlineCallbacks
@log_function
Expand Down Expand Up @@ -1810,7 +1833,14 @@ def prep(ev_info: _NewEventInfo):
)

@defer.inlineCallbacks
def _persist_auth_tree(self, origin, auth_events, state, event):
def _persist_auth_tree(
self,
origin: str,
auth_events: List[EventBase],
state: List[EventBase],
event: EventBase,
room_version: RoomVersion,
):
"""Checks the auth chain is valid (and passes auth checks) for the
state and event. Then persists the auth chain and state atomically.
Persists the event separately. Notifies about the persisted events
Expand All @@ -1819,10 +1849,12 @@ def _persist_auth_tree(self, origin, auth_events, state, event):
Will attempt to fetch missing auth events.
Args:
origin (str): Where the events came from
auth_events (list)
state (list)
event (Event)
origin: Where the events came from
auth_events
state
event
room_version: The room version we expect this room to have, and
will raise if it doesn't match the version in the create event.
Returns:
Deferred
Expand All @@ -1848,10 +1880,13 @@ def _persist_auth_tree(self, origin, auth_events, state, event):
# invalid, and it would fail auth checks anyway.
raise SynapseError(400, "No create event in state")

room_version = create_event.content.get(
room_version_id = create_event.content.get(
"room_version", RoomVersions.V1.identifier
)

if room_version.identifier != room_version_id:
raise SynapseError(400, "Room version mismatch")

missing_auth_events = set()
for e in itertools.chain(auth_events, state, [event]):
for e_id in e.auth_event_ids():
Expand Down
52 changes: 32 additions & 20 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
from synapse.http.endpoint import parse_and_validate_server_name
from synapse.storage.state import StateFilter
from synapse.types import (
Expand Down Expand Up @@ -100,13 +100,15 @@ def __init__(self, hs):
self.third_party_event_rules = hs.get_third_party_event_rules()

@defer.inlineCallbacks
def upgrade_room(self, requester, old_room_id, new_version):
def upgrade_room(
self, requester: Requester, old_room_id: str, new_version: RoomVersion
):
"""Replace a room with a new room with a different version
Args:
requester (synapse.types.Requester): the user requesting the upgrade
old_room_id (unicode): the id of the room to be replaced
new_version (unicode): the new room version to use
requester: the user requesting the upgrade
old_room_id: the id of the room to be replaced
new_version: the new room version to use
Returns:
Deferred[unicode]: the new room id
Expand Down Expand Up @@ -151,7 +153,7 @@ def _upgrade_room(self, requester, old_room_id, new_version):
if r is None:
raise NotFoundError("Unknown room id %s" % (old_room_id,))
new_room_id = yield self._generate_room_id(
creator_id=user_id, is_public=r["is_public"]
creator_id=user_id, is_public=r["is_public"], room_version=new_version,
)

logger.info("Creating new room %s to replace %s", new_room_id, old_room_id)
Expand Down Expand Up @@ -299,18 +301,22 @@ def _update_upgraded_room_pls(

@defer.inlineCallbacks
def clone_existing_room(
self, requester, old_room_id, new_room_id, new_room_version, tombstone_event_id
self,
requester: Requester,
old_room_id: str,
new_room_id: str,
new_room_version: RoomVersion,
tombstone_event_id: str,
):
"""Populate a new room based on an old room
Args:
requester (synapse.types.Requester): the user requesting the upgrade
old_room_id (unicode): the id of the room to be replaced
new_room_id (unicode): the id to give the new room (should already have been
requester: the user requesting the upgrade
old_room_id : the id of the room to be replaced
new_room_id: the id to give the new room (should already have been
created with _gemerate_room_id())
new_room_version (unicode): the new room version to use
tombstone_event_id (unicode|str): the ID of the tombstone event in the old
room.
new_room_version: the new room version to use
tombstone_event_id: the ID of the tombstone event in the old room.
Returns:
Deferred
"""
Expand All @@ -320,7 +326,7 @@ def clone_existing_room(
raise SynapseError(403, "You are not permitted to create rooms")

creation_content = {
"room_version": new_room_version,
"room_version": new_room_version.identifier,
"predecessor": {"room_id": old_room_id, "event_id": tombstone_event_id},
}

Expand Down Expand Up @@ -577,14 +583,15 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No
if ratelimit:
yield self.ratelimit(requester)

room_version = config.get(
room_version_id = config.get(
"room_version", self.config.default_room_version.identifier
)

if not isinstance(room_version, string_types):
if not isinstance(room_version_id, string_types):
raise SynapseError(400, "room_version must be a string", Codes.BAD_JSON)

if room_version not in KNOWN_ROOM_VERSIONS:
room_version = KNOWN_ROOM_VERSIONS.get(room_version_id)
if room_version is None:
raise SynapseError(
400,
"Your homeserver does not support this room version",
Expand Down Expand Up @@ -631,7 +638,9 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No
visibility = config.get("visibility", None)
is_public = visibility == "public"

room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public)
room_id = yield self._generate_room_id(
creator_id=user_id, is_public=is_public, room_version=room_version,
)

directory_handler = self.hs.get_handlers().directory_handler
if room_alias:
Expand Down Expand Up @@ -660,7 +669,7 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No
creation_content = config.get("creation_content", {})

# override any attempt to set room versions via the creation_content
creation_content["room_version"] = room_version
creation_content["room_version"] = room_version.identifier

yield self._send_events_for_new_room(
requester,
Expand Down Expand Up @@ -849,7 +858,9 @@ def send(etype, content, **kwargs):
yield send(etype=etype, state_key=state_key, content=content)

@defer.inlineCallbacks
def _generate_room_id(self, creator_id, is_public):
def _generate_room_id(
self, creator_id: str, is_public: str, room_version: RoomVersion,
):
# autogen room IDs and try to create it. We may clash, so just
# try a few times till one goes through, giving up eventually.
attempts = 0
Expand All @@ -863,6 +874,7 @@ def _generate_room_id(self, creator_id, is_public):
room_id=gen_room_id,
room_creator_user_id=creator_id,
is_public=is_public,
room_version=room_version,
)
return gen_room_id
except StoreError:
Expand Down
Loading

0 comments on commit 8df862e

Please sign in to comment.