validator.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership
  16. from synapse.api.errors import Codes, SynapseError
  17. from synapse.api.room_versions import EventFormatVersions
  18. from synapse.events.utils import validate_canonicaljson
  19. from synapse.types import EventID, RoomID, UserID
  20. class EventValidator(object):
  21. def validate_new(self, event, config):
  22. """Validates the event has roughly the right format
  23. Args:
  24. event (FrozenEvent): The event to validate.
  25. config (Config): The homeserver's configuration.
  26. """
  27. self.validate_builder(event)
  28. if event.format_version == EventFormatVersions.V1:
  29. EventID.from_string(event.event_id)
  30. required = [
  31. "auth_events",
  32. "content",
  33. "hashes",
  34. "origin",
  35. "prev_events",
  36. "sender",
  37. "type",
  38. ]
  39. for k in required:
  40. if not hasattr(event, k):
  41. raise SynapseError(400, "Event does not have key %s" % (k,))
  42. # Check that the following keys have string values
  43. event_strings = ["origin"]
  44. for s in event_strings:
  45. if not isinstance(getattr(event, s), str):
  46. raise SynapseError(400, "'%s' not a string type" % (s,))
  47. # Depending on the room version, ensure the data is spec compliant JSON.
  48. if event.room_version.strict_canonicaljson:
  49. # Note that only the client controlled portion of the event is
  50. # checked, since we trust the portions of the event we created.
  51. validate_canonicaljson(event.content)
  52. if event.type == EventTypes.Aliases:
  53. if "aliases" in event.content:
  54. for alias in event.content["aliases"]:
  55. if len(alias) > MAX_ALIAS_LENGTH:
  56. raise SynapseError(
  57. 400,
  58. (
  59. "Can't create aliases longer than"
  60. " %d characters" % (MAX_ALIAS_LENGTH,)
  61. ),
  62. Codes.INVALID_PARAM,
  63. )
  64. if event.type == EventTypes.Retention:
  65. self._validate_retention(event, config)
  66. def _validate_retention(self, event, config):
  67. """Checks that an event that defines the retention policy for a room respects the
  68. boundaries imposed by the server's administrator.
  69. Args:
  70. event (FrozenEvent): The event to validate.
  71. config (Config): The homeserver's configuration.
  72. """
  73. min_lifetime = event.content.get("min_lifetime")
  74. max_lifetime = event.content.get("max_lifetime")
  75. if min_lifetime is not None:
  76. if not isinstance(min_lifetime, int):
  77. raise SynapseError(
  78. code=400,
  79. msg="'min_lifetime' must be an integer",
  80. errcode=Codes.BAD_JSON,
  81. )
  82. if (
  83. config.retention_allowed_lifetime_min is not None
  84. and min_lifetime < config.retention_allowed_lifetime_min
  85. ):
  86. raise SynapseError(
  87. code=400,
  88. msg=(
  89. "'min_lifetime' can't be lower than the minimum allowed"
  90. " value enforced by the server's administrator"
  91. ),
  92. errcode=Codes.BAD_JSON,
  93. )
  94. if (
  95. config.retention_allowed_lifetime_max is not None
  96. and min_lifetime > config.retention_allowed_lifetime_max
  97. ):
  98. raise SynapseError(
  99. code=400,
  100. msg=(
  101. "'min_lifetime' can't be greater than the maximum allowed"
  102. " value enforced by the server's administrator"
  103. ),
  104. errcode=Codes.BAD_JSON,
  105. )
  106. if max_lifetime is not None:
  107. if not isinstance(max_lifetime, int):
  108. raise SynapseError(
  109. code=400,
  110. msg="'max_lifetime' must be an integer",
  111. errcode=Codes.BAD_JSON,
  112. )
  113. if (
  114. config.retention_allowed_lifetime_min is not None
  115. and max_lifetime < config.retention_allowed_lifetime_min
  116. ):
  117. raise SynapseError(
  118. code=400,
  119. msg=(
  120. "'max_lifetime' can't be lower than the minimum allowed value"
  121. " enforced by the server's administrator"
  122. ),
  123. errcode=Codes.BAD_JSON,
  124. )
  125. if (
  126. config.retention_allowed_lifetime_max is not None
  127. and max_lifetime > config.retention_allowed_lifetime_max
  128. ):
  129. raise SynapseError(
  130. code=400,
  131. msg=(
  132. "'max_lifetime' can't be greater than the maximum allowed"
  133. " value enforced by the server's administrator"
  134. ),
  135. errcode=Codes.BAD_JSON,
  136. )
  137. if (
  138. min_lifetime is not None
  139. and max_lifetime is not None
  140. and min_lifetime > max_lifetime
  141. ):
  142. raise SynapseError(
  143. code=400,
  144. msg="'min_lifetime' can't be greater than 'max_lifetime",
  145. errcode=Codes.BAD_JSON,
  146. )
  147. def validate_builder(self, event):
  148. """Validates that the builder/event has roughly the right format. Only
  149. checks values that we expect a proto event to have, rather than all the
  150. fields an event would have
  151. Args:
  152. event (EventBuilder|FrozenEvent)
  153. """
  154. strings = ["room_id", "sender", "type"]
  155. if hasattr(event, "state_key"):
  156. strings.append("state_key")
  157. for s in strings:
  158. if not isinstance(getattr(event, s), str):
  159. raise SynapseError(400, "Not '%s' a string type" % (s,))
  160. RoomID.from_string(event.room_id)
  161. UserID.from_string(event.sender)
  162. if event.type == EventTypes.Message:
  163. strings = ["body", "msgtype"]
  164. self._ensure_strings(event.content, strings)
  165. elif event.type == EventTypes.Topic:
  166. self._ensure_strings(event.content, ["topic"])
  167. self._ensure_state_event(event)
  168. elif event.type == EventTypes.Name:
  169. self._ensure_strings(event.content, ["name"])
  170. self._ensure_state_event(event)
  171. elif event.type == EventTypes.Member:
  172. if "membership" not in event.content:
  173. raise SynapseError(400, "Content has not membership key")
  174. if event.content["membership"] not in Membership.LIST:
  175. raise SynapseError(400, "Invalid membership key")
  176. self._ensure_state_event(event)
  177. elif event.type == EventTypes.Tombstone:
  178. if "replacement_room" not in event.content:
  179. raise SynapseError(400, "Content has no replacement_room key")
  180. if event.content["replacement_room"] == event.room_id:
  181. raise SynapseError(
  182. 400, "Tombstone cannot reference the room it was sent in"
  183. )
  184. self._ensure_state_event(event)
  185. def _ensure_strings(self, d, keys):
  186. for s in keys:
  187. if s not in d:
  188. raise SynapseError(400, "'%s' not in content" % (s,))
  189. if not isinstance(d[s], str):
  190. raise SynapseError(400, "'%s' not a string type" % (s,))
  191. def _ensure_state_event(self, event):
  192. if not event.is_state():
  193. raise SynapseError(400, "'%s' must be state events" % (event.type,))