room.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. # Copyright 2018 New Vector Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """ This module contains REST servlets to do with rooms: /rooms/<paths> """
  17. import logging
  18. from typing import List, Optional
  19. from six.moves.urllib import parse as urlparse
  20. from canonicaljson import json
  21. from synapse.api.constants import EventTypes, Membership
  22. from synapse.api.errors import (
  23. AuthError,
  24. Codes,
  25. InvalidClientCredentialsError,
  26. SynapseError,
  27. )
  28. from synapse.api.filtering import Filter
  29. from synapse.events.utils import format_event_for_client_v2
  30. from synapse.http.servlet import (
  31. RestServlet,
  32. assert_params_in_dict,
  33. parse_integer,
  34. parse_json_object_from_request,
  35. parse_string,
  36. )
  37. from synapse.logging.opentracing import set_tag
  38. from synapse.rest.client.transactions import HttpTransactionCache
  39. from synapse.rest.client.v2_alpha._base import client_patterns
  40. from synapse.storage.state import StateFilter
  41. from synapse.streams.config import PaginationConfig
  42. from synapse.types import RoomAlias, RoomID, StreamToken, ThirdPartyInstanceID, UserID
  43. MYPY = False
  44. if MYPY:
  45. import synapse.server
  46. logger = logging.getLogger(__name__)
  47. class TransactionRestServlet(RestServlet):
  48. def __init__(self, hs):
  49. super(TransactionRestServlet, self).__init__()
  50. self.txns = HttpTransactionCache(hs)
  51. class RoomCreateRestServlet(TransactionRestServlet):
  52. # No PATTERN; we have custom dispatch rules here
  53. def __init__(self, hs):
  54. super(RoomCreateRestServlet, self).__init__(hs)
  55. self._room_creation_handler = hs.get_room_creation_handler()
  56. self.auth = hs.get_auth()
  57. def register(self, http_server):
  58. PATTERNS = "/createRoom"
  59. register_txn_path(self, PATTERNS, http_server)
  60. # define CORS for all of /rooms in RoomCreateRestServlet for simplicity
  61. http_server.register_paths(
  62. "OPTIONS",
  63. client_patterns("/rooms(?:/.*)?$", v1=True),
  64. self.on_OPTIONS,
  65. self.__class__.__name__,
  66. )
  67. # define CORS for /createRoom[/txnid]
  68. http_server.register_paths(
  69. "OPTIONS",
  70. client_patterns("/createRoom(?:/.*)?$", v1=True),
  71. self.on_OPTIONS,
  72. self.__class__.__name__,
  73. )
  74. def on_PUT(self, request, txn_id):
  75. set_tag("txn_id", txn_id)
  76. return self.txns.fetch_or_execute_request(request, self.on_POST, request)
  77. async def on_POST(self, request):
  78. requester = await self.auth.get_user_by_req(request)
  79. info = await self._room_creation_handler.create_room(
  80. requester, self.get_room_config(request)
  81. )
  82. return 200, info
  83. def get_room_config(self, request):
  84. user_supplied_config = parse_json_object_from_request(request)
  85. return user_supplied_config
  86. def on_OPTIONS(self, request):
  87. return 200, {}
  88. # TODO: Needs unit testing for generic events
  89. class RoomStateEventRestServlet(TransactionRestServlet):
  90. def __init__(self, hs):
  91. super(RoomStateEventRestServlet, self).__init__(hs)
  92. self.handlers = hs.get_handlers()
  93. self.event_creation_handler = hs.get_event_creation_handler()
  94. self.room_member_handler = hs.get_room_member_handler()
  95. self.message_handler = hs.get_message_handler()
  96. self.auth = hs.get_auth()
  97. def register(self, http_server):
  98. # /room/$roomid/state/$eventtype
  99. no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"
  100. # /room/$roomid/state/$eventtype/$statekey
  101. state_key = (
  102. "/rooms/(?P<room_id>[^/]*)/state/"
  103. "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$"
  104. )
  105. http_server.register_paths(
  106. "GET",
  107. client_patterns(state_key, v1=True),
  108. self.on_GET,
  109. self.__class__.__name__,
  110. )
  111. http_server.register_paths(
  112. "PUT",
  113. client_patterns(state_key, v1=True),
  114. self.on_PUT,
  115. self.__class__.__name__,
  116. )
  117. http_server.register_paths(
  118. "GET",
  119. client_patterns(no_state_key, v1=True),
  120. self.on_GET_no_state_key,
  121. self.__class__.__name__,
  122. )
  123. http_server.register_paths(
  124. "PUT",
  125. client_patterns(no_state_key, v1=True),
  126. self.on_PUT_no_state_key,
  127. self.__class__.__name__,
  128. )
  129. def on_GET_no_state_key(self, request, room_id, event_type):
  130. return self.on_GET(request, room_id, event_type, "")
  131. def on_PUT_no_state_key(self, request, room_id, event_type):
  132. return self.on_PUT(request, room_id, event_type, "")
  133. async def on_GET(self, request, room_id, event_type, state_key):
  134. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  135. format = parse_string(
  136. request, "format", default="content", allowed_values=["content", "event"]
  137. )
  138. msg_handler = self.message_handler
  139. data = await msg_handler.get_room_data(
  140. user_id=requester.user.to_string(),
  141. room_id=room_id,
  142. event_type=event_type,
  143. state_key=state_key,
  144. is_guest=requester.is_guest,
  145. )
  146. if not data:
  147. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  148. if format == "event":
  149. event = format_event_for_client_v2(data.get_dict())
  150. return 200, event
  151. elif format == "content":
  152. return 200, data.get_dict()["content"]
  153. async def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
  154. requester = await self.auth.get_user_by_req(request)
  155. if txn_id:
  156. set_tag("txn_id", txn_id)
  157. content = parse_json_object_from_request(request)
  158. if event_type == EventTypes.Aliases:
  159. # MSC2260
  160. raise SynapseError(
  161. 400, "Cannot send m.room.aliases events via /rooms/{room_id}/state"
  162. )
  163. event_dict = {
  164. "type": event_type,
  165. "content": content,
  166. "room_id": room_id,
  167. "sender": requester.user.to_string(),
  168. }
  169. if state_key is not None:
  170. event_dict["state_key"] = state_key
  171. if event_type == EventTypes.Member:
  172. membership = content.get("membership", None)
  173. event = await self.room_member_handler.update_membership(
  174. requester,
  175. target=UserID.from_string(state_key),
  176. room_id=room_id,
  177. action=membership,
  178. content=content,
  179. )
  180. else:
  181. event = await self.event_creation_handler.create_and_send_nonmember_event(
  182. requester, event_dict, txn_id=txn_id
  183. )
  184. ret = {} # type: dict
  185. if event:
  186. set_tag("event_id", event.event_id)
  187. ret = {"event_id": event.event_id}
  188. return 200, ret
  189. # TODO: Needs unit testing for generic events + feedback
  190. class RoomSendEventRestServlet(TransactionRestServlet):
  191. def __init__(self, hs):
  192. super(RoomSendEventRestServlet, self).__init__(hs)
  193. self.event_creation_handler = hs.get_event_creation_handler()
  194. self.auth = hs.get_auth()
  195. def register(self, http_server):
  196. # /rooms/$roomid/send/$event_type[/$txn_id]
  197. PATTERNS = "/rooms/(?P<room_id>[^/]*)/send/(?P<event_type>[^/]*)"
  198. register_txn_path(self, PATTERNS, http_server, with_get=True)
  199. async def on_POST(self, request, room_id, event_type, txn_id=None):
  200. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  201. content = parse_json_object_from_request(request)
  202. if event_type == EventTypes.Aliases:
  203. # MSC2260
  204. raise SynapseError(
  205. 400, "Cannot send m.room.aliases events via /rooms/{room_id}/send"
  206. )
  207. event_dict = {
  208. "type": event_type,
  209. "content": content,
  210. "room_id": room_id,
  211. "sender": requester.user.to_string(),
  212. }
  213. if b"ts" in request.args and requester.app_service:
  214. event_dict["origin_server_ts"] = parse_integer(request, "ts", 0)
  215. event = await self.event_creation_handler.create_and_send_nonmember_event(
  216. requester, event_dict, txn_id=txn_id
  217. )
  218. set_tag("event_id", event.event_id)
  219. return 200, {"event_id": event.event_id}
  220. def on_GET(self, request, room_id, event_type, txn_id):
  221. return 200, "Not implemented"
  222. def on_PUT(self, request, room_id, event_type, txn_id):
  223. set_tag("txn_id", txn_id)
  224. return self.txns.fetch_or_execute_request(
  225. request, self.on_POST, request, room_id, event_type, txn_id
  226. )
  227. # TODO: Needs unit testing for room ID + alias joins
  228. class JoinRoomAliasServlet(TransactionRestServlet):
  229. def __init__(self, hs):
  230. super(JoinRoomAliasServlet, self).__init__(hs)
  231. self.room_member_handler = hs.get_room_member_handler()
  232. self.auth = hs.get_auth()
  233. def register(self, http_server):
  234. # /join/$room_identifier[/$txn_id]
  235. PATTERNS = "/join/(?P<room_identifier>[^/]*)"
  236. register_txn_path(self, PATTERNS, http_server)
  237. async def on_POST(self, request, room_identifier, txn_id=None):
  238. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  239. try:
  240. content = parse_json_object_from_request(request)
  241. except Exception:
  242. # Turns out we used to ignore the body entirely, and some clients
  243. # cheekily send invalid bodies.
  244. content = {}
  245. if RoomID.is_valid(room_identifier):
  246. room_id = room_identifier
  247. try:
  248. remote_room_hosts = [
  249. x.decode("ascii") for x in request.args[b"server_name"]
  250. ] # type: Optional[List[str]]
  251. except Exception:
  252. remote_room_hosts = None
  253. elif RoomAlias.is_valid(room_identifier):
  254. handler = self.room_member_handler
  255. room_alias = RoomAlias.from_string(room_identifier)
  256. room_id, remote_room_hosts = await handler.lookup_room_alias(room_alias)
  257. room_id = room_id.to_string()
  258. else:
  259. raise SynapseError(
  260. 400, "%s was not legal room ID or room alias" % (room_identifier,)
  261. )
  262. await self.room_member_handler.update_membership(
  263. requester=requester,
  264. target=requester.user,
  265. room_id=room_id,
  266. action="join",
  267. txn_id=txn_id,
  268. remote_room_hosts=remote_room_hosts,
  269. content=content,
  270. third_party_signed=content.get("third_party_signed", None),
  271. )
  272. return 200, {"room_id": room_id}
  273. def on_PUT(self, request, room_identifier, txn_id):
  274. set_tag("txn_id", txn_id)
  275. return self.txns.fetch_or_execute_request(
  276. request, self.on_POST, request, room_identifier, txn_id
  277. )
  278. # TODO: Needs unit testing
  279. class PublicRoomListRestServlet(TransactionRestServlet):
  280. PATTERNS = client_patterns("/publicRooms$", v1=True)
  281. def __init__(self, hs):
  282. super(PublicRoomListRestServlet, self).__init__(hs)
  283. self.hs = hs
  284. self.auth = hs.get_auth()
  285. async def on_GET(self, request):
  286. server = parse_string(request, "server", default=None)
  287. try:
  288. await self.auth.get_user_by_req(request, allow_guest=True)
  289. except InvalidClientCredentialsError as e:
  290. # Option to allow servers to require auth when accessing
  291. # /publicRooms via CS API. This is especially helpful in private
  292. # federations.
  293. if not self.hs.config.allow_public_rooms_without_auth:
  294. raise
  295. # We allow people to not be authed if they're just looking at our
  296. # room list, but require auth when we proxy the request.
  297. # In both cases we call the auth function, as that has the side
  298. # effect of logging who issued this request if an access token was
  299. # provided.
  300. if server:
  301. raise e
  302. else:
  303. pass
  304. limit = parse_integer(request, "limit", 0)
  305. since_token = parse_string(request, "since", None)
  306. if limit == 0:
  307. # zero is a special value which corresponds to no limit.
  308. limit = None
  309. handler = self.hs.get_room_list_handler()
  310. if server:
  311. data = await handler.get_remote_public_room_list(
  312. server, limit=limit, since_token=since_token
  313. )
  314. else:
  315. data = await handler.get_local_public_room_list(
  316. limit=limit, since_token=since_token
  317. )
  318. return 200, data
  319. async def on_POST(self, request):
  320. await self.auth.get_user_by_req(request, allow_guest=True)
  321. server = parse_string(request, "server", default=None)
  322. content = parse_json_object_from_request(request)
  323. limit = int(content.get("limit", 100)) # type: Optional[int]
  324. since_token = content.get("since", None)
  325. search_filter = content.get("filter", None)
  326. include_all_networks = content.get("include_all_networks", False)
  327. third_party_instance_id = content.get("third_party_instance_id", None)
  328. if include_all_networks:
  329. network_tuple = None
  330. if third_party_instance_id is not None:
  331. raise SynapseError(
  332. 400, "Can't use include_all_networks with an explicit network"
  333. )
  334. elif third_party_instance_id is None:
  335. network_tuple = ThirdPartyInstanceID(None, None)
  336. else:
  337. network_tuple = ThirdPartyInstanceID.from_string(third_party_instance_id)
  338. if limit == 0:
  339. # zero is a special value which corresponds to no limit.
  340. limit = None
  341. handler = self.hs.get_room_list_handler()
  342. if server:
  343. data = await handler.get_remote_public_room_list(
  344. server,
  345. limit=limit,
  346. since_token=since_token,
  347. search_filter=search_filter,
  348. include_all_networks=include_all_networks,
  349. third_party_instance_id=third_party_instance_id,
  350. )
  351. else:
  352. data = await handler.get_local_public_room_list(
  353. limit=limit,
  354. since_token=since_token,
  355. search_filter=search_filter,
  356. network_tuple=network_tuple,
  357. )
  358. return 200, data
  359. # TODO: Needs unit testing
  360. class RoomMemberListRestServlet(RestServlet):
  361. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)
  362. def __init__(self, hs):
  363. super(RoomMemberListRestServlet, self).__init__()
  364. self.message_handler = hs.get_message_handler()
  365. self.auth = hs.get_auth()
  366. async def on_GET(self, request, room_id):
  367. # TODO support Pagination stream API (limit/tokens)
  368. requester = await self.auth.get_user_by_req(request)
  369. handler = self.message_handler
  370. # request the state as of a given event, as identified by a stream token,
  371. # for consistency with /messages etc.
  372. # useful for getting the membership in retrospect as of a given /sync
  373. # response.
  374. at_token_string = parse_string(request, "at")
  375. if at_token_string is None:
  376. at_token = None
  377. else:
  378. at_token = StreamToken.from_string(at_token_string)
  379. # let you filter down on particular memberships.
  380. # XXX: this may not be the best shape for this API - we could pass in a filter
  381. # instead, except filters aren't currently aware of memberships.
  382. # See https://github.com/matrix-org/matrix-doc/issues/1337 for more details.
  383. membership = parse_string(request, "membership")
  384. not_membership = parse_string(request, "not_membership")
  385. events = await handler.get_state_events(
  386. room_id=room_id,
  387. user_id=requester.user.to_string(),
  388. at_token=at_token,
  389. state_filter=StateFilter.from_types([(EventTypes.Member, None)]),
  390. )
  391. chunk = []
  392. for event in events:
  393. if (membership and event["content"].get("membership") != membership) or (
  394. not_membership and event["content"].get("membership") == not_membership
  395. ):
  396. continue
  397. chunk.append(event)
  398. return 200, {"chunk": chunk}
  399. # deprecated in favour of /members?membership=join?
  400. # except it does custom AS logic and has a simpler return format
  401. class JoinedRoomMemberListRestServlet(RestServlet):
  402. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/joined_members$", v1=True)
  403. def __init__(self, hs):
  404. super(JoinedRoomMemberListRestServlet, self).__init__()
  405. self.message_handler = hs.get_message_handler()
  406. self.auth = hs.get_auth()
  407. async def on_GET(self, request, room_id):
  408. requester = await self.auth.get_user_by_req(request)
  409. users_with_profile = await self.message_handler.get_joined_members(
  410. requester, room_id
  411. )
  412. return 200, {"joined": users_with_profile}
  413. # TODO: Needs better unit testing
  414. class RoomMessageListRestServlet(RestServlet):
  415. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/messages$", v1=True)
  416. def __init__(self, hs):
  417. super(RoomMessageListRestServlet, self).__init__()
  418. self.pagination_handler = hs.get_pagination_handler()
  419. self.auth = hs.get_auth()
  420. async def on_GET(self, request, room_id):
  421. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  422. pagination_config = PaginationConfig.from_request(request, default_limit=10)
  423. as_client_event = b"raw" not in request.args
  424. filter_bytes = parse_string(request, b"filter", encoding=None)
  425. if filter_bytes:
  426. filter_json = urlparse.unquote(filter_bytes.decode("UTF-8"))
  427. event_filter = Filter(json.loads(filter_json)) # type: Optional[Filter]
  428. if (
  429. event_filter
  430. and event_filter.filter_json.get("event_format", "client")
  431. == "federation"
  432. ):
  433. as_client_event = False
  434. else:
  435. event_filter = None
  436. msgs = await self.pagination_handler.get_messages(
  437. room_id=room_id,
  438. requester=requester,
  439. pagin_config=pagination_config,
  440. as_client_event=as_client_event,
  441. event_filter=event_filter,
  442. )
  443. return 200, msgs
  444. # TODO: Needs unit testing
  445. class RoomStateRestServlet(RestServlet):
  446. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)
  447. def __init__(self, hs):
  448. super(RoomStateRestServlet, self).__init__()
  449. self.message_handler = hs.get_message_handler()
  450. self.auth = hs.get_auth()
  451. async def on_GET(self, request, room_id):
  452. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  453. # Get all the current state for this room
  454. events = await self.message_handler.get_state_events(
  455. room_id=room_id,
  456. user_id=requester.user.to_string(),
  457. is_guest=requester.is_guest,
  458. )
  459. return 200, events
  460. # TODO: Needs unit testing
  461. class RoomInitialSyncRestServlet(RestServlet):
  462. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)
  463. def __init__(self, hs):
  464. super(RoomInitialSyncRestServlet, self).__init__()
  465. self.initial_sync_handler = hs.get_initial_sync_handler()
  466. self.auth = hs.get_auth()
  467. async def on_GET(self, request, room_id):
  468. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  469. pagination_config = PaginationConfig.from_request(request)
  470. content = await self.initial_sync_handler.room_initial_sync(
  471. room_id=room_id, requester=requester, pagin_config=pagination_config
  472. )
  473. return 200, content
  474. class RoomEventServlet(RestServlet):
  475. PATTERNS = client_patterns(
  476. "/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True
  477. )
  478. def __init__(self, hs):
  479. super(RoomEventServlet, self).__init__()
  480. self.clock = hs.get_clock()
  481. self.event_handler = hs.get_event_handler()
  482. self._event_serializer = hs.get_event_client_serializer()
  483. self.auth = hs.get_auth()
  484. async def on_GET(self, request, room_id, event_id):
  485. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  486. try:
  487. event = await self.event_handler.get_event(
  488. requester.user, room_id, event_id
  489. )
  490. except AuthError:
  491. # This endpoint is supposed to return a 404 when the requester does
  492. # not have permission to access the event
  493. # https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-rooms-roomid-event-eventid
  494. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  495. time_now = self.clock.time_msec()
  496. if event:
  497. event = await self._event_serializer.serialize_event(event, time_now)
  498. return 200, event
  499. return SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  500. class RoomEventContextServlet(RestServlet):
  501. PATTERNS = client_patterns(
  502. "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True
  503. )
  504. def __init__(self, hs):
  505. super(RoomEventContextServlet, self).__init__()
  506. self.clock = hs.get_clock()
  507. self.room_context_handler = hs.get_room_context_handler()
  508. self._event_serializer = hs.get_event_client_serializer()
  509. self.auth = hs.get_auth()
  510. async def on_GET(self, request, room_id, event_id):
  511. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  512. limit = parse_integer(request, "limit", default=10)
  513. # picking the API shape for symmetry with /messages
  514. filter_bytes = parse_string(request, "filter")
  515. if filter_bytes:
  516. filter_json = urlparse.unquote(filter_bytes)
  517. event_filter = Filter(json.loads(filter_json)) # type: Optional[Filter]
  518. else:
  519. event_filter = None
  520. results = await self.room_context_handler.get_event_context(
  521. requester.user, room_id, event_id, limit, event_filter
  522. )
  523. if not results:
  524. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  525. time_now = self.clock.time_msec()
  526. results["events_before"] = await self._event_serializer.serialize_events(
  527. results["events_before"], time_now
  528. )
  529. results["event"] = await self._event_serializer.serialize_event(
  530. results["event"], time_now
  531. )
  532. results["events_after"] = await self._event_serializer.serialize_events(
  533. results["events_after"], time_now
  534. )
  535. results["state"] = await self._event_serializer.serialize_events(
  536. results["state"], time_now
  537. )
  538. return 200, results
  539. class RoomForgetRestServlet(TransactionRestServlet):
  540. def __init__(self, hs):
  541. super(RoomForgetRestServlet, self).__init__(hs)
  542. self.room_member_handler = hs.get_room_member_handler()
  543. self.auth = hs.get_auth()
  544. def register(self, http_server):
  545. PATTERNS = "/rooms/(?P<room_id>[^/]*)/forget"
  546. register_txn_path(self, PATTERNS, http_server)
  547. async def on_POST(self, request, room_id, txn_id=None):
  548. requester = await self.auth.get_user_by_req(request, allow_guest=False)
  549. await self.room_member_handler.forget(user=requester.user, room_id=room_id)
  550. return 200, {}
  551. def on_PUT(self, request, room_id, txn_id):
  552. set_tag("txn_id", txn_id)
  553. return self.txns.fetch_or_execute_request(
  554. request, self.on_POST, request, room_id, txn_id
  555. )
  556. # TODO: Needs unit testing
  557. class RoomMembershipRestServlet(TransactionRestServlet):
  558. def __init__(self, hs):
  559. super(RoomMembershipRestServlet, self).__init__(hs)
  560. self.room_member_handler = hs.get_room_member_handler()
  561. self.auth = hs.get_auth()
  562. def register(self, http_server):
  563. # /rooms/$roomid/[invite|join|leave]
  564. PATTERNS = (
  565. "/rooms/(?P<room_id>[^/]*)/"
  566. "(?P<membership_action>join|invite|leave|ban|unban|kick)"
  567. )
  568. register_txn_path(self, PATTERNS, http_server)
  569. async def on_POST(self, request, room_id, membership_action, txn_id=None):
  570. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  571. if requester.is_guest and membership_action not in {
  572. Membership.JOIN,
  573. Membership.LEAVE,
  574. }:
  575. raise AuthError(403, "Guest access not allowed")
  576. try:
  577. content = parse_json_object_from_request(request)
  578. except Exception:
  579. # Turns out we used to ignore the body entirely, and some clients
  580. # cheekily send invalid bodies.
  581. content = {}
  582. if membership_action == "invite" and self._has_3pid_invite_keys(content):
  583. await self.room_member_handler.do_3pid_invite(
  584. room_id,
  585. requester.user,
  586. content["medium"],
  587. content["address"],
  588. content["id_server"],
  589. requester,
  590. txn_id,
  591. content.get("id_access_token"),
  592. )
  593. return 200, {}
  594. target = requester.user
  595. if membership_action in ["invite", "ban", "unban", "kick"]:
  596. assert_params_in_dict(content, ["user_id"])
  597. target = UserID.from_string(content["user_id"])
  598. event_content = None
  599. if "reason" in content:
  600. event_content = {"reason": content["reason"]}
  601. await self.room_member_handler.update_membership(
  602. requester=requester,
  603. target=target,
  604. room_id=room_id,
  605. action=membership_action,
  606. txn_id=txn_id,
  607. third_party_signed=content.get("third_party_signed", None),
  608. content=event_content,
  609. )
  610. return_value = {}
  611. if membership_action == "join":
  612. return_value["room_id"] = room_id
  613. return 200, return_value
  614. def _has_3pid_invite_keys(self, content):
  615. for key in {"id_server", "medium", "address"}:
  616. if key not in content:
  617. return False
  618. return True
  619. def on_PUT(self, request, room_id, membership_action, txn_id):
  620. set_tag("txn_id", txn_id)
  621. return self.txns.fetch_or_execute_request(
  622. request, self.on_POST, request, room_id, membership_action, txn_id
  623. )
  624. class RoomRedactEventRestServlet(TransactionRestServlet):
  625. def __init__(self, hs):
  626. super(RoomRedactEventRestServlet, self).__init__(hs)
  627. self.handlers = hs.get_handlers()
  628. self.event_creation_handler = hs.get_event_creation_handler()
  629. self.auth = hs.get_auth()
  630. def register(self, http_server):
  631. PATTERNS = "/rooms/(?P<room_id>[^/]*)/redact/(?P<event_id>[^/]*)"
  632. register_txn_path(self, PATTERNS, http_server)
  633. async def on_POST(self, request, room_id, event_id, txn_id=None):
  634. requester = await self.auth.get_user_by_req(request)
  635. content = parse_json_object_from_request(request)
  636. event = await self.event_creation_handler.create_and_send_nonmember_event(
  637. requester,
  638. {
  639. "type": EventTypes.Redaction,
  640. "content": content,
  641. "room_id": room_id,
  642. "sender": requester.user.to_string(),
  643. "redacts": event_id,
  644. },
  645. txn_id=txn_id,
  646. )
  647. set_tag("event_id", event.event_id)
  648. return 200, {"event_id": event.event_id}
  649. def on_PUT(self, request, room_id, event_id, txn_id):
  650. set_tag("txn_id", txn_id)
  651. return self.txns.fetch_or_execute_request(
  652. request, self.on_POST, request, room_id, event_id, txn_id
  653. )
  654. class RoomTypingRestServlet(RestServlet):
  655. PATTERNS = client_patterns(
  656. "/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True
  657. )
  658. def __init__(self, hs):
  659. super(RoomTypingRestServlet, self).__init__()
  660. self.presence_handler = hs.get_presence_handler()
  661. self.typing_handler = hs.get_typing_handler()
  662. self.auth = hs.get_auth()
  663. async def on_PUT(self, request, room_id, user_id):
  664. requester = await self.auth.get_user_by_req(request)
  665. room_id = urlparse.unquote(room_id)
  666. target_user = UserID.from_string(urlparse.unquote(user_id))
  667. content = parse_json_object_from_request(request)
  668. await self.presence_handler.bump_presence_active_time(requester.user)
  669. # Limit timeout to stop people from setting silly typing timeouts.
  670. timeout = min(content.get("timeout", 30000), 120000)
  671. if content["typing"]:
  672. await self.typing_handler.started_typing(
  673. target_user=target_user,
  674. auth_user=requester.user,
  675. room_id=room_id,
  676. timeout=timeout,
  677. )
  678. else:
  679. await self.typing_handler.stopped_typing(
  680. target_user=target_user, auth_user=requester.user, room_id=room_id
  681. )
  682. return 200, {}
  683. class RoomAliasListServlet(RestServlet):
  684. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/aliases", unstable=False)
  685. def __init__(self, hs: "synapse.server.HomeServer"):
  686. super().__init__()
  687. self.auth = hs.get_auth()
  688. self.directory_handler = hs.get_handlers().directory_handler
  689. async def on_GET(self, request, room_id):
  690. requester = await self.auth.get_user_by_req(request)
  691. alias_list = await self.directory_handler.get_aliases_for_room(
  692. requester, room_id
  693. )
  694. return 200, {"aliases": alias_list}
  695. class SearchRestServlet(RestServlet):
  696. PATTERNS = client_patterns("/search$", v1=True)
  697. def __init__(self, hs):
  698. super(SearchRestServlet, self).__init__()
  699. self.handlers = hs.get_handlers()
  700. self.auth = hs.get_auth()
  701. async def on_POST(self, request):
  702. requester = await self.auth.get_user_by_req(request)
  703. content = parse_json_object_from_request(request)
  704. batch = parse_string(request, "next_batch")
  705. results = await self.handlers.search_handler.search(
  706. requester.user, content, batch
  707. )
  708. return 200, results
  709. class JoinedRoomsRestServlet(RestServlet):
  710. PATTERNS = client_patterns("/joined_rooms$", v1=True)
  711. def __init__(self, hs):
  712. super(JoinedRoomsRestServlet, self).__init__()
  713. self.store = hs.get_datastore()
  714. self.auth = hs.get_auth()
  715. async def on_GET(self, request):
  716. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  717. room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
  718. return 200, {"joined_rooms": list(room_ids)}
  719. def register_txn_path(servlet, regex_string, http_server, with_get=False):
  720. """Registers a transaction-based path.
  721. This registers two paths:
  722. PUT regex_string/$txnid
  723. POST regex_string
  724. Args:
  725. regex_string (str): The regex string to register. Must NOT have a
  726. trailing $ as this string will be appended to.
  727. http_server : The http_server to register paths with.
  728. with_get: True to also register respective GET paths for the PUTs.
  729. """
  730. http_server.register_paths(
  731. "POST",
  732. client_patterns(regex_string + "$", v1=True),
  733. servlet.on_POST,
  734. servlet.__class__.__name__,
  735. )
  736. http_server.register_paths(
  737. "PUT",
  738. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  739. servlet.on_PUT,
  740. servlet.__class__.__name__,
  741. )
  742. if with_get:
  743. http_server.register_paths(
  744. "GET",
  745. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  746. servlet.on_GET,
  747. servlet.__class__.__name__,
  748. )
  749. def register_servlets(hs, http_server):
  750. RoomStateEventRestServlet(hs).register(http_server)
  751. RoomCreateRestServlet(hs).register(http_server)
  752. RoomMemberListRestServlet(hs).register(http_server)
  753. JoinedRoomMemberListRestServlet(hs).register(http_server)
  754. RoomMessageListRestServlet(hs).register(http_server)
  755. JoinRoomAliasServlet(hs).register(http_server)
  756. RoomForgetRestServlet(hs).register(http_server)
  757. RoomMembershipRestServlet(hs).register(http_server)
  758. RoomSendEventRestServlet(hs).register(http_server)
  759. PublicRoomListRestServlet(hs).register(http_server)
  760. RoomStateRestServlet(hs).register(http_server)
  761. RoomRedactEventRestServlet(hs).register(http_server)
  762. RoomTypingRestServlet(hs).register(http_server)
  763. SearchRestServlet(hs).register(http_server)
  764. JoinedRoomsRestServlet(hs).register(http_server)
  765. RoomEventServlet(hs).register(http_server)
  766. RoomEventContextServlet(hs).register(http_server)
  767. RoomAliasListServlet(hs).register(http_server)
  768. def register_deprecated_servlets(hs, http_server):
  769. RoomInitialSyncRestServlet(hs).register(http_server)