2014-12-02 06:40:22 -05:00
|
|
|
#
|
2023-11-21 15:29:58 -05:00
|
|
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2023 New Vector, Ltd
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# See the GNU Affero General Public License for more details:
|
|
|
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
|
|
#
|
|
|
|
# Originally licensed under the Apache License, Version 2.0:
|
|
|
|
# <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
|
|
#
|
|
|
|
# [This file includes modifications made by New Vector Limited]
|
2014-12-02 06:40:22 -05:00
|
|
|
#
|
|
|
|
#
|
2021-06-22 05:02:53 -04:00
|
|
|
import logging
|
2023-09-13 07:57:19 -04:00
|
|
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
import attr
|
2022-03-29 17:37:50 -04:00
|
|
|
from signedjson.types import SigningKey
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-04-01 05:24:38 -04:00
|
|
|
from synapse.api.constants import MAX_DEPTH
|
|
|
|
from synapse.api.room_versions import (
|
2019-01-25 12:19:31 -05:00
|
|
|
KNOWN_EVENT_FORMAT_VERSIONS,
|
2019-01-29 12:26:24 -05:00
|
|
|
EventFormatVersions,
|
2020-01-29 12:55:48 -05:00
|
|
|
RoomVersion,
|
2019-01-25 12:19:31 -05:00
|
|
|
)
|
|
|
|
from synapse.crypto.event_signing import add_hashes_and_signatures
|
2022-07-18 05:05:30 -04:00
|
|
|
from synapse.event_auth import auth_types_for_event
|
2024-01-08 09:06:48 -05:00
|
|
|
from synapse.events import EventBase, make_event_from_dict
|
2020-08-12 10:05:50 -04:00
|
|
|
from synapse.state import StateHandler
|
|
|
|
from synapse.storage.databases.main import DataStore
|
2024-01-08 09:06:48 -05:00
|
|
|
from synapse.synapse_rust.events import EventInternalMetadata
|
2023-09-13 07:57:19 -04:00
|
|
|
from synapse.types import EventID, JsonDict, StrCollection
|
2022-12-12 11:19:30 -05:00
|
|
|
from synapse.types.state import StateFilter
|
2020-01-29 12:58:01 -05:00
|
|
|
from synapse.util import Clock
|
2014-12-02 06:40:22 -05:00
|
|
|
from synapse.util.stringutils import random_string
|
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
if TYPE_CHECKING:
|
2021-07-01 14:25:37 -04:00
|
|
|
from synapse.handlers.event_auth import EventAuthHandler
|
2021-06-30 07:08:42 -04:00
|
|
|
from synapse.server import HomeServer
|
|
|
|
|
2021-06-22 05:02:53 -04:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
@attr.s(slots=True, cmp=False, frozen=True, auto_attribs=True)
|
2020-09-04 06:54:56 -04:00
|
|
|
class EventBuilder:
|
2019-01-25 12:19:31 -05:00
|
|
|
"""A format independent event builder used to build up the event content
|
|
|
|
before signing the event.
|
2019-01-23 15:21:33 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
(Note that while objects of this class are frozen, the
|
|
|
|
content/unsigned/internal_metadata fields are still mutable)
|
2019-01-23 15:21:33 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
Attributes:
|
2020-01-29 12:55:48 -05:00
|
|
|
room_version: Version of the target room
|
2020-08-12 10:05:50 -04:00
|
|
|
room_id
|
|
|
|
type
|
|
|
|
sender
|
|
|
|
content
|
|
|
|
unsigned
|
|
|
|
internal_metadata
|
2019-01-25 12:19:31 -05:00
|
|
|
|
2020-08-12 10:05:50 -04:00
|
|
|
_state
|
|
|
|
_auth
|
|
|
|
_store
|
|
|
|
_clock
|
|
|
|
_hostname: The hostname of the server creating the event
|
2019-01-25 12:19:31 -05:00
|
|
|
_signing_key: The signing key to use to sign the event as the server
|
2019-01-23 15:21:33 -05:00
|
|
|
"""
|
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
_state: StateHandler
|
2021-07-01 14:25:37 -04:00
|
|
|
_event_auth_handler: "EventAuthHandler"
|
2021-06-30 07:08:42 -04:00
|
|
|
_store: DataStore
|
|
|
|
_clock: Clock
|
|
|
|
_hostname: str
|
|
|
|
_signing_key: SigningKey
|
2019-01-25 12:19:31 -05:00
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
room_version: RoomVersion
|
2019-01-25 12:19:31 -05:00
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
room_id: str
|
|
|
|
type: str
|
|
|
|
sender: str
|
2019-01-25 12:19:31 -05:00
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
content: JsonDict = attr.Factory(dict)
|
|
|
|
unsigned: JsonDict = attr.Factory(dict)
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
# These only exist on a subset of events, so they raise AttributeError if
|
|
|
|
# someone tries to get them when they don't exist.
|
2021-06-30 07:08:42 -04:00
|
|
|
_state_key: Optional[str] = None
|
|
|
|
_redacts: Optional[str] = None
|
|
|
|
_origin_server_ts: Optional[int] = None
|
2019-01-25 12:19:31 -05:00
|
|
|
|
2024-01-08 09:06:48 -05:00
|
|
|
internal_metadata: EventInternalMetadata = attr.Factory(
|
|
|
|
lambda: EventInternalMetadata({})
|
2019-06-20 05:32:02 -04:00
|
|
|
)
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
@property
|
2021-10-13 07:24:07 -04:00
|
|
|
def state_key(self) -> str:
|
2019-01-25 12:19:31 -05:00
|
|
|
if self._state_key is not None:
|
|
|
|
return self._state_key
|
|
|
|
|
|
|
|
raise AttributeError("state_key")
|
|
|
|
|
2021-10-13 07:24:07 -04:00
|
|
|
def is_state(self) -> bool:
|
2019-01-25 12:19:31 -05:00
|
|
|
return self._state_key is not None
|
2019-01-23 15:21:33 -05:00
|
|
|
|
2020-10-13 18:14:35 -04:00
|
|
|
async def build(
|
2021-02-16 17:32:34 -05:00
|
|
|
self,
|
2023-09-18 09:29:05 -04:00
|
|
|
prev_event_ids: List[str],
|
2021-02-16 17:32:34 -05:00
|
|
|
auth_event_ids: Optional[List[str]],
|
2021-06-22 05:02:53 -04:00
|
|
|
depth: Optional[int] = None,
|
2020-10-13 18:14:35 -04:00
|
|
|
) -> EventBase:
|
2019-01-25 12:19:31 -05:00
|
|
|
"""Transform into a fully signed and hashed event
|
2014-12-11 08:25:19 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
Args:
|
2020-09-01 08:39:04 -04:00
|
|
|
prev_event_ids: The event IDs to use as the prev events
|
2020-10-13 18:14:35 -04:00
|
|
|
auth_event_ids: The event IDs to use as the auth events.
|
|
|
|
Should normally be set to None, which will cause them to be calculated
|
|
|
|
based on the room state at the prev_events.
|
2021-06-22 05:02:53 -04:00
|
|
|
depth: Override the depth used to order the event in the DAG.
|
|
|
|
Should normally be set to None, which will cause the depth to be calculated
|
|
|
|
based on the prev_events.
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
Returns:
|
2020-09-01 08:39:04 -04:00
|
|
|
The signed and hashed event.
|
2019-01-25 12:19:31 -05:00
|
|
|
"""
|
2020-10-13 18:14:35 -04:00
|
|
|
if auth_event_ids is None:
|
2022-07-15 08:20:47 -04:00
|
|
|
state_ids = await self._state.compute_state_after_events(
|
2022-07-18 05:05:30 -04:00
|
|
|
self.room_id,
|
|
|
|
prev_event_ids,
|
|
|
|
state_filter=StateFilter.from_types(
|
|
|
|
auth_types_for_event(self.room_version, self)
|
|
|
|
),
|
2022-11-17 11:01:14 -05:00
|
|
|
await_full_state=False,
|
2020-10-13 18:14:35 -04:00
|
|
|
)
|
2021-07-01 14:25:37 -04:00
|
|
|
auth_event_ids = self._event_auth_handler.compute_auth_events(
|
|
|
|
self, state_ids
|
|
|
|
)
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2020-01-29 12:55:48 -05:00
|
|
|
format_version = self.room_version.event_format
|
2021-11-11 12:04:44 -05:00
|
|
|
# The types of auth/prev events changes between event versions.
|
2023-09-13 07:57:19 -04:00
|
|
|
prev_events: Union[StrCollection, List[Tuple[str, Dict[str, str]]]]
|
2021-11-11 12:04:44 -05:00
|
|
|
auth_events: Union[List[str], List[Tuple[str, Dict[str, str]]]]
|
2022-09-07 06:08:20 -04:00
|
|
|
if format_version == EventFormatVersions.ROOM_V1_V2:
|
2021-11-11 12:04:44 -05:00
|
|
|
auth_events = await self._store.add_event_hashes(auth_event_ids)
|
|
|
|
prev_events = await self._store.add_event_hashes(prev_event_ids)
|
2019-01-29 12:26:24 -05:00
|
|
|
else:
|
2020-10-13 18:14:35 -04:00
|
|
|
auth_events = auth_event_ids
|
2019-01-29 12:26:24 -05:00
|
|
|
prev_events = prev_event_ids
|
2017-01-13 08:16:54 -05:00
|
|
|
|
2021-06-22 05:02:53 -04:00
|
|
|
# Otherwise, progress the depth as normal
|
|
|
|
if depth is None:
|
|
|
|
(
|
|
|
|
_,
|
|
|
|
most_recent_prev_event_depth,
|
|
|
|
) = await self._store.get_max_depth_of(prev_event_ids)
|
|
|
|
|
|
|
|
depth = most_recent_prev_event_depth + 1
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
# we cap depth of generated events, to ensure that they are not
|
|
|
|
# rejected by other servers (and so that they can be persisted in
|
|
|
|
# the db)
|
|
|
|
depth = min(depth, MAX_DEPTH)
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2021-07-15 06:02:43 -04:00
|
|
|
event_dict: Dict[str, Any] = {
|
2019-01-25 12:19:31 -05:00
|
|
|
"auth_events": auth_events,
|
|
|
|
"prev_events": prev_events,
|
|
|
|
"type": self.type,
|
|
|
|
"room_id": self.room_id,
|
|
|
|
"sender": self.sender,
|
|
|
|
"content": self.content,
|
|
|
|
"unsigned": self.unsigned,
|
|
|
|
"depth": depth,
|
2021-07-15 06:02:43 -04:00
|
|
|
}
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
if self.is_state():
|
|
|
|
event_dict["state_key"] = self._state_key
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2023-04-13 09:47:07 -04:00
|
|
|
# MSC2174 moves the redacts property to the content, it is invalid to
|
|
|
|
# provide it as a top-level property.
|
2023-07-18 08:44:59 -04:00
|
|
|
if self._redacts is not None and not self.room_version.updated_redaction_rules:
|
2019-01-25 12:19:31 -05:00
|
|
|
event_dict["redacts"] = self._redacts
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-05-24 09:44:04 -04:00
|
|
|
if self._origin_server_ts is not None:
|
|
|
|
event_dict["origin_server_ts"] = self._origin_server_ts
|
|
|
|
|
2019-07-23 09:00:55 -04:00
|
|
|
return create_local_event_from_event_dict(
|
|
|
|
clock=self._clock,
|
|
|
|
hostname=self._hostname,
|
|
|
|
signing_key=self._signing_key,
|
2020-01-29 12:58:01 -05:00
|
|
|
room_version=self.room_version,
|
2019-07-23 09:00:55 -04:00
|
|
|
event_dict=event_dict,
|
|
|
|
internal_metadata_dict=self.internal_metadata.get_dict(),
|
2019-01-25 12:19:31 -05:00
|
|
|
)
|
2014-12-02 06:40:22 -05:00
|
|
|
|
|
|
|
|
2020-09-04 06:54:56 -04:00
|
|
|
class EventBuilderFactory:
|
2021-06-30 07:08:42 -04:00
|
|
|
def __init__(self, hs: "HomeServer"):
|
2019-01-25 12:19:31 -05:00
|
|
|
self.clock = hs.get_clock()
|
|
|
|
self.hostname = hs.hostname
|
2020-07-08 12:51:56 -04:00
|
|
|
self.signing_key = hs.signing_key
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2022-02-23 06:04:02 -05:00
|
|
|
self.store = hs.get_datastores().main
|
2019-01-25 12:19:31 -05:00
|
|
|
self.state = hs.get_state_handler()
|
2021-07-01 14:25:37 -04:00
|
|
|
self._event_auth_handler = hs.get_event_auth_handler()
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
def for_room_version(
|
|
|
|
self, room_version: RoomVersion, key_values: dict
|
|
|
|
) -> EventBuilder:
|
2019-04-01 05:24:38 -04:00
|
|
|
"""Generate an event builder appropriate for the given room version
|
|
|
|
|
|
|
|
Args:
|
2021-06-30 07:08:42 -04:00
|
|
|
room_version:
|
2019-04-01 05:24:38 -04:00
|
|
|
Version of the room that we're creating an event builder for
|
2021-06-30 07:08:42 -04:00
|
|
|
key_values: Fields used as the basis of the new event
|
2019-04-01 05:24:38 -04:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
EventBuilder
|
|
|
|
"""
|
2019-01-25 12:19:31 -05:00
|
|
|
return EventBuilder(
|
|
|
|
store=self.store,
|
|
|
|
state=self.state,
|
2021-07-01 14:25:37 -04:00
|
|
|
event_auth_handler=self._event_auth_handler,
|
2019-01-25 12:19:31 -05:00
|
|
|
clock=self.clock,
|
|
|
|
hostname=self.hostname,
|
|
|
|
signing_key=self.signing_key,
|
2020-01-29 12:55:48 -05:00
|
|
|
room_version=room_version,
|
2019-01-25 12:19:31 -05:00
|
|
|
type=key_values["type"],
|
|
|
|
state_key=key_values.get("state_key"),
|
|
|
|
room_id=key_values["room_id"],
|
|
|
|
sender=key_values["sender"],
|
|
|
|
content=key_values.get("content", {}),
|
|
|
|
unsigned=key_values.get("unsigned", {}),
|
|
|
|
redacts=key_values.get("redacts", None),
|
2019-05-24 09:44:04 -04:00
|
|
|
origin_server_ts=key_values.get("origin_server_ts", None),
|
2019-01-25 12:19:31 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-06-20 05:32:02 -04:00
|
|
|
def create_local_event_from_event_dict(
|
2020-01-29 12:58:01 -05:00
|
|
|
clock: Clock,
|
|
|
|
hostname: str,
|
|
|
|
signing_key: SigningKey,
|
|
|
|
room_version: RoomVersion,
|
|
|
|
event_dict: JsonDict,
|
|
|
|
internal_metadata_dict: Optional[JsonDict] = None,
|
|
|
|
) -> EventBase:
|
2019-01-25 12:19:31 -05:00
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
|
2020-01-29 12:58:01 -05:00
|
|
|
format_version = room_version.event_format
|
2019-01-25 12:19:31 -05:00
|
|
|
if format_version not in KNOWN_EVENT_FORMAT_VERSIONS:
|
2019-06-20 05:32:02 -04:00
|
|
|
raise Exception("No event format defined for version %r" % (format_version,))
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
if internal_metadata_dict is None:
|
|
|
|
internal_metadata_dict = {}
|
|
|
|
|
|
|
|
time_now = int(clock.time_msec())
|
|
|
|
|
2022-09-07 06:08:20 -04:00
|
|
|
if format_version == EventFormatVersions.ROOM_V1_V2:
|
2019-01-29 12:26:24 -05:00
|
|
|
event_dict["event_id"] = _create_event_id(clock, hostname)
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
event_dict["origin"] = hostname
|
2019-05-24 09:44:04 -04:00
|
|
|
event_dict.setdefault("origin_server_ts", time_now)
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
event_dict.setdefault("unsigned", {})
|
|
|
|
age = event_dict["unsigned"].pop("age", 0)
|
|
|
|
event_dict["unsigned"].setdefault("age_ts", time_now - age)
|
|
|
|
|
|
|
|
event_dict.setdefault("signatures", {})
|
|
|
|
|
2020-01-29 12:40:33 -05:00
|
|
|
add_hashes_and_signatures(room_version, event_dict, hostname, signing_key)
|
2020-02-07 10:30:04 -05:00
|
|
|
return make_event_from_dict(
|
|
|
|
event_dict, room_version, internal_metadata_dict=internal_metadata_dict
|
2019-01-25 12:19:31 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# A counter used when generating new event IDs
|
|
|
|
_event_id_counter = 0
|
|
|
|
|
|
|
|
|
2021-06-30 07:08:42 -04:00
|
|
|
def _create_event_id(clock: Clock, hostname: str) -> str:
|
2019-01-25 12:19:31 -05:00
|
|
|
"""Create a new event ID
|
|
|
|
|
|
|
|
Args:
|
2021-06-30 07:08:42 -04:00
|
|
|
clock
|
|
|
|
hostname: The server name for the event ID
|
2019-01-25 12:19:31 -05:00
|
|
|
|
|
|
|
Returns:
|
2021-06-30 07:08:42 -04:00
|
|
|
The new event ID
|
2019-01-25 12:19:31 -05:00
|
|
|
"""
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
global _event_id_counter
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
i = str(_event_id_counter)
|
|
|
|
_event_id_counter += 1
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
local_part = str(int(clock.time())) + i + random_string(5)
|
2014-12-02 06:40:22 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
e_id = EventID(local_part, hostname)
|
2014-12-10 05:06:12 -05:00
|
|
|
|
2019-01-25 12:19:31 -05:00
|
|
|
return e_id.to_string()
|