diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py
index 533ba327f5..67db763dbf 100644
--- a/synapse/events/__init__.py
+++ b/synapse/events/__init__.py
@@ -20,8 +20,6 @@ import os
from distutils.util import strtobool
from typing import Dict, Optional, Type
-import six
-
from unpaddedbase64 import encode_base64
from synapse.api.room_versions import EventFormatVersions, RoomVersion, RoomVersions
@@ -135,6 +133,8 @@ class _EventInternalMetadata(object):
rejection. This is needed as those events are marked as outliers, but
they still need to be processed as if they're new events (e.g. updating
invite state in the database, relaying to clients, etc).
+
+ (Added in synapse 0.99.0, so may be unreliable for events received before that)
"""
return self._dict.get("out_of_band_membership", False)
@@ -290,7 +290,7 @@ class EventBase(metaclass=abc.ABCMeta):
return list(self._dict.items())
def keys(self):
- return six.iterkeys(self._dict)
+ return self._dict.keys()
def prev_event_ids(self):
"""Returns the list of prev event IDs. The order matches the order
diff --git a/synapse/events/builder.py b/synapse/events/builder.py
index a0c4a40c27..9ed24380dd 100644
--- a/synapse/events/builder.py
+++ b/synapse/events/builder.py
@@ -17,8 +17,7 @@ from typing import Optional
import attr
from nacl.signing import SigningKey
-from twisted.internet import defer
-
+from synapse.api.auth import Auth
from synapse.api.constants import MAX_DEPTH
from synapse.api.errors import UnsupportedRoomVersionError
from synapse.api.room_versions import (
@@ -29,6 +28,8 @@ from synapse.api.room_versions import (
)
from synapse.crypto.event_signing import add_hashes_and_signatures
from synapse.events import EventBase, _EventInternalMetadata, make_event_from_dict
+from synapse.state import StateHandler
+from synapse.storage.databases.main import DataStore
from synapse.types import EventID, JsonDict
from synapse.util import Clock
from synapse.util.stringutils import random_string
@@ -44,45 +45,46 @@ class EventBuilder(object):
Attributes:
room_version: Version of the target room
- room_id (str)
- type (str)
- sender (str)
- content (dict)
- unsigned (dict)
- internal_metadata (_EventInternalMetadata)
-
- _state (StateHandler)
- _auth (synapse.api.Auth)
- _store (DataStore)
- _clock (Clock)
- _hostname (str): The hostname of the server creating the event
+ room_id
+ type
+ sender
+ content
+ unsigned
+ internal_metadata
+
+ _state
+ _auth
+ _store
+ _clock
+ _hostname: The hostname of the server creating the event
_signing_key: The signing key to use to sign the event as the server
"""
- _state = attr.ib()
- _auth = attr.ib()
- _store = attr.ib()
- _clock = attr.ib()
- _hostname = attr.ib()
- _signing_key = attr.ib()
+ _state = attr.ib(type=StateHandler)
+ _auth = attr.ib(type=Auth)
+ _store = attr.ib(type=DataStore)
+ _clock = attr.ib(type=Clock)
+ _hostname = attr.ib(type=str)
+ _signing_key = attr.ib(type=SigningKey)
room_version = attr.ib(type=RoomVersion)
- room_id = attr.ib()
- type = attr.ib()
- sender = attr.ib()
+ room_id = attr.ib(type=str)
+ type = attr.ib(type=str)
+ sender = attr.ib(type=str)
- content = attr.ib(default=attr.Factory(dict))
- unsigned = attr.ib(default=attr.Factory(dict))
+ content = attr.ib(default=attr.Factory(dict), type=JsonDict)
+ unsigned = attr.ib(default=attr.Factory(dict), type=JsonDict)
# These only exist on a subset of events, so they raise AttributeError if
# someone tries to get them when they don't exist.
- _state_key = attr.ib(default=None)
- _redacts = attr.ib(default=None)
- _origin_server_ts = attr.ib(default=None)
+ _state_key = attr.ib(default=None, type=Optional[str])
+ _redacts = attr.ib(default=None, type=Optional[str])
+ _origin_server_ts = attr.ib(default=None, type=Optional[int])
internal_metadata = attr.ib(
- default=attr.Factory(lambda: _EventInternalMetadata({}))
+ default=attr.Factory(lambda: _EventInternalMetadata({})),
+ type=_EventInternalMetadata,
)
@property
@@ -95,31 +97,30 @@ class EventBuilder(object):
def is_state(self):
return self._state_key is not None
- @defer.inlineCallbacks
- def build(self, prev_event_ids):
+ async def build(self, prev_event_ids):
"""Transform into a fully signed and hashed event
Args:
prev_event_ids (list[str]): The event IDs to use as the prev events
Returns:
- Deferred[FrozenEvent]
+ FrozenEvent
"""
- state_ids = yield self._state.get_current_state_ids(
+ state_ids = await self._state.get_current_state_ids(
self.room_id, prev_event_ids
)
- auth_ids = yield self._auth.compute_auth_events(self, state_ids)
+ auth_ids = self._auth.compute_auth_events(self, state_ids)
format_version = self.room_version.event_format
if format_version == EventFormatVersions.V1:
- auth_events = yield self._store.add_event_hashes(auth_ids)
- prev_events = yield self._store.add_event_hashes(prev_event_ids)
+ auth_events = await self._store.add_event_hashes(auth_ids)
+ prev_events = await self._store.add_event_hashes(prev_event_ids)
else:
auth_events = auth_ids
prev_events = prev_event_ids
- old_depth = yield self._store.get_max_depth_of(prev_event_ids)
+ old_depth = await self._store.get_max_depth_of(prev_event_ids)
depth = old_depth + 1
# we cap depth of generated events, to ensure that they are not
@@ -162,7 +163,7 @@ class EventBuilderFactory(object):
def __init__(self, hs):
self.clock = hs.get_clock()
self.hostname = hs.hostname
- self.signing_key = hs.config.signing_key[0]
+ self.signing_key = hs.signing_key
self.store = hs.get_datastore()
self.state = hs.get_state_handler()
diff --git a/synapse/events/snapshot.py b/synapse/events/snapshot.py
index 7c5f620d09..afecafe15c 100644
--- a/synapse/events/snapshot.py
+++ b/synapse/events/snapshot.py
@@ -12,19 +12,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import Optional, Union
-
-from six import iteritems
+from typing import TYPE_CHECKING, Optional, Union
import attr
from frozendict import frozendict
-from twisted.internet import defer
-
from synapse.appservice import ApplicationService
+from synapse.events import EventBase
from synapse.logging.context import make_deferred_yieldable, run_in_background
from synapse.types import StateMap
+if TYPE_CHECKING:
+ from synapse.storage.databases.main import DataStore
+
@attr.s(slots=True)
class EventContext:
@@ -131,8 +131,7 @@ class EventContext:
delta_ids=delta_ids,
)
- @defer.inlineCallbacks
- def serialize(self, event, store):
+ async def serialize(self, event: EventBase, store: "DataStore") -> dict:
"""Converts self to a type that can be serialized as JSON, and then
deserialized by `deserialize`
@@ -148,7 +147,7 @@ class EventContext:
# the prev_state_ids, so if we're a state event we include the event
# id that we replaced in the state.
if event.is_state():
- prev_state_ids = yield self.get_prev_state_ids()
+ prev_state_ids = await self.get_prev_state_ids()
prev_state_id = prev_state_ids.get((event.type, event.state_key))
else:
prev_state_id = None
@@ -216,8 +215,7 @@ class EventContext:
return self._state_group
- @defer.inlineCallbacks
- def get_current_state_ids(self):
+ async def get_current_state_ids(self) -> Optional[StateMap[str]]:
"""
Gets the room state map, including this event - ie, the state in ``state_group``
@@ -226,32 +224,31 @@ class EventContext:
``rejected`` is set.
Returns:
- Deferred[dict[(str, str), str]|None]: Returns None if state_group
- is None, which happens when the associated event is an outlier.
+ Returns None if state_group is None, which happens when the associated
+ event is an outlier.
- Maps a (type, state_key) to the event ID of the state event matching
- this tuple.
+ Maps a (type, state_key) to the event ID of the state event matching
+ this tuple.
"""
if self.rejected:
raise RuntimeError("Attempt to access state_ids of rejected event")
- yield self._ensure_fetched()
+ await self._ensure_fetched()
return self._current_state_ids
- @defer.inlineCallbacks
- def get_prev_state_ids(self):
+ async def get_prev_state_ids(self):
"""
Gets the room state map, excluding this event.
For a non-state event, this will be the same as get_current_state_ids().
Returns:
- Deferred[dict[(str, str), str]|None]: Returns None if state_group
+ dict[(str, str), str]|None: Returns None if state_group
is None, which happens when the associated event is an outlier.
Maps a (type, state_key) to the event ID of the state event matching
this tuple.
"""
- yield self._ensure_fetched()
+ await self._ensure_fetched()
return self._prev_state_ids
def get_cached_current_state_ids(self):
@@ -271,8 +268,8 @@ class EventContext:
return self._current_state_ids
- def _ensure_fetched(self):
- return defer.succeed(None)
+ async def _ensure_fetched(self):
+ return None
@attr.s(slots=True)
@@ -305,21 +302,20 @@ class _AsyncEventContextImpl(EventContext):
_event_state_key = attr.ib(default=None)
_fetching_state_deferred = attr.ib(default=None)
- def _ensure_fetched(self):
+ async def _ensure_fetched(self):
if not self._fetching_state_deferred:
self._fetching_state_deferred = run_in_background(self._fill_out_state)
- return make_deferred_yieldable(self._fetching_state_deferred)
+ return await make_deferred_yieldable(self._fetching_state_deferred)
- @defer.inlineCallbacks
- def _fill_out_state(self):
+ async def _fill_out_state(self):
"""Called to populate the _current_state_ids and _prev_state_ids
attributes by loading from the database.
"""
if self.state_group is None:
return
- self._current_state_ids = yield self._storage.state.get_state_ids_for_group(
+ self._current_state_ids = await self._storage.state.get_state_ids_for_group(
self.state_group
)
if self._event_state_key is not None:
@@ -341,7 +337,7 @@ def _encode_state_dict(state_dict):
if state_dict is None:
return None
- return [(etype, state_key, v) for (etype, state_key), v in iteritems(state_dict)]
+ return [(etype, state_key, v) for (etype, state_key), v in state_dict.items()]
def _decode_state_dict(input):
diff --git a/synapse/events/spamcheck.py b/synapse/events/spamcheck.py
index 1ffc9525d1..a7cddac974 100644
--- a/synapse/events/spamcheck.py
+++ b/synapse/events/spamcheck.py
@@ -15,9 +15,10 @@
# limitations under the License.
import inspect
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Optional, Tuple
-from synapse.spam_checker_api import SpamCheckerApi
+from synapse.spam_checker_api import RegistrationBehaviour, SpamCheckerApi
+from synapse.types import Collection
MYPY = False
if MYPY:
@@ -160,3 +161,33 @@ class SpamChecker(object):
return True
return False
+
+ def check_registration_for_spam(
+ self,
+ email_threepid: Optional[dict],
+ username: Optional[str],
+ request_info: Collection[Tuple[str, str]],
+ ) -> RegistrationBehaviour:
+ """Checks if we should allow the given registration request.
+
+ Args:
+ email_threepid: The email threepid used for registering, if any
+ username: The request user name, if any
+ request_info: List of tuples of user agent and IP that
+ were used during the registration process.
+
+ Returns:
+ Enum for how the request should be handled
+ """
+
+ for spam_checker in self.spam_checkers:
+ # For backwards compatibility, only run if the method exists on the
+ # spam checker
+ checker = getattr(spam_checker, "check_registration_for_spam", None)
+ if checker:
+ behaviour = checker(email_threepid, username, request_info)
+ assert isinstance(behaviour, RegistrationBehaviour)
+ if behaviour != RegistrationBehaviour.ALLOW:
+ return behaviour
+
+ return RegistrationBehaviour.ALLOW
diff --git a/synapse/events/third_party_rules.py b/synapse/events/third_party_rules.py
index 459132d388..2956a64234 100644
--- a/synapse/events/third_party_rules.py
+++ b/synapse/events/third_party_rules.py
@@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from twisted.internet import defer
+from synapse.events import EventBase
+from synapse.events.snapshot import EventContext
+from synapse.types import Requester
class ThirdPartyEventRules(object):
@@ -39,76 +41,79 @@ class ThirdPartyEventRules(object):
config=config, http_client=hs.get_simple_http_client()
)
- @defer.inlineCallbacks
- def check_event_allowed(self, event, context):
+ async def check_event_allowed(
+ self, event: EventBase, context: EventContext
+ ) -> bool:
"""Check if a provided event should be allowed in the given context.
Args:
- event (synapse.events.EventBase): The event to be checked.
- context (synapse.events.snapshot.EventContext): The context of the event.
+ event: The event to be checked.
+ context: The context of the event.
Returns:
- defer.Deferred[bool]: True if the event should be allowed, False if not.
+ True if the event should be allowed, False if not.
"""
if self.third_party_rules is None:
return True
- prev_state_ids = yield context.get_prev_state_ids()
+ prev_state_ids = await context.get_prev_state_ids()
# Retrieve the state events from the database.
state_events = {}
for key, event_id in prev_state_ids.items():
- state_events[key] = yield self.store.get_event(event_id, allow_none=True)
+ state_events[key] = await self.store.get_event(event_id, allow_none=True)
- ret = yield self.third_party_rules.check_event_allowed(event, state_events)
+ ret = await self.third_party_rules.check_event_allowed(event, state_events)
return ret
- @defer.inlineCallbacks
- def on_create_room(self, requester, config, is_requester_admin):
+ async def on_create_room(
+ self, requester: Requester, config: dict, is_requester_admin: bool
+ ) -> bool:
"""Intercept requests to create room to allow, deny or update the
request config.
Args:
- requester (Requester)
- config (dict): The creation config from the client.
- is_requester_admin (bool): If the requester is an admin
+ requester
+ config: The creation config from the client.
+ is_requester_admin: If the requester is an admin
Returns:
- defer.Deferred[bool]: Whether room creation is allowed or denied.
+ Whether room creation is allowed or denied.
"""
if self.third_party_rules is None:
return True
- ret = yield self.third_party_rules.on_create_room(
+ ret = await self.third_party_rules.on_create_room(
requester, config, is_requester_admin
)
return ret
- @defer.inlineCallbacks
- def check_threepid_can_be_invited(self, medium, address, room_id):
+ async def check_threepid_can_be_invited(
+ self, medium: str, address: str, room_id: str
+ ) -> bool:
"""Check if a provided 3PID can be invited in the given room.
Args:
- medium (str): The 3PID's medium.
- address (str): The 3PID's address.
- room_id (str): The room we want to invite the threepid to.
+ medium: The 3PID's medium.
+ address: The 3PID's address.
+ room_id: The room we want to invite the threepid to.
Returns:
- defer.Deferred[bool], True if the 3PID can be invited, False if not.
+ True if the 3PID can be invited, False if not.
"""
if self.third_party_rules is None:
return True
- state_ids = yield self.store.get_filtered_current_state_ids(room_id)
- room_state_events = yield self.store.get_events(state_ids.values())
+ state_ids = await self.store.get_filtered_current_state_ids(room_id)
+ room_state_events = await self.store.get_events(state_ids.values())
state_events = {}
for key, event_id in state_ids.items():
state_events[key] = room_state_events[event_id]
- ret = yield self.third_party_rules.check_threepid_can_be_invited(
+ ret = await self.third_party_rules.check_threepid_can_be_invited(
medium, address, state_events
)
return ret
diff --git a/synapse/events/utils.py b/synapse/events/utils.py
index dd340be9a7..2d42e268c6 100644
--- a/synapse/events/utils.py
+++ b/synapse/events/utils.py
@@ -12,16 +12,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-import collections
+import collections.abc
import re
from typing import Any, Mapping, Union
-from six import string_types
-
from frozendict import frozendict
-from twisted.internet import defer
-
from synapse.api.constants import EventTypes, RelationTypes
from synapse.api.errors import Codes, SynapseError
from synapse.api.room_versions import RoomVersion
@@ -318,7 +314,7 @@ def serialize_event(
if only_event_fields:
if not isinstance(only_event_fields, list) or not all(
- isinstance(f, string_types) for f in only_event_fields
+ isinstance(f, str) for f in only_event_fields
):
raise TypeError("only_event_fields must be a list of strings")
d = only_fields(d, only_event_fields)
@@ -339,8 +335,9 @@ class EventClientSerializer(object):
hs.config.experimental_msc1849_support_enabled
)
- @defer.inlineCallbacks
- def serialize_event(self, event, time_now, bundle_aggregations=True, **kwargs):
+ async def serialize_event(
+ self, event, time_now, bundle_aggregations=True, **kwargs
+ ):
"""Serializes a single event.
Args:
@@ -350,7 +347,7 @@ class EventClientSerializer(object):
**kwargs: Arguments to pass to `serialize_event`
Returns:
- Deferred[dict]: The serialized event
+ dict: The serialized event
"""
# To handle the case of presence events and the like
if not isinstance(event, EventBase):
@@ -365,8 +362,8 @@ class EventClientSerializer(object):
if not event.internal_metadata.is_redacted() and (
self.experimental_msc1849_support_enabled and bundle_aggregations
):
- annotations = yield self.store.get_aggregation_groups_for_event(event_id)
- references = yield self.store.get_relations_for_event(
+ annotations = await self.store.get_aggregation_groups_for_event(event_id)
+ references = await self.store.get_relations_for_event(
event_id, RelationTypes.REFERENCE, direction="f"
)
@@ -380,7 +377,7 @@ class EventClientSerializer(object):
edit = None
if event.type == EventTypes.Message:
- edit = yield self.store.get_applicable_edit(event_id)
+ edit = await self.store.get_applicable_edit(event_id)
if edit:
# If there is an edit replace the content, preserving existing
@@ -426,7 +423,7 @@ def copy_power_levels_contents(
Raises:
TypeError if the input does not look like a valid power levels event content
"""
- if not isinstance(old_power_levels, collections.Mapping):
+ if not isinstance(old_power_levels, collections.abc.Mapping):
raise TypeError("Not a valid power-levels content: %r" % (old_power_levels,))
power_levels = {}
@@ -436,7 +433,7 @@ def copy_power_levels_contents(
power_levels[k] = v
continue
- if isinstance(v, collections.Mapping):
+ if isinstance(v, collections.abc.Mapping):
power_levels[k] = h = {}
for k1, v1 in v.items():
# we should only have one level of nesting
diff --git a/synapse/events/validator.py b/synapse/events/validator.py
index b001c64bb4..5ce3874fba 100644
--- a/synapse/events/validator.py
+++ b/synapse/events/validator.py
@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from six import integer_types, string_types
-
from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership
from synapse.api.errors import Codes, SynapseError
from synapse.api.room_versions import EventFormatVersions
@@ -53,7 +51,7 @@ class EventValidator(object):
event_strings = ["origin"]
for s in event_strings:
- if not isinstance(getattr(event, s), string_types):
+ if not isinstance(getattr(event, s), str):
raise SynapseError(400, "'%s' not a string type" % (s,))
# Depending on the room version, ensure the data is spec compliant JSON.
@@ -76,87 +74,34 @@ class EventValidator(object):
)
if event.type == EventTypes.Retention:
- self._validate_retention(event, config)
+ self._validate_retention(event)
- def _validate_retention(self, event, config):
+ def _validate_retention(self, event):
"""Checks that an event that defines the retention policy for a room respects the
- boundaries imposed by the server's administrator.
+ format enforced by the spec.
Args:
event (FrozenEvent): The event to validate.
- config (Config): The homeserver's configuration.
"""
min_lifetime = event.content.get("min_lifetime")
max_lifetime = event.content.get("max_lifetime")
if min_lifetime is not None:
- if not isinstance(min_lifetime, integer_types):
+ if not isinstance(min_lifetime, int):
raise SynapseError(
code=400,
msg="'min_lifetime' must be an integer",
errcode=Codes.BAD_JSON,
)
- if (
- config.retention_allowed_lifetime_min is not None
- and min_lifetime < config.retention_allowed_lifetime_min
- ):
- raise SynapseError(
- code=400,
- msg=(
- "'min_lifetime' can't be lower than the minimum allowed"
- " value enforced by the server's administrator"
- ),
- errcode=Codes.BAD_JSON,
- )
-
- if (
- config.retention_allowed_lifetime_max is not None
- and min_lifetime > config.retention_allowed_lifetime_max
- ):
- raise SynapseError(
- code=400,
- msg=(
- "'min_lifetime' can't be greater than the maximum allowed"
- " value enforced by the server's administrator"
- ),
- errcode=Codes.BAD_JSON,
- )
-
if max_lifetime is not None:
- if not isinstance(max_lifetime, integer_types):
+ if not isinstance(max_lifetime, int):
raise SynapseError(
code=400,
msg="'max_lifetime' must be an integer",
errcode=Codes.BAD_JSON,
)
- if (
- config.retention_allowed_lifetime_min is not None
- and max_lifetime < config.retention_allowed_lifetime_min
- ):
- raise SynapseError(
- code=400,
- msg=(
- "'max_lifetime' can't be lower than the minimum allowed value"
- " enforced by the server's administrator"
- ),
- errcode=Codes.BAD_JSON,
- )
-
- if (
- config.retention_allowed_lifetime_max is not None
- and max_lifetime > config.retention_allowed_lifetime_max
- ):
- raise SynapseError(
- code=400,
- msg=(
- "'max_lifetime' can't be greater than the maximum allowed"
- " value enforced by the server's administrator"
- ),
- errcode=Codes.BAD_JSON,
- )
-
if (
min_lifetime is not None
and max_lifetime is not None
@@ -183,7 +128,7 @@ class EventValidator(object):
strings.append("state_key")
for s in strings:
- if not isinstance(getattr(event, s), string_types):
+ if not isinstance(getattr(event, s), str):
raise SynapseError(400, "Not '%s' a string type" % (s,))
RoomID.from_string(event.room_id)
@@ -223,7 +168,7 @@ class EventValidator(object):
for s in keys:
if s not in d:
raise SynapseError(400, "'%s' not in content" % (s,))
- if not isinstance(d[s], string_types):
+ if not isinstance(d[s], str):
raise SynapseError(400, "'%s' not a string type" % (s,))
def _ensure_state_event(self, event):
|