diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py
index 88ed6d764f..f813fa2fe7 100644
--- a/synapse/events/__init__.py
+++ b/synapse/events/__init__.py
@@ -23,6 +23,7 @@ from unpaddedbase64 import encode_base64
from synapse.api.errors import UnsupportedRoomVersionError
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, EventFormatVersions
+from synapse.types import JsonDict
from synapse.util.caches import intern_dict
from synapse.util.frozenutils import freeze
@@ -116,16 +117,32 @@ class _EventInternalMetadata(object):
return getattr(self, "redacted", False)
-def _event_dict_property(key):
+_SENTINEL = object()
+
+
+def _event_dict_property(key, default=_SENTINEL):
+ """Creates a new property for the given key that delegates access to
+ `self._event_dict`.
+
+ The default is used if the key is missing from the `_event_dict`, if given,
+ otherwise an AttributeError will be raised.
+
+ Note: If a default is given then `hasattr` will always return true.
+ """
+
# We want to be able to use hasattr with the event dict properties.
# However, (on python3) hasattr expects AttributeError to be raised. Hence,
# we need to transform the KeyError into an AttributeError
- def getter(self):
+
+ def getter_raises(self):
try:
return self._event_dict[key]
except KeyError:
raise AttributeError(key)
+ def getter_default(self):
+ return self._event_dict.get(key, default)
+
def setter(self, v):
try:
self._event_dict[key] = v
@@ -138,7 +155,11 @@ def _event_dict_property(key):
except KeyError:
raise AttributeError(key)
- return property(getter, setter, delete)
+ if default is _SENTINEL:
+ # No default given, so use the getter that raises
+ return property(getter_raises, setter, delete)
+ else:
+ return property(getter_default, setter, delete)
class EventBase(object):
@@ -165,7 +186,7 @@ class EventBase(object):
origin = _event_dict_property("origin")
origin_server_ts = _event_dict_property("origin_server_ts")
prev_events = _event_dict_property("prev_events")
- redacts = _event_dict_property("redacts")
+ redacts = _event_dict_property("redacts", None)
room_id = _event_dict_property("room_id")
sender = _event_dict_property("sender")
user_id = _event_dict_property("sender")
@@ -177,7 +198,7 @@ class EventBase(object):
def is_state(self):
return hasattr(self, "state_key") and self.state_key is not None
- def get_dict(self):
+ def get_dict(self) -> JsonDict:
d = dict(self._event_dict)
d.update({"signatures": self.signatures, "unsigned": dict(self.unsigned)})
@@ -189,7 +210,7 @@ class EventBase(object):
def get_internal_metadata_dict(self):
return self.internal_metadata.get_dict()
- def get_pdu_json(self, time_now=None):
+ def get_pdu_json(self, time_now=None) -> JsonDict:
pdu_json = self.get_dict()
if time_now is not None and "age_ts" in pdu_json["unsigned"]:
diff --git a/synapse/events/builder.py b/synapse/events/builder.py
index 3997751337..8d63ad6dc3 100644
--- a/synapse/events/builder.py
+++ b/synapse/events/builder.py
@@ -12,8 +12,10 @@
# 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
import attr
+from nacl.signing import SigningKey
from twisted.internet import defer
@@ -23,13 +25,18 @@ from synapse.api.room_versions import (
KNOWN_EVENT_FORMAT_VERSIONS,
KNOWN_ROOM_VERSIONS,
EventFormatVersions,
+ RoomVersion,
)
from synapse.crypto.event_signing import add_hashes_and_signatures
-from synapse.types import EventID
+from synapse.events import (
+ EventBase,
+ _EventInternalMetadata,
+ event_type_from_format_version,
+)
+from synapse.types import EventID, JsonDict
+from synapse.util import Clock
from synapse.util.stringutils import random_string
-from . import _EventInternalMetadata, event_type_from_format_version
-
@attr.s(slots=True, cmp=False, frozen=True)
class EventBuilder(object):
@@ -40,7 +47,7 @@ class EventBuilder(object):
content/unsigned/internal_metadata fields are still mutable)
Attributes:
- format_version (int): Event format version
+ room_version: Version of the target room
room_id (str)
type (str)
sender (str)
@@ -63,7 +70,7 @@ class EventBuilder(object):
_hostname = attr.ib()
_signing_key = attr.ib()
- format_version = attr.ib()
+ room_version = attr.ib(type=RoomVersion)
room_id = attr.ib()
type = attr.ib()
@@ -108,7 +115,8 @@ class EventBuilder(object):
)
auth_ids = yield self._auth.compute_auth_events(self, state_ids)
- if self.format_version == EventFormatVersions.V1:
+ 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)
else:
@@ -148,7 +156,7 @@ class EventBuilder(object):
clock=self._clock,
hostname=self._hostname,
signing_key=self._signing_key,
- format_version=self.format_version,
+ room_version=self.room_version,
event_dict=event_dict,
internal_metadata_dict=self.internal_metadata.get_dict(),
)
@@ -201,7 +209,7 @@ class EventBuilderFactory(object):
clock=self.clock,
hostname=self.hostname,
signing_key=self.signing_key,
- format_version=room_version.event_format,
+ room_version=room_version,
type=key_values["type"],
state_key=key_values.get("state_key"),
room_id=key_values["room_id"],
@@ -214,29 +222,19 @@ class EventBuilderFactory(object):
def create_local_event_from_event_dict(
- clock,
- hostname,
- signing_key,
- format_version,
- event_dict,
- internal_metadata_dict=None,
-):
+ clock: Clock,
+ hostname: str,
+ signing_key: SigningKey,
+ room_version: RoomVersion,
+ event_dict: JsonDict,
+ internal_metadata_dict: Optional[JsonDict] = None,
+) -> EventBase:
"""Takes a fully formed event dict, ensuring that fields like `origin`
and `origin_server_ts` have correct values for a locally produced event,
then signs and hashes it.
-
- Args:
- clock (Clock)
- hostname (str)
- signing_key
- format_version (int)
- event_dict (dict)
- internal_metadata_dict (dict|None)
-
- Returns:
- FrozenEvent
"""
+ format_version = room_version.event_format
if format_version not in KNOWN_EVENT_FORMAT_VERSIONS:
raise Exception("No event format defined for version %r" % (format_version,))
@@ -257,7 +255,7 @@ def create_local_event_from_event_dict(
event_dict.setdefault("signatures", {})
- add_hashes_and_signatures(event_dict, hostname, signing_key)
+ add_hashes_and_signatures(room_version, event_dict, hostname, signing_key)
return event_type_from_format_version(format_version)(
event_dict, internal_metadata_dict=internal_metadata_dict
)
diff --git a/synapse/events/utils.py b/synapse/events/utils.py
index 07d1c5bcf0..f70f5032fb 100644
--- a/synapse/events/utils.py
+++ b/synapse/events/utils.py
@@ -12,8 +12,9 @@
# 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 re
+from typing import Mapping, Union
from six import string_types
@@ -422,3 +423,37 @@ class EventClientSerializer(object):
return yieldable_gather_results(
self.serialize_event, events, time_now=time_now, **kwargs
)
+
+
+def copy_power_levels_contents(
+ old_power_levels: Mapping[str, Union[int, Mapping[str, int]]]
+):
+ """Copy the content of a power_levels event, unfreezing frozendicts along the way
+
+ Raises:
+ TypeError if the input does not look like a valid power levels event content
+ """
+ if not isinstance(old_power_levels, collections.Mapping):
+ raise TypeError("Not a valid power-levels content: %r" % (old_power_levels,))
+
+ power_levels = {}
+ for k, v in old_power_levels.items():
+
+ if isinstance(v, int):
+ power_levels[k] = v
+ continue
+
+ if isinstance(v, collections.Mapping):
+ power_levels[k] = h = {}
+ for k1, v1 in v.items():
+ # we should only have one level of nesting
+ if not isinstance(v1, int):
+ raise TypeError(
+ "Invalid power_levels value for %s.%s: %r" % (k, k1, v1)
+ )
+ h[k1] = v1
+ continue
+
+ raise TypeError("Invalid power_levels value for %s: %r" % (k, v))
+
+ return power_levels
|