mirror of
https://mau.dev/maunium/synapse.git
synced 2024-12-14 02:53:51 +01:00
Merge pull request #3654 from matrix-org/rav/room_versions
Support for room versioning
This commit is contained in:
commit
8521ae13e3
14 changed files with 244 additions and 22 deletions
1
changelog.d/3654.feature
Normal file
1
changelog.d/3654.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Basic support for room versioning
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014-2016 OpenMarket Ltd
|
# Copyright 2014-2016 OpenMarket Ltd
|
||||||
# Copyright 2017 Vector Creations Ltd
|
# Copyright 2017 Vector Creations Ltd
|
||||||
|
# Copyright 2018 New Vector Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -94,3 +95,11 @@ class RoomCreationPreset(object):
|
||||||
class ThirdPartyEntityKind(object):
|
class ThirdPartyEntityKind(object):
|
||||||
USER = "user"
|
USER = "user"
|
||||||
LOCATION = "location"
|
LOCATION = "location"
|
||||||
|
|
||||||
|
|
||||||
|
# the version we will give rooms which are created on this server
|
||||||
|
DEFAULT_ROOM_VERSION = "1"
|
||||||
|
|
||||||
|
# vdh-test-version is a placeholder to get room versioning support working and tested
|
||||||
|
# until we have a working v2.
|
||||||
|
KNOWN_ROOM_VERSIONS = {"1", "vdh-test-version"}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014-2016 OpenMarket Ltd
|
# Copyright 2014-2016 OpenMarket Ltd
|
||||||
|
# Copyright 2018 New Vector Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -56,6 +57,8 @@ class Codes(object):
|
||||||
CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
||||||
CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
||||||
MAU_LIMIT_EXCEEDED = "M_MAU_LIMIT_EXCEEDED"
|
MAU_LIMIT_EXCEEDED = "M_MAU_LIMIT_EXCEEDED"
|
||||||
|
UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION"
|
||||||
|
INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION"
|
||||||
|
|
||||||
|
|
||||||
class CodeMessageException(RuntimeError):
|
class CodeMessageException(RuntimeError):
|
||||||
|
@ -285,6 +288,27 @@ class LimitExceededError(SynapseError):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class IncompatibleRoomVersionError(SynapseError):
|
||||||
|
"""A server is trying to join a room whose version it does not support."""
|
||||||
|
|
||||||
|
def __init__(self, room_version):
|
||||||
|
super(IncompatibleRoomVersionError, self).__init__(
|
||||||
|
code=400,
|
||||||
|
msg="Your homeserver does not support the features required to "
|
||||||
|
"join this room",
|
||||||
|
errcode=Codes.INCOMPATIBLE_ROOM_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._room_version = room_version
|
||||||
|
|
||||||
|
def error_dict(self):
|
||||||
|
return cs_error(
|
||||||
|
self.msg,
|
||||||
|
self.errcode,
|
||||||
|
room_version=self._room_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def cs_error(msg, code=Codes.UNKNOWN, **kwargs):
|
def cs_error(msg, code=Codes.UNKNOWN, **kwargs):
|
||||||
""" Utility method for constructing an error response for client-server
|
""" Utility method for constructing an error response for client-server
|
||||||
interactions.
|
interactions.
|
||||||
|
|
|
@ -20,7 +20,7 @@ from signedjson.key import decode_verify_key_bytes
|
||||||
from signedjson.sign import SignatureVerifyException, verify_signed_json
|
from signedjson.sign import SignatureVerifyException, verify_signed_json
|
||||||
from unpaddedbase64 import decode_base64
|
from unpaddedbase64 import decode_base64
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, JoinRules, Membership
|
from synapse.api.constants import KNOWN_ROOM_VERSIONS, EventTypes, JoinRules, Membership
|
||||||
from synapse.api.errors import AuthError, EventSizeError, SynapseError
|
from synapse.api.errors import AuthError, EventSizeError, SynapseError
|
||||||
from synapse.types import UserID, get_domain_from_id
|
from synapse.types import UserID, get_domain_from_id
|
||||||
|
|
||||||
|
@ -83,6 +83,14 @@ def check(event, auth_events, do_sig_check=True, do_size_check=True):
|
||||||
403,
|
403,
|
||||||
"Creation event's room_id domain does not match sender's"
|
"Creation event's room_id domain does not match sender's"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
room_version = event.content.get("room_version", "1")
|
||||||
|
if room_version not in KNOWN_ROOM_VERSIONS:
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
"room appears to have unsupported version %s" % (
|
||||||
|
room_version,
|
||||||
|
))
|
||||||
# FIXME
|
# FIXME
|
||||||
logger.debug("Allowing! %s", event)
|
logger.debug("Allowing! %s", event)
|
||||||
return
|
return
|
||||||
|
|
|
@ -25,7 +25,7 @@ from prometheus_client import Counter
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import Membership
|
from synapse.api.constants import KNOWN_ROOM_VERSIONS, EventTypes, Membership
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
CodeMessageException,
|
CodeMessageException,
|
||||||
FederationDeniedError,
|
FederationDeniedError,
|
||||||
|
@ -518,10 +518,10 @@ class FederationClient(FederationBase):
|
||||||
description, destination, exc_info=1,
|
description, destination, exc_info=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
raise RuntimeError("Failed to %s via any server", description)
|
raise RuntimeError("Failed to %s via any server" % (description, ))
|
||||||
|
|
||||||
def make_membership_event(self, destinations, room_id, user_id, membership,
|
def make_membership_event(self, destinations, room_id, user_id, membership,
|
||||||
content={},):
|
content, params):
|
||||||
"""
|
"""
|
||||||
Creates an m.room.member event, with context, without participating in the room.
|
Creates an m.room.member event, with context, without participating in the room.
|
||||||
|
|
||||||
|
@ -537,8 +537,10 @@ class FederationClient(FederationBase):
|
||||||
user_id (str): The user whose membership is being evented.
|
user_id (str): The user whose membership is being evented.
|
||||||
membership (str): The "membership" property of the event. Must be
|
membership (str): The "membership" property of the event. Must be
|
||||||
one of "join" or "leave".
|
one of "join" or "leave".
|
||||||
content (object): Any additional data to put into the content field
|
content (dict): Any additional data to put into the content field
|
||||||
of the event.
|
of the event.
|
||||||
|
params (dict[str, str|Iterable[str]]): Query parameters to include in the
|
||||||
|
request.
|
||||||
Return:
|
Return:
|
||||||
Deferred: resolves to a tuple of (origin (str), event (object))
|
Deferred: resolves to a tuple of (origin (str), event (object))
|
||||||
where origin is the remote homeserver which generated the event.
|
where origin is the remote homeserver which generated the event.
|
||||||
|
@ -558,10 +560,12 @@ class FederationClient(FederationBase):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def send_request(destination):
|
def send_request(destination):
|
||||||
ret = yield self.transport_layer.make_membership_event(
|
ret = yield self.transport_layer.make_membership_event(
|
||||||
destination, room_id, user_id, membership
|
destination, room_id, user_id, membership, params,
|
||||||
)
|
)
|
||||||
|
|
||||||
pdu_dict = ret["event"]
|
pdu_dict = ret.get("event", None)
|
||||||
|
if not isinstance(pdu_dict, dict):
|
||||||
|
raise InvalidResponseError("Bad 'event' field in response")
|
||||||
|
|
||||||
logger.debug("Got response to make_%s: %s", membership, pdu_dict)
|
logger.debug("Got response to make_%s: %s", membership, pdu_dict)
|
||||||
|
|
||||||
|
@ -605,6 +609,26 @@ class FederationClient(FederationBase):
|
||||||
Fails with a ``RuntimeError`` if no servers were reachable.
|
Fails with a ``RuntimeError`` if no servers were reachable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def check_authchain_validity(signed_auth_chain):
|
||||||
|
for e in signed_auth_chain:
|
||||||
|
if e.type == EventTypes.Create:
|
||||||
|
create_event = e
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise InvalidResponseError(
|
||||||
|
"no %s in auth chain" % (EventTypes.Create,),
|
||||||
|
)
|
||||||
|
|
||||||
|
# the room version should be sane.
|
||||||
|
room_version = create_event.content.get("room_version", "1")
|
||||||
|
if room_version not in KNOWN_ROOM_VERSIONS:
|
||||||
|
# This shouldn't be possible, because the remote server should have
|
||||||
|
# rejected the join attempt during make_join.
|
||||||
|
raise InvalidResponseError(
|
||||||
|
"room appears to have unsupported version %s" % (
|
||||||
|
room_version,
|
||||||
|
))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def send_request(destination):
|
def send_request(destination):
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
|
@ -661,7 +685,7 @@ class FederationClient(FederationBase):
|
||||||
for s in signed_state:
|
for s in signed_state:
|
||||||
s.internal_metadata = copy.deepcopy(s.internal_metadata)
|
s.internal_metadata = copy.deepcopy(s.internal_metadata)
|
||||||
|
|
||||||
auth_chain.sort(key=lambda e: e.depth)
|
check_authchain_validity(signed_auth)
|
||||||
|
|
||||||
defer.returnValue({
|
defer.returnValue({
|
||||||
"state": signed_state,
|
"state": signed_state,
|
||||||
|
|
|
@ -27,7 +27,13 @@ from twisted.internet.abstract import isIPAddress
|
||||||
from twisted.python import failure
|
from twisted.python import failure
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes
|
||||||
from synapse.api.errors import AuthError, FederationError, NotFoundError, SynapseError
|
from synapse.api.errors import (
|
||||||
|
AuthError,
|
||||||
|
FederationError,
|
||||||
|
IncompatibleRoomVersionError,
|
||||||
|
NotFoundError,
|
||||||
|
SynapseError,
|
||||||
|
)
|
||||||
from synapse.crypto.event_signing import compute_event_signature
|
from synapse.crypto.event_signing import compute_event_signature
|
||||||
from synapse.federation.federation_base import FederationBase, event_from_pdu_json
|
from synapse.federation.federation_base import FederationBase, event_from_pdu_json
|
||||||
from synapse.federation.persistence import TransactionActions
|
from synapse.federation.persistence import TransactionActions
|
||||||
|
@ -323,12 +329,21 @@ class FederationServer(FederationBase):
|
||||||
defer.returnValue((200, resp))
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_make_join_request(self, origin, room_id, user_id):
|
def on_make_join_request(self, origin, room_id, user_id, supported_versions):
|
||||||
origin_host, _ = parse_server_name(origin)
|
origin_host, _ = parse_server_name(origin)
|
||||||
yield self.check_server_matches_acl(origin_host, room_id)
|
yield self.check_server_matches_acl(origin_host, room_id)
|
||||||
|
|
||||||
|
room_version = yield self.store.get_room_version(room_id)
|
||||||
|
if room_version not in supported_versions:
|
||||||
|
logger.warn("Room version %s not in %s", room_version, supported_versions)
|
||||||
|
raise IncompatibleRoomVersionError(room_version=room_version)
|
||||||
|
|
||||||
pdu = yield self.handler.on_make_join_request(room_id, user_id)
|
pdu = yield self.handler.on_make_join_request(room_id, user_id)
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
defer.returnValue({"event": pdu.get_pdu_json(time_now)})
|
defer.returnValue({
|
||||||
|
"event": pdu.get_pdu_json(time_now),
|
||||||
|
"room_version": room_version,
|
||||||
|
})
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_invite_request(self, origin, content):
|
def on_invite_request(self, origin, content):
|
||||||
|
|
|
@ -195,7 +195,7 @@ class TransportLayerClient(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def make_membership_event(self, destination, room_id, user_id, membership):
|
def make_membership_event(self, destination, room_id, user_id, membership, params):
|
||||||
"""Asks a remote server to build and sign us a membership event
|
"""Asks a remote server to build and sign us a membership event
|
||||||
|
|
||||||
Note that this does not append any events to any graphs.
|
Note that this does not append any events to any graphs.
|
||||||
|
@ -205,6 +205,8 @@ class TransportLayerClient(object):
|
||||||
room_id (str): room to join/leave
|
room_id (str): room to join/leave
|
||||||
user_id (str): user to be joined/left
|
user_id (str): user to be joined/left
|
||||||
membership (str): one of join/leave
|
membership (str): one of join/leave
|
||||||
|
params (dict[str, str|Iterable[str]]): Query parameters to include in the
|
||||||
|
request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred: Succeeds when we get a 2xx HTTP response. The result
|
Deferred: Succeeds when we get a 2xx HTTP response. The result
|
||||||
|
@ -241,6 +243,7 @@ class TransportLayerClient(object):
|
||||||
content = yield self.client.get_json(
|
content = yield self.client.get_json(
|
||||||
destination=destination,
|
destination=destination,
|
||||||
path=path,
|
path=path,
|
||||||
|
args=params,
|
||||||
retry_on_dns_fail=retry_on_dns_fail,
|
retry_on_dns_fail=retry_on_dns_fail,
|
||||||
timeout=20000,
|
timeout=20000,
|
||||||
ignore_backoff=ignore_backoff,
|
ignore_backoff=ignore_backoff,
|
||||||
|
|
|
@ -190,6 +190,41 @@ def _parse_auth_header(header_bytes):
|
||||||
|
|
||||||
|
|
||||||
class BaseFederationServlet(object):
|
class BaseFederationServlet(object):
|
||||||
|
"""Abstract base class for federation servlet classes.
|
||||||
|
|
||||||
|
The servlet object should have a PATH attribute which takes the form of a regexp to
|
||||||
|
match against the request path (excluding the /federation/v1 prefix).
|
||||||
|
|
||||||
|
The servlet should also implement one or more of on_GET, on_POST, on_PUT, to match
|
||||||
|
the appropriate HTTP method. These methods have the signature:
|
||||||
|
|
||||||
|
on_<METHOD>(self, origin, content, query, **kwargs)
|
||||||
|
|
||||||
|
With arguments:
|
||||||
|
|
||||||
|
origin (unicode|None): The authenticated server_name of the calling server,
|
||||||
|
unless REQUIRE_AUTH is set to False and authentication failed.
|
||||||
|
|
||||||
|
content (unicode|None): decoded json body of the request. None if the
|
||||||
|
request was a GET.
|
||||||
|
|
||||||
|
query (dict[bytes, list[bytes]]): Query params from the request. url-decoded
|
||||||
|
(ie, '+' and '%xx' are decoded) but note that it is *not* utf8-decoded
|
||||||
|
yet.
|
||||||
|
|
||||||
|
**kwargs (dict[unicode, unicode]): the dict mapping keys to path
|
||||||
|
components as specified in the path match regexp.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred[(int, object)|None]: either (response code, response object) to
|
||||||
|
return a JSON response, or None if the request has already been handled.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SynapseError: to return an error code
|
||||||
|
|
||||||
|
Exception: other exceptions will be caught, logged, and a 500 will be
|
||||||
|
returned.
|
||||||
|
"""
|
||||||
REQUIRE_AUTH = True
|
REQUIRE_AUTH = True
|
||||||
|
|
||||||
def __init__(self, handler, authenticator, ratelimiter, server_name):
|
def __init__(self, handler, authenticator, ratelimiter, server_name):
|
||||||
|
@ -204,6 +239,18 @@ class BaseFederationServlet(object):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def new_func(request, *args, **kwargs):
|
def new_func(request, *args, **kwargs):
|
||||||
|
""" A callback which can be passed to HttpServer.RegisterPaths
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (twisted.web.http.Request):
|
||||||
|
*args: unused?
|
||||||
|
**kwargs (dict[unicode, unicode]): the dict mapping keys to path
|
||||||
|
components as specified in the path match regexp.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred[(int, object)|None]: (response code, response object) as returned
|
||||||
|
by the callback method. None if the request has already been handled.
|
||||||
|
"""
|
||||||
content = None
|
content = None
|
||||||
if request.method in ["PUT", "POST"]:
|
if request.method in ["PUT", "POST"]:
|
||||||
# TODO: Handle other method types? other content types?
|
# TODO: Handle other method types? other content types?
|
||||||
|
@ -384,9 +431,31 @@ class FederationMakeJoinServlet(BaseFederationServlet):
|
||||||
PATH = "/make_join/(?P<context>[^/]*)/(?P<user_id>[^/]*)"
|
PATH = "/make_join/(?P<context>[^/]*)/(?P<user_id>[^/]*)"
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_GET(self, origin, content, query, context, user_id):
|
def on_GET(self, origin, _content, query, context, user_id):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
origin (unicode): The authenticated server_name of the calling server
|
||||||
|
|
||||||
|
_content (None): (GETs don't have bodies)
|
||||||
|
|
||||||
|
query (dict[bytes, list[bytes]]): Query params from the request.
|
||||||
|
|
||||||
|
**kwargs (dict[unicode, unicode]): the dict mapping keys to path
|
||||||
|
components as specified in the path match regexp.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred[(int, object)|None]: either (response code, response object) to
|
||||||
|
return a JSON response, or None if the request has already been handled.
|
||||||
|
"""
|
||||||
|
versions = query.get(b'ver')
|
||||||
|
if versions is not None:
|
||||||
|
supported_versions = [v.decode("utf-8") for v in versions]
|
||||||
|
else:
|
||||||
|
supported_versions = ["1"]
|
||||||
|
|
||||||
content = yield self.handler.on_make_join_request(
|
content = yield self.handler.on_make_join_request(
|
||||||
origin, context, user_id,
|
origin, context, user_id,
|
||||||
|
supported_versions=supported_versions,
|
||||||
)
|
)
|
||||||
defer.returnValue((200, content))
|
defer.returnValue((200, content))
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,12 @@ from unpaddedbase64 import decode_base64
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, Membership, RejectedReason
|
from synapse.api.constants import (
|
||||||
|
KNOWN_ROOM_VERSIONS,
|
||||||
|
EventTypes,
|
||||||
|
Membership,
|
||||||
|
RejectedReason,
|
||||||
|
)
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
CodeMessageException,
|
CodeMessageException,
|
||||||
|
@ -922,6 +927,9 @@ class FederationHandler(BaseHandler):
|
||||||
joinee,
|
joinee,
|
||||||
"join",
|
"join",
|
||||||
content,
|
content,
|
||||||
|
params={
|
||||||
|
"ver": KNOWN_ROOM_VERSIONS,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# This shouldn't happen, because the RoomMemberHandler has a
|
# This shouldn't happen, because the RoomMemberHandler has a
|
||||||
|
@ -1187,13 +1195,14 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _make_and_verify_event(self, target_hosts, room_id, user_id, membership,
|
def _make_and_verify_event(self, target_hosts, room_id, user_id, membership,
|
||||||
content={},):
|
content={}, params=None):
|
||||||
origin, pdu = yield self.federation_client.make_membership_event(
|
origin, pdu = yield self.federation_client.make_membership_event(
|
||||||
target_hosts,
|
target_hosts,
|
||||||
room_id,
|
room_id,
|
||||||
user_id,
|
user_id,
|
||||||
membership,
|
membership,
|
||||||
content,
|
content,
|
||||||
|
params=params,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("Got response to make_%s: %s", membership, pdu)
|
logger.debug("Got response to make_%s: %s", membership, pdu)
|
||||||
|
|
|
@ -21,9 +21,17 @@ import math
|
||||||
import string
|
import string
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
|
from synapse.api.constants import (
|
||||||
|
DEFAULT_ROOM_VERSION,
|
||||||
|
KNOWN_ROOM_VERSIONS,
|
||||||
|
EventTypes,
|
||||||
|
JoinRules,
|
||||||
|
RoomCreationPreset,
|
||||||
|
)
|
||||||
from synapse.api.errors import AuthError, Codes, StoreError, SynapseError
|
from synapse.api.errors import AuthError, Codes, StoreError, SynapseError
|
||||||
from synapse.types import RoomAlias, RoomID, RoomStreamToken, StreamToken, UserID
|
from synapse.types import RoomAlias, RoomID, RoomStreamToken, StreamToken, UserID
|
||||||
from synapse.util import stringutils
|
from synapse.util import stringutils
|
||||||
|
@ -99,6 +107,21 @@ class RoomCreationHandler(BaseHandler):
|
||||||
if ratelimit:
|
if ratelimit:
|
||||||
yield self.ratelimit(requester)
|
yield self.ratelimit(requester)
|
||||||
|
|
||||||
|
room_version = config.get("room_version", DEFAULT_ROOM_VERSION)
|
||||||
|
if not isinstance(room_version, string_types):
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"room_version must be a string",
|
||||||
|
Codes.BAD_JSON,
|
||||||
|
)
|
||||||
|
|
||||||
|
if room_version not in KNOWN_ROOM_VERSIONS:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Your homeserver does not support this room version",
|
||||||
|
Codes.UNSUPPORTED_ROOM_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
if "room_alias_name" in config:
|
if "room_alias_name" in config:
|
||||||
for wchar in string.whitespace:
|
for wchar in string.whitespace:
|
||||||
if wchar in config["room_alias_name"]:
|
if wchar in config["room_alias_name"]:
|
||||||
|
@ -184,6 +207,9 @@ class RoomCreationHandler(BaseHandler):
|
||||||
|
|
||||||
creation_content = config.get("creation_content", {})
|
creation_content = config.get("creation_content", {})
|
||||||
|
|
||||||
|
# override any attempt to set room versions via the creation_content
|
||||||
|
creation_content["room_version"] = room_version
|
||||||
|
|
||||||
room_member_handler = self.hs.get_room_member_handler()
|
room_member_handler = self.hs.get_room_member_handler()
|
||||||
|
|
||||||
yield self._send_events_for_new_room(
|
yield self._send_events_for_new_room(
|
||||||
|
|
|
@ -439,7 +439,7 @@ class MatrixFederationHttpClient(object):
|
||||||
defer.returnValue(json.loads(body))
|
defer.returnValue(json.loads(body))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_json(self, destination, path, args={}, retry_on_dns_fail=True,
|
def get_json(self, destination, path, args=None, retry_on_dns_fail=True,
|
||||||
timeout=None, ignore_backoff=False):
|
timeout=None, ignore_backoff=False):
|
||||||
""" GETs some json from the given host homeserver and path
|
""" GETs some json from the given host homeserver and path
|
||||||
|
|
||||||
|
@ -447,7 +447,7 @@ class MatrixFederationHttpClient(object):
|
||||||
destination (str): The remote server to send the HTTP request
|
destination (str): The remote server to send the HTTP request
|
||||||
to.
|
to.
|
||||||
path (str): The HTTP path.
|
path (str): The HTTP path.
|
||||||
args (dict): A dictionary used to create query strings, defaults to
|
args (dict|None): A dictionary used to create query strings, defaults to
|
||||||
None.
|
None.
|
||||||
timeout (int): How long to try (in ms) the destination for before
|
timeout (int): How long to try (in ms) the destination for before
|
||||||
giving up. None indicates no timeout and that the request will
|
giving up. None indicates no timeout and that the request will
|
||||||
|
@ -702,6 +702,9 @@ def check_content_type_is_json(headers):
|
||||||
|
|
||||||
|
|
||||||
def encode_query_args(args):
|
def encode_query_args(args):
|
||||||
|
if args is None:
|
||||||
|
return b""
|
||||||
|
|
||||||
encoded_args = {}
|
encoded_args = {}
|
||||||
for k, vs in args.items():
|
for k, vs in args.items():
|
||||||
if isinstance(vs, string_types):
|
if isinstance(vs, string_types):
|
||||||
|
|
|
@ -44,8 +44,8 @@ class SlavedEventStore(EventFederationWorkerStore,
|
||||||
RoomMemberWorkerStore,
|
RoomMemberWorkerStore,
|
||||||
EventPushActionsWorkerStore,
|
EventPushActionsWorkerStore,
|
||||||
StreamWorkerStore,
|
StreamWorkerStore,
|
||||||
EventsWorkerStore,
|
|
||||||
StateGroupWorkerStore,
|
StateGroupWorkerStore,
|
||||||
|
EventsWorkerStore,
|
||||||
SignatureWorkerStore,
|
SignatureWorkerStore,
|
||||||
UserErasureWorkerStore,
|
UserErasureWorkerStore,
|
||||||
BaseSlavedStore):
|
BaseSlavedStore):
|
||||||
|
|
|
@ -21,15 +21,17 @@ from six.moves import range
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
from synapse.api.constants import EventTypes
|
||||||
|
from synapse.api.errors import NotFoundError
|
||||||
|
from synapse.storage._base import SQLBaseStore
|
||||||
from synapse.storage.background_updates import BackgroundUpdateStore
|
from synapse.storage.background_updates import BackgroundUpdateStore
|
||||||
from synapse.storage.engines import PostgresEngine
|
from synapse.storage.engines import PostgresEngine
|
||||||
|
from synapse.storage.events_worker import EventsWorkerStore
|
||||||
from synapse.util.caches import get_cache_factor_for, intern_string
|
from synapse.util.caches import get_cache_factor_for, intern_string
|
||||||
from synapse.util.caches.descriptors import cached, cachedList
|
from synapse.util.caches.descriptors import cached, cachedList
|
||||||
from synapse.util.caches.dictionary_cache import DictionaryCache
|
from synapse.util.caches.dictionary_cache import DictionaryCache
|
||||||
from synapse.util.stringutils import to_ascii
|
from synapse.util.stringutils import to_ascii
|
||||||
|
|
||||||
from ._base import SQLBaseStore
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +48,8 @@ class _GetStateGroupDelta(namedtuple("_GetStateGroupDelta", ("prev_group", "delt
|
||||||
return len(self.delta_ids) if self.delta_ids else 0
|
return len(self.delta_ids) if self.delta_ids else 0
|
||||||
|
|
||||||
|
|
||||||
class StateGroupWorkerStore(SQLBaseStore):
|
# this inherits from EventsWorkerStore because it calls self.get_events
|
||||||
|
class StateGroupWorkerStore(EventsWorkerStore, SQLBaseStore):
|
||||||
"""The parts of StateGroupStore that can be called from workers.
|
"""The parts of StateGroupStore that can be called from workers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -61,6 +64,30 @@ class StateGroupWorkerStore(SQLBaseStore):
|
||||||
"*stateGroupCache*", 500000 * get_cache_factor_for("stateGroupCache")
|
"*stateGroupCache*", 500000 * get_cache_factor_for("stateGroupCache")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_room_version(self, room_id):
|
||||||
|
"""Get the room_version of a given room
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id (str)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred[str]
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFoundError if the room is unknown
|
||||||
|
"""
|
||||||
|
# for now we do this by looking at the create event. We may want to cache this
|
||||||
|
# more intelligently in future.
|
||||||
|
state_ids = yield self.get_current_state_ids(room_id)
|
||||||
|
create_id = state_ids.get((EventTypes.Create, ""))
|
||||||
|
|
||||||
|
if not create_id:
|
||||||
|
raise NotFoundError("Unknown room")
|
||||||
|
|
||||||
|
create_event = yield self.get_event(create_id)
|
||||||
|
defer.returnValue(create_event.content.get("room_version", "1"))
|
||||||
|
|
||||||
@cached(max_entries=100000, iterable=True)
|
@cached(max_entries=100000, iterable=True)
|
||||||
def get_current_state_ids(self, room_id):
|
def get_current_state_ids(self, room_id):
|
||||||
"""Get the current state event ids for a room based on the
|
"""Get the current state event ids for a room based on the
|
||||||
|
|
|
@ -77,6 +77,10 @@ def setup_test_homeserver(name="test", datastore=None, config=None, reactor=None
|
||||||
config.max_mau_value = 50
|
config.max_mau_value = 50
|
||||||
config.mau_limits_reserved_threepids = []
|
config.mau_limits_reserved_threepids = []
|
||||||
|
|
||||||
|
# we need a sane default_room_version, otherwise attempts to create rooms will
|
||||||
|
# fail.
|
||||||
|
config.default_room_version = "1"
|
||||||
|
|
||||||
# disable user directory updates, because they get done in the
|
# disable user directory updates, because they get done in the
|
||||||
# background, which upsets the test runner.
|
# background, which upsets the test runner.
|
||||||
config.update_user_directory = False
|
config.update_user_directory = False
|
||||||
|
|
Loading…
Reference in a new issue