Merge branch 'erikj/smaller_events' into erikj/test_send
This commit is contained in:
@@ -23,6 +23,7 @@ from jsonschema import FormatChecker
|
||||
from synapse.api.constants import EventContentFields
|
||||
from synapse.api.errors import SynapseError
|
||||
from synapse.api.presence import UserPresenceState
|
||||
from synapse.events import EventBase
|
||||
from synapse.types import RoomID, UserID
|
||||
|
||||
FILTER_SCHEMA = {
|
||||
@@ -290,6 +291,13 @@ class Filter:
|
||||
ev_type = "m.presence"
|
||||
contains_url = False
|
||||
labels = [] # type: List[str]
|
||||
elif isinstance(event, EventBase):
|
||||
sender = event.sender
|
||||
room_id = event.room_id
|
||||
ev_type = event.type
|
||||
content = event.content
|
||||
contains_url = isinstance(content.get("url"), str)
|
||||
labels = content.get(EventContentFields.LABELS, [])
|
||||
else:
|
||||
sender = event.get("sender", None)
|
||||
if not sender:
|
||||
|
||||
@@ -48,7 +48,7 @@ def check_event_content_hash(
|
||||
|
||||
# some malformed events lack a 'hashes'. Protect against it being missing
|
||||
# or a weird type by basically treating it the same as an unhashed event.
|
||||
hashes = event.get("hashes")
|
||||
hashes = getattr(event, "hashes", None)
|
||||
# nb it might be a frozendict or a dict
|
||||
if not isinstance(hashes, collections.abc.Mapping):
|
||||
raise SynapseError(
|
||||
|
||||
@@ -418,7 +418,9 @@ def get_send_level(
|
||||
def _can_send_event(event: EventBase, auth_events: StateMap[EventBase]) -> bool:
|
||||
power_levels_event = _get_power_level_event(auth_events)
|
||||
|
||||
send_level = get_send_level(event.type, event.get("state_key"), power_levels_event)
|
||||
send_level = get_send_level(
|
||||
event.type, getattr(event, "state_key", None), power_levels_event
|
||||
)
|
||||
user_level = get_user_power_level(event.user_id, auth_events)
|
||||
|
||||
if user_level < send_level:
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
import abc
|
||||
import os
|
||||
from typing import Dict, Optional, Tuple, Type
|
||||
import zlib
|
||||
from typing import Dict, List, Optional, Tuple, Type, Union
|
||||
|
||||
from unpaddedbase64 import encode_base64
|
||||
import attr
|
||||
from unpaddedbase64 import decode_base64, encode_base64
|
||||
|
||||
from synapse.api.room_versions import EventFormatVersions, RoomVersion, RoomVersions
|
||||
from synapse.types import JsonDict, RoomStreamToken
|
||||
from synapse.util import json_decoder, json_encoder
|
||||
from synapse.util.caches import intern_dict
|
||||
from synapse.util.frozenutils import freeze
|
||||
from synapse.util.stringutils import strtobool
|
||||
@@ -37,6 +40,26 @@ from synapse.util.stringutils import strtobool
|
||||
USE_FROZEN_DICTS = strtobool(os.environ.get("SYNAPSE_USE_FROZEN_DICTS", "0"))
|
||||
|
||||
|
||||
_PRESET_ZDICT = b"""{"auth_events":[],"prev_events":[],"type":"m.room.member",m.room.message"room_id":,"sender":,"content":{"msgtype":"m.text","body":""room_version":"creator":"depth":"prev_state":"state_key":""origin":"origin_server_ts":"hashes":{"sha256":"signatures":,"unsigned":{"age_ts":"ed25519"""
|
||||
|
||||
|
||||
def _encode_dict(d: JsonDict) -> bytes:
|
||||
json_bytes = json_encoder.encode(d).encode("utf-8")
|
||||
c = zlib.compressobj(1, zdict=_PRESET_ZDICT)
|
||||
result_bytes = c.compress(json_bytes)
|
||||
result_bytes += c.flush()
|
||||
return result_bytes
|
||||
|
||||
|
||||
def _decode_dict(b: bytes) -> JsonDict:
|
||||
d = zlib.decompressobj(zdict=_PRESET_ZDICT)
|
||||
|
||||
result_bytes = d.decompress(b)
|
||||
result_bytes += d.flush()
|
||||
|
||||
return json_decoder.decode(result_bytes.decode("utf-8"))
|
||||
|
||||
|
||||
class DictProperty:
|
||||
"""An object property which delegates to the `_dict` within its parent object."""
|
||||
|
||||
@@ -205,7 +228,81 @@ class _EventInternalMetadata:
|
||||
return self._dict.get("redacted", False)
|
||||
|
||||
|
||||
@attr.s(slots=True, auto_attribs=True)
|
||||
class _Signatures:
|
||||
_signatures_bytes: bytes
|
||||
|
||||
@staticmethod
|
||||
def from_dict(signature_dict: JsonDict) -> "_Signatures":
|
||||
return _Signatures(_encode_dict(signature_dict))
|
||||
|
||||
def get_dict(self) -> JsonDict:
|
||||
return _decode_dict(self._signatures_bytes)
|
||||
|
||||
def get(self, server_name):
|
||||
return self.get_dict().get(server_name)
|
||||
|
||||
def update(self, other: Union[JsonDict, "_Signatures"]):
|
||||
if isinstance(other, _Signatures):
|
||||
other_dict = _decode_dict(other._signatures_bytes)
|
||||
else:
|
||||
other_dict = other
|
||||
|
||||
signatures = self.get_dict()
|
||||
signatures.update(other_dict)
|
||||
self._signatures_bytes = _encode_dict(signatures)
|
||||
|
||||
|
||||
class _SmallListV1(str):
|
||||
__slots__ = []
|
||||
|
||||
def get(self):
|
||||
return self.split(",")
|
||||
|
||||
@staticmethod
|
||||
def create(event_ids):
|
||||
return _SmallListV1(",".join(event_ids))
|
||||
|
||||
|
||||
class _SmallListV2_V3(bytes):
|
||||
__slots__ = []
|
||||
|
||||
def get(self, url_safe):
|
||||
i = 0
|
||||
while i * 32 < len(self):
|
||||
bit = self[i * 32 : (i + 1) * 32]
|
||||
i += 1
|
||||
yield "$" + encode_base64(bit, urlsafe=url_safe)
|
||||
|
||||
@staticmethod
|
||||
def create(event_ids):
|
||||
return _SmallListV2_V3(
|
||||
b"".join(decode_base64(event_id[1:]) for event_id in event_ids)
|
||||
)
|
||||
|
||||
|
||||
class EventBase(metaclass=abc.ABCMeta):
|
||||
__slots__ = [
|
||||
"room_version",
|
||||
"signatures",
|
||||
"unsigned",
|
||||
"rejected_reason",
|
||||
"_encoded_dict",
|
||||
"_auth_event_ids",
|
||||
"depth",
|
||||
"_content",
|
||||
"_hashes",
|
||||
"origin",
|
||||
"origin_server_ts",
|
||||
"_prev_event_ids",
|
||||
"redacts",
|
||||
"room_id",
|
||||
"sender",
|
||||
"type",
|
||||
"state_key",
|
||||
"internal_metadata",
|
||||
]
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def format_version(self) -> int:
|
||||
@@ -224,32 +321,44 @@ class EventBase(metaclass=abc.ABCMeta):
|
||||
assert room_version.event_format == self.format_version
|
||||
|
||||
self.room_version = room_version
|
||||
self.signatures = signatures
|
||||
self.signatures = _Signatures.from_dict(signatures)
|
||||
self.unsigned = unsigned
|
||||
self.rejected_reason = rejected_reason
|
||||
|
||||
self._dict = event_dict
|
||||
self._encoded_dict = _encode_dict(event_dict)
|
||||
|
||||
self.depth = event_dict["depth"]
|
||||
self.origin = event_dict["origin"]
|
||||
self.origin_server_ts = event_dict["origin_server_ts"]
|
||||
self.redacts = event_dict.get("redacts")
|
||||
self.room_id = event_dict["room_id"]
|
||||
self.sender = event_dict["sender"]
|
||||
self.type = event_dict["type"]
|
||||
if "state_key" in event_dict:
|
||||
self.state_key = event_dict["state_key"]
|
||||
|
||||
self.internal_metadata = _EventInternalMetadata(internal_metadata_dict)
|
||||
|
||||
auth_events = DictProperty("auth_events")
|
||||
depth = DictProperty("depth")
|
||||
content = DictProperty("content")
|
||||
hashes = DictProperty("hashes")
|
||||
origin = DictProperty("origin")
|
||||
origin_server_ts = DictProperty("origin_server_ts")
|
||||
prev_events = DictProperty("prev_events")
|
||||
redacts = DefaultDictProperty("redacts", None)
|
||||
room_id = DictProperty("room_id")
|
||||
sender = DictProperty("sender")
|
||||
state_key = DictProperty("state_key")
|
||||
type = DictProperty("type")
|
||||
user_id = DictProperty("sender")
|
||||
@property
|
||||
def content(self) -> JsonDict:
|
||||
return self.get_dict()["content"]
|
||||
|
||||
@property
|
||||
def hashes(self) -> JsonDict:
|
||||
return self.get_dict()["hashes"]
|
||||
|
||||
@property
|
||||
def prev_events(self) -> List[str]:
|
||||
return list(self._prev_events)
|
||||
|
||||
@property
|
||||
def event_id(self) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def user_id(self) -> str:
|
||||
return self.sender
|
||||
|
||||
@property
|
||||
def membership(self):
|
||||
return self.content["membership"]
|
||||
@@ -258,17 +367,13 @@ class EventBase(metaclass=abc.ABCMeta):
|
||||
return hasattr(self, "state_key") and self.state_key is not None
|
||||
|
||||
def get_dict(self) -> JsonDict:
|
||||
d = dict(self._dict)
|
||||
d.update({"signatures": self.signatures, "unsigned": dict(self.unsigned)})
|
||||
d = _decode_dict(self._encoded_dict)
|
||||
d.update(
|
||||
{"signatures": self.signatures.get_dict(), "unsigned": dict(self.unsigned)}
|
||||
)
|
||||
|
||||
return d
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._dict.get(key, default)
|
||||
|
||||
def get_internal_metadata_dict(self):
|
||||
return self.internal_metadata.get_dict()
|
||||
|
||||
def get_pdu_json(self, time_now=None) -> JsonDict:
|
||||
pdu_json = self.get_dict()
|
||||
|
||||
@@ -285,41 +390,11 @@ class EventBase(metaclass=abc.ABCMeta):
|
||||
def __set__(self, instance, value):
|
||||
raise AttributeError("Unrecognized attribute %s" % (instance,))
|
||||
|
||||
def __getitem__(self, field):
|
||||
return self._dict[field]
|
||||
|
||||
def __contains__(self, field):
|
||||
return field in self._dict
|
||||
|
||||
def items(self):
|
||||
return list(self._dict.items())
|
||||
|
||||
def keys(self):
|
||||
return self._dict.keys()
|
||||
|
||||
def prev_event_ids(self):
|
||||
"""Returns the list of prev event IDs. The order matches the order
|
||||
specified in the event, though there is no meaning to it.
|
||||
|
||||
Returns:
|
||||
list[str]: The list of event IDs of this event's prev_events
|
||||
"""
|
||||
return [e for e, _ in self.prev_events]
|
||||
|
||||
def auth_event_ids(self):
|
||||
"""Returns the list of auth event IDs. The order matches the order
|
||||
specified in the event, though there is no meaning to it.
|
||||
|
||||
Returns:
|
||||
list[str]: The list of event IDs of this event's auth_events
|
||||
"""
|
||||
return [e for e, _ in self.auth_events]
|
||||
|
||||
def freeze(self):
|
||||
"""'Freeze' the event dict, so it cannot be modified by accident"""
|
||||
|
||||
# this will be a no-op if the event dict is already frozen.
|
||||
self._dict = freeze(self._dict)
|
||||
# self._dict = freeze(self._dict)
|
||||
|
||||
|
||||
class FrozenEvent(EventBase):
|
||||
@@ -355,6 +430,12 @@ class FrozenEvent(EventBase):
|
||||
frozen_dict = event_dict
|
||||
|
||||
self._event_id = event_dict["event_id"]
|
||||
self._auth_event_ids = _SmallListV1.create(
|
||||
e for e, _ in event_dict["auth_events"]
|
||||
)
|
||||
self._prev_event_ids = _SmallListV1.create(
|
||||
e for e, _ in event_dict["prev_events"]
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
frozen_dict,
|
||||
@@ -369,18 +450,26 @@ class FrozenEvent(EventBase):
|
||||
def event_id(self) -> str:
|
||||
return self._event_id
|
||||
|
||||
def auth_event_ids(self):
|
||||
return list(self._auth_event_ids.get())
|
||||
|
||||
def prev_event_ids(self):
|
||||
return list(self._prev_event_ids.get())
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
return "<FrozenEvent event_id=%r, type=%r, state_key=%r>" % (
|
||||
self.get("event_id", None),
|
||||
self.get("type", None),
|
||||
self.get("state_key", None),
|
||||
self.event_id,
|
||||
self.type,
|
||||
getattr(self, "state_key", None),
|
||||
)
|
||||
|
||||
|
||||
class FrozenEventV2(EventBase):
|
||||
__slots__ = ["_event_id"]
|
||||
|
||||
format_version = EventFormatVersions.V2 # All events of this type are V2
|
||||
|
||||
def __init__(
|
||||
@@ -415,6 +504,8 @@ class FrozenEventV2(EventBase):
|
||||
frozen_dict = event_dict
|
||||
|
||||
self._event_id = None
|
||||
self._auth_event_ids = _SmallListV2_V3.create(event_dict["auth_events"])
|
||||
self._prev_event_ids = _SmallListV2_V3.create(event_dict["prev_events"])
|
||||
|
||||
super().__init__(
|
||||
frozen_dict,
|
||||
@@ -436,24 +527,6 @@ class FrozenEventV2(EventBase):
|
||||
self._event_id = "$" + encode_base64(compute_event_reference_hash(self)[1])
|
||||
return self._event_id
|
||||
|
||||
def prev_event_ids(self):
|
||||
"""Returns the list of prev event IDs. The order matches the order
|
||||
specified in the event, though there is no meaning to it.
|
||||
|
||||
Returns:
|
||||
list[str]: The list of event IDs of this event's prev_events
|
||||
"""
|
||||
return self.prev_events
|
||||
|
||||
def auth_event_ids(self):
|
||||
"""Returns the list of auth event IDs. The order matches the order
|
||||
specified in the event, though there is no meaning to it.
|
||||
|
||||
Returns:
|
||||
list[str]: The list of event IDs of this event's auth_events
|
||||
"""
|
||||
return self.auth_events
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
@@ -461,14 +534,22 @@ class FrozenEventV2(EventBase):
|
||||
return "<%s event_id=%r, type=%r, state_key=%r>" % (
|
||||
self.__class__.__name__,
|
||||
self.event_id,
|
||||
self.get("type", None),
|
||||
self.get("state_key", None),
|
||||
self.type,
|
||||
self.state_key if self.is_state() else None,
|
||||
)
|
||||
|
||||
def auth_event_ids(self):
|
||||
return list(self._auth_event_ids.get(False))
|
||||
|
||||
def prev_event_ids(self):
|
||||
return list(self._prev_event_ids.get(False))
|
||||
|
||||
|
||||
class FrozenEventV3(FrozenEventV2):
|
||||
"""FrozenEventV3, which differs from FrozenEventV2 only in the event_id format"""
|
||||
|
||||
__slots__ = ["_event_id"]
|
||||
|
||||
format_version = EventFormatVersions.V3 # All events of this type are V3
|
||||
|
||||
@property
|
||||
@@ -484,6 +565,12 @@ class FrozenEventV3(FrozenEventV2):
|
||||
)
|
||||
return self._event_id
|
||||
|
||||
def auth_event_ids(self):
|
||||
return list(self._auth_event_ids.get(True))
|
||||
|
||||
def prev_event_ids(self):
|
||||
return list(self._prev_event_ids.get(True))
|
||||
|
||||
|
||||
def _event_type_from_format_version(format_version: int) -> Type[EventBase]:
|
||||
"""Returns the python type to use to construct an Event object for the
|
||||
|
||||
@@ -38,6 +38,8 @@ class EventValidator:
|
||||
if event.format_version == EventFormatVersions.V1:
|
||||
EventID.from_string(event.event_id)
|
||||
|
||||
event_dict = event.get_dict()
|
||||
|
||||
required = [
|
||||
"auth_events",
|
||||
"content",
|
||||
@@ -49,7 +51,7 @@ class EventValidator:
|
||||
]
|
||||
|
||||
for k in required:
|
||||
if not hasattr(event, k):
|
||||
if k not in event_dict:
|
||||
raise SynapseError(400, "Event does not have key %s" % (k,))
|
||||
|
||||
# Check that the following keys have string values
|
||||
|
||||
@@ -90,9 +90,7 @@ class FederationBase:
|
||||
# received event was probably a redacted copy (but we then use our
|
||||
# *actual* redacted copy to be on the safe side.)
|
||||
redacted_event = prune_event(pdu)
|
||||
if set(redacted_event.keys()) == set(pdu.keys()) and set(
|
||||
redacted_event.content.keys()
|
||||
) == set(pdu.content.keys()):
|
||||
if set(redacted_event.content.keys()) == set(pdu.content.keys()):
|
||||
logger.info(
|
||||
"Event %s seems to have been redacted; using our redacted "
|
||||
"copy",
|
||||
|
||||
@@ -1108,7 +1108,7 @@ class EventCreationHandler:
|
||||
# it's not a self-redaction (to avoid having to look up whether the
|
||||
# user is actually admin or not).
|
||||
is_admin_redaction = False
|
||||
if event.type == EventTypes.Redaction:
|
||||
if event.type == EventTypes.Redaction and event.redacts:
|
||||
original_event = await self.store.get_event(
|
||||
event.redacts,
|
||||
redact_behaviour=EventRedactBehaviour.AS_IS,
|
||||
@@ -1195,7 +1195,7 @@ class EventCreationHandler:
|
||||
# TODO: Make sure the signatures actually are correct.
|
||||
event.signatures.update(returned_invite.signatures)
|
||||
|
||||
if event.type == EventTypes.Redaction:
|
||||
if event.type == EventTypes.Redaction and event.redacts:
|
||||
original_event = await self.store.get_event(
|
||||
event.redacts,
|
||||
redact_behaviour=EventRedactBehaviour.AS_IS,
|
||||
@@ -1401,7 +1401,7 @@ class EventCreationHandler:
|
||||
]
|
||||
|
||||
for k in immutable_fields:
|
||||
if getattr(builder, k, None) != original_event.get(k):
|
||||
if getattr(builder, k, None) != getattr(original_event, k, None):
|
||||
raise Exception(
|
||||
"Third party rules module created an invalid event: "
|
||||
"cannot change field " + k
|
||||
|
||||
@@ -475,7 +475,7 @@ class RoomCreationHandler(BaseHandler):
|
||||
):
|
||||
await self.room_member_handler.update_membership(
|
||||
requester,
|
||||
UserID.from_string(old_event["state_key"]),
|
||||
UserID.from_string(old_event.state_key),
|
||||
new_room_id,
|
||||
"ban",
|
||||
ratelimit=False,
|
||||
|
||||
@@ -277,7 +277,7 @@ class Notifier:
|
||||
event_pos=event_pos,
|
||||
room_id=event.room_id,
|
||||
event_type=event.type,
|
||||
state_key=event.get("state_key"),
|
||||
state_key=getattr(event, "state_key", None),
|
||||
membership=event.content.get("membership"),
|
||||
max_room_stream_token=max_room_stream_token,
|
||||
extra_users=extra_users or [],
|
||||
|
||||
@@ -125,7 +125,7 @@ class PushRuleEvaluatorForEvent:
|
||||
self._power_levels = power_levels
|
||||
|
||||
# Maps strings of e.g. 'content.body' -> event["content"]["body"]
|
||||
self._value_cache = _flatten_dict(event)
|
||||
self._value_cache = _flatten_dict(event.get_dict())
|
||||
|
||||
def matches(
|
||||
self, condition: Dict[str, Any], user_id: str, display_name: str
|
||||
@@ -271,7 +271,7 @@ def _re_word_boundary(r: str) -> str:
|
||||
|
||||
|
||||
def _flatten_dict(
|
||||
d: Union[EventBase, dict],
|
||||
d: dict,
|
||||
prefix: Optional[List[str]] = None,
|
||||
result: Optional[Dict[str, str]] = None,
|
||||
) -> Dict[str, str]:
|
||||
|
||||
Reference in New Issue
Block a user