You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

297 lines
9.6 KiB

  1. # Copyright 2014-2016 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import Any, Dict, List, Optional, Tuple, Union
  15. import attr
  16. from nacl.signing import SigningKey
  17. from synapse.api.auth import Auth
  18. from synapse.api.constants import MAX_DEPTH
  19. from synapse.api.errors import UnsupportedRoomVersionError
  20. from synapse.api.room_versions import (
  21. KNOWN_EVENT_FORMAT_VERSIONS,
  22. KNOWN_ROOM_VERSIONS,
  23. EventFormatVersions,
  24. RoomVersion,
  25. )
  26. from synapse.crypto.event_signing import add_hashes_and_signatures
  27. from synapse.events import EventBase, _EventInternalMetadata, make_event_from_dict
  28. from synapse.state import StateHandler
  29. from synapse.storage.databases.main import DataStore
  30. from synapse.types import EventID, JsonDict
  31. from synapse.util import Clock
  32. from synapse.util.stringutils import random_string
  33. @attr.s(slots=True, cmp=False, frozen=True)
  34. class EventBuilder:
  35. """A format independent event builder used to build up the event content
  36. before signing the event.
  37. (Note that while objects of this class are frozen, the
  38. content/unsigned/internal_metadata fields are still mutable)
  39. Attributes:
  40. room_version: Version of the target room
  41. room_id
  42. type
  43. sender
  44. content
  45. unsigned
  46. internal_metadata
  47. _state
  48. _auth
  49. _store
  50. _clock
  51. _hostname: The hostname of the server creating the event
  52. _signing_key: The signing key to use to sign the event as the server
  53. """
  54. _state = attr.ib(type=StateHandler)
  55. _auth = attr.ib(type=Auth)
  56. _store = attr.ib(type=DataStore)
  57. _clock = attr.ib(type=Clock)
  58. _hostname = attr.ib(type=str)
  59. _signing_key = attr.ib(type=SigningKey)
  60. room_version = attr.ib(type=RoomVersion)
  61. room_id = attr.ib(type=str)
  62. type = attr.ib(type=str)
  63. sender = attr.ib(type=str)
  64. content = attr.ib(default=attr.Factory(dict), type=JsonDict)
  65. unsigned = attr.ib(default=attr.Factory(dict), type=JsonDict)
  66. # These only exist on a subset of events, so they raise AttributeError if
  67. # someone tries to get them when they don't exist.
  68. _state_key = attr.ib(default=None, type=Optional[str])
  69. _redacts = attr.ib(default=None, type=Optional[str])
  70. _origin_server_ts = attr.ib(default=None, type=Optional[int])
  71. internal_metadata = attr.ib(
  72. default=attr.Factory(lambda: _EventInternalMetadata({})),
  73. type=_EventInternalMetadata,
  74. )
  75. @property
  76. def state_key(self):
  77. if self._state_key is not None:
  78. return self._state_key
  79. raise AttributeError("state_key")
  80. def is_state(self):
  81. return self._state_key is not None
  82. async def build(
  83. self,
  84. prev_event_ids: List[str],
  85. auth_event_ids: Optional[List[str]],
  86. ) -> EventBase:
  87. """Transform into a fully signed and hashed event
  88. Args:
  89. prev_event_ids: The event IDs to use as the prev events
  90. auth_event_ids: The event IDs to use as the auth events.
  91. Should normally be set to None, which will cause them to be calculated
  92. based on the room state at the prev_events.
  93. Returns:
  94. The signed and hashed event.
  95. """
  96. if auth_event_ids is None:
  97. state_ids = await self._state.get_current_state_ids(
  98. self.room_id, prev_event_ids
  99. )
  100. auth_event_ids = self._auth.compute_auth_events(self, state_ids)
  101. format_version = self.room_version.event_format
  102. if format_version == EventFormatVersions.V1:
  103. # The types of auth/prev events changes between event versions.
  104. auth_events = await self._store.add_event_hashes(
  105. auth_event_ids
  106. ) # type: Union[List[str], List[Tuple[str, Dict[str, str]]]]
  107. prev_events = await self._store.add_event_hashes(
  108. prev_event_ids
  109. ) # type: Union[List[str], List[Tuple[str, Dict[str, str]]]]
  110. else:
  111. auth_events = auth_event_ids
  112. prev_events = prev_event_ids
  113. old_depth = await self._store.get_max_depth_of(prev_event_ids)
  114. depth = old_depth + 1
  115. # we cap depth of generated events, to ensure that they are not
  116. # rejected by other servers (and so that they can be persisted in
  117. # the db)
  118. depth = min(depth, MAX_DEPTH)
  119. event_dict = {
  120. "auth_events": auth_events,
  121. "prev_events": prev_events,
  122. "type": self.type,
  123. "room_id": self.room_id,
  124. "sender": self.sender,
  125. "content": self.content,
  126. "unsigned": self.unsigned,
  127. "depth": depth,
  128. "prev_state": [],
  129. } # type: Dict[str, Any]
  130. if self.is_state():
  131. event_dict["state_key"] = self._state_key
  132. if self._redacts is not None:
  133. event_dict["redacts"] = self._redacts
  134. if self._origin_server_ts is not None:
  135. event_dict["origin_server_ts"] = self._origin_server_ts
  136. return create_local_event_from_event_dict(
  137. clock=self._clock,
  138. hostname=self._hostname,
  139. signing_key=self._signing_key,
  140. room_version=self.room_version,
  141. event_dict=event_dict,
  142. internal_metadata_dict=self.internal_metadata.get_dict(),
  143. )
  144. class EventBuilderFactory:
  145. def __init__(self, hs):
  146. self.clock = hs.get_clock()
  147. self.hostname = hs.hostname
  148. self.signing_key = hs.signing_key
  149. self.store = hs.get_datastore()
  150. self.state = hs.get_state_handler()
  151. self.auth = hs.get_auth()
  152. def new(self, room_version, key_values):
  153. """Generate an event builder appropriate for the given room version
  154. Deprecated: use for_room_version with a RoomVersion object instead
  155. Args:
  156. room_version (str): Version of the room that we're creating an event builder
  157. for
  158. key_values (dict): Fields used as the basis of the new event
  159. Returns:
  160. EventBuilder
  161. """
  162. v = KNOWN_ROOM_VERSIONS.get(room_version)
  163. if not v:
  164. # this can happen if support is withdrawn for a room version
  165. raise UnsupportedRoomVersionError()
  166. return self.for_room_version(v, key_values)
  167. def for_room_version(self, room_version, key_values):
  168. """Generate an event builder appropriate for the given room version
  169. Args:
  170. room_version (synapse.api.room_versions.RoomVersion):
  171. Version of the room that we're creating an event builder for
  172. key_values (dict): Fields used as the basis of the new event
  173. Returns:
  174. EventBuilder
  175. """
  176. return EventBuilder(
  177. store=self.store,
  178. state=self.state,
  179. auth=self.auth,
  180. clock=self.clock,
  181. hostname=self.hostname,
  182. signing_key=self.signing_key,
  183. room_version=room_version,
  184. type=key_values["type"],
  185. state_key=key_values.get("state_key"),
  186. room_id=key_values["room_id"],
  187. sender=key_values["sender"],
  188. content=key_values.get("content", {}),
  189. unsigned=key_values.get("unsigned", {}),
  190. redacts=key_values.get("redacts", None),
  191. origin_server_ts=key_values.get("origin_server_ts", None),
  192. )
  193. def create_local_event_from_event_dict(
  194. clock: Clock,
  195. hostname: str,
  196. signing_key: SigningKey,
  197. room_version: RoomVersion,
  198. event_dict: JsonDict,
  199. internal_metadata_dict: Optional[JsonDict] = None,
  200. ) -> EventBase:
  201. """Takes a fully formed event dict, ensuring that fields like `origin`
  202. and `origin_server_ts` have correct values for a locally produced event,
  203. then signs and hashes it.
  204. """
  205. format_version = room_version.event_format
  206. if format_version not in KNOWN_EVENT_FORMAT_VERSIONS:
  207. raise Exception("No event format defined for version %r" % (format_version,))
  208. if internal_metadata_dict is None:
  209. internal_metadata_dict = {}
  210. time_now = int(clock.time_msec())
  211. if format_version == EventFormatVersions.V1:
  212. event_dict["event_id"] = _create_event_id(clock, hostname)
  213. event_dict["origin"] = hostname
  214. event_dict.setdefault("origin_server_ts", time_now)
  215. event_dict.setdefault("unsigned", {})
  216. age = event_dict["unsigned"].pop("age", 0)
  217. event_dict["unsigned"].setdefault("age_ts", time_now - age)
  218. event_dict.setdefault("signatures", {})
  219. add_hashes_and_signatures(room_version, event_dict, hostname, signing_key)
  220. return make_event_from_dict(
  221. event_dict, room_version, internal_metadata_dict=internal_metadata_dict
  222. )
  223. # A counter used when generating new event IDs
  224. _event_id_counter = 0
  225. def _create_event_id(clock, hostname):
  226. """Create a new event ID
  227. Args:
  228. clock (Clock)
  229. hostname (str): The server name for the event ID
  230. Returns:
  231. str
  232. """
  233. global _event_id_counter
  234. i = str(_event_id_counter)
  235. _event_id_counter += 1
  236. local_part = str(int(clock.time())) + i + random_string(5)
  237. e_id = EventID(local_part, hostname)
  238. return e_id.to_string()