server.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  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. import functools
  17. import logging
  18. import re
  19. from twisted.internet import defer
  20. import synapse
  21. from synapse.api.errors import Codes, FederationDeniedError, SynapseError
  22. from synapse.api.room_versions import RoomVersions
  23. from synapse.api.urls import (
  24. FEDERATION_UNSTABLE_PREFIX,
  25. FEDERATION_V1_PREFIX,
  26. FEDERATION_V2_PREFIX,
  27. )
  28. from synapse.http.endpoint import parse_and_validate_server_name
  29. from synapse.http.server import JsonResource
  30. from synapse.http.servlet import (
  31. parse_boolean_from_args,
  32. parse_integer_from_args,
  33. parse_json_object_from_request,
  34. parse_string_from_args,
  35. )
  36. from synapse.logging.context import run_in_background
  37. from synapse.types import ThirdPartyInstanceID, get_domain_from_id
  38. from synapse.util.ratelimitutils import FederationRateLimiter
  39. from synapse.util.versionstring import get_version_string
  40. logger = logging.getLogger(__name__)
  41. class TransportLayerServer(JsonResource):
  42. """Handles incoming federation HTTP requests"""
  43. def __init__(self, hs, servlet_groups=None):
  44. """Initialize the TransportLayerServer
  45. Will by default register all servlets. For custom behaviour, pass in
  46. a list of servlet_groups to register.
  47. Args:
  48. hs (synapse.server.HomeServer): homeserver
  49. servlet_groups (list[str], optional): List of servlet groups to register.
  50. Defaults to ``DEFAULT_SERVLET_GROUPS``.
  51. """
  52. self.hs = hs
  53. self.clock = hs.get_clock()
  54. self.servlet_groups = servlet_groups
  55. super(TransportLayerServer, self).__init__(hs, canonical_json=False)
  56. self.authenticator = Authenticator(hs)
  57. self.ratelimiter = FederationRateLimiter(
  58. self.clock, config=hs.config.rc_federation
  59. )
  60. self.register_servlets()
  61. def register_servlets(self):
  62. register_servlets(
  63. self.hs,
  64. resource=self,
  65. ratelimiter=self.ratelimiter,
  66. authenticator=self.authenticator,
  67. servlet_groups=self.servlet_groups,
  68. )
  69. class AuthenticationError(SynapseError):
  70. """There was a problem authenticating the request"""
  71. pass
  72. class NoAuthenticationError(AuthenticationError):
  73. """The request had no authentication information"""
  74. pass
  75. class Authenticator(object):
  76. def __init__(self, hs):
  77. self._clock = hs.get_clock()
  78. self.keyring = hs.get_keyring()
  79. self.server_name = hs.hostname
  80. self.store = hs.get_datastore()
  81. self.federation_domain_whitelist = hs.config.federation_domain_whitelist
  82. # A method just so we can pass 'self' as the authenticator to the Servlets
  83. @defer.inlineCallbacks
  84. def authenticate_request(self, request, content):
  85. now = self._clock.time_msec()
  86. json_request = {
  87. "method": request.method.decode("ascii"),
  88. "uri": request.uri.decode("ascii"),
  89. "destination": self.server_name,
  90. "signatures": {},
  91. }
  92. if content is not None:
  93. json_request["content"] = content
  94. origin = None
  95. auth_headers = request.requestHeaders.getRawHeaders(b"Authorization")
  96. if not auth_headers:
  97. raise NoAuthenticationError(
  98. 401, "Missing Authorization headers", Codes.UNAUTHORIZED
  99. )
  100. for auth in auth_headers:
  101. if auth.startswith(b"X-Matrix"):
  102. (origin, key, sig) = _parse_auth_header(auth)
  103. json_request["origin"] = origin
  104. json_request["signatures"].setdefault(origin, {})[key] = sig
  105. if (
  106. self.federation_domain_whitelist is not None
  107. and origin not in self.federation_domain_whitelist
  108. ):
  109. raise FederationDeniedError(origin)
  110. if not json_request["signatures"]:
  111. raise NoAuthenticationError(
  112. 401, "Missing Authorization headers", Codes.UNAUTHORIZED
  113. )
  114. yield self.keyring.verify_json_for_server(
  115. origin, json_request, now, "Incoming request"
  116. )
  117. logger.info("Request from %s", origin)
  118. request.authenticated_entity = origin
  119. # If we get a valid signed request from the other side, its probably
  120. # alive
  121. retry_timings = yield self.store.get_destination_retry_timings(origin)
  122. if retry_timings and retry_timings["retry_last_ts"]:
  123. run_in_background(self._reset_retry_timings, origin)
  124. defer.returnValue(origin)
  125. @defer.inlineCallbacks
  126. def _reset_retry_timings(self, origin):
  127. try:
  128. logger.info("Marking origin %r as up", origin)
  129. yield self.store.set_destination_retry_timings(origin, 0, 0)
  130. except Exception:
  131. logger.exception("Error resetting retry timings on %s", origin)
  132. def _parse_auth_header(header_bytes):
  133. """Parse an X-Matrix auth header
  134. Args:
  135. header_bytes (bytes): header value
  136. Returns:
  137. Tuple[str, str, str]: origin, key id, signature.
  138. Raises:
  139. AuthenticationError if the header could not be parsed
  140. """
  141. try:
  142. header_str = header_bytes.decode("utf-8")
  143. params = header_str.split(" ")[1].split(",")
  144. param_dict = dict(kv.split("=") for kv in params)
  145. def strip_quotes(value):
  146. if value.startswith('"'):
  147. return value[1:-1]
  148. else:
  149. return value
  150. origin = strip_quotes(param_dict["origin"])
  151. # ensure that the origin is a valid server name
  152. parse_and_validate_server_name(origin)
  153. key = strip_quotes(param_dict["key"])
  154. sig = strip_quotes(param_dict["sig"])
  155. return origin, key, sig
  156. except Exception as e:
  157. logger.warn(
  158. "Error parsing auth header '%s': %s",
  159. header_bytes.decode("ascii", "replace"),
  160. e,
  161. )
  162. raise AuthenticationError(
  163. 400, "Malformed Authorization header", Codes.UNAUTHORIZED
  164. )
  165. class BaseFederationServlet(object):
  166. """Abstract base class for federation servlet classes.
  167. The servlet object should have a PATH attribute which takes the form of a regexp to
  168. match against the request path (excluding the /federation/v1 prefix).
  169. The servlet should also implement one or more of on_GET, on_POST, on_PUT, to match
  170. the appropriate HTTP method. These methods have the signature:
  171. on_<METHOD>(self, origin, content, query, **kwargs)
  172. With arguments:
  173. origin (unicode|None): The authenticated server_name of the calling server,
  174. unless REQUIRE_AUTH is set to False and authentication failed.
  175. content (unicode|None): decoded json body of the request. None if the
  176. request was a GET.
  177. query (dict[bytes, list[bytes]]): Query params from the request. url-decoded
  178. (ie, '+' and '%xx' are decoded) but note that it is *not* utf8-decoded
  179. yet.
  180. **kwargs (dict[unicode, unicode]): the dict mapping keys to path
  181. components as specified in the path match regexp.
  182. Returns:
  183. Deferred[(int, object)|None]: either (response code, response object) to
  184. return a JSON response, or None if the request has already been handled.
  185. Raises:
  186. SynapseError: to return an error code
  187. Exception: other exceptions will be caught, logged, and a 500 will be
  188. returned.
  189. """
  190. REQUIRE_AUTH = True
  191. PREFIX = FEDERATION_V1_PREFIX # Allows specifying the API version
  192. def __init__(self, handler, authenticator, ratelimiter, server_name):
  193. self.handler = handler
  194. self.authenticator = authenticator
  195. self.ratelimiter = ratelimiter
  196. def _wrap(self, func):
  197. authenticator = self.authenticator
  198. ratelimiter = self.ratelimiter
  199. @defer.inlineCallbacks
  200. @functools.wraps(func)
  201. def new_func(request, *args, **kwargs):
  202. """ A callback which can be passed to HttpServer.RegisterPaths
  203. Args:
  204. request (twisted.web.http.Request):
  205. *args: unused?
  206. **kwargs (dict[unicode, unicode]): the dict mapping keys to path
  207. components as specified in the path match regexp.
  208. Returns:
  209. Deferred[(int, object)|None]: (response code, response object) as returned
  210. by the callback method. None if the request has already been handled.
  211. """
  212. content = None
  213. if request.method in [b"PUT", b"POST"]:
  214. # TODO: Handle other method types? other content types?
  215. content = parse_json_object_from_request(request)
  216. try:
  217. origin = yield authenticator.authenticate_request(request, content)
  218. except NoAuthenticationError:
  219. origin = None
  220. if self.REQUIRE_AUTH:
  221. logger.warn("authenticate_request failed: missing authentication")
  222. raise
  223. except Exception as e:
  224. logger.warn("authenticate_request failed: %s", e)
  225. raise
  226. if origin:
  227. with ratelimiter.ratelimit(origin) as d:
  228. yield d
  229. response = yield func(
  230. origin, content, request.args, *args, **kwargs
  231. )
  232. else:
  233. response = yield func(origin, content, request.args, *args, **kwargs)
  234. defer.returnValue(response)
  235. # Extra logic that functools.wraps() doesn't finish
  236. new_func.__self__ = func.__self__
  237. return new_func
  238. def register(self, server):
  239. pattern = re.compile("^" + self.PREFIX + self.PATH + "$")
  240. for method in ("GET", "PUT", "POST"):
  241. code = getattr(self, "on_%s" % (method), None)
  242. if code is None:
  243. continue
  244. server.register_paths(method, (pattern,), self._wrap(code))
  245. class FederationSendServlet(BaseFederationServlet):
  246. PATH = "/send/(?P<transaction_id>[^/]*)/?"
  247. def __init__(self, handler, server_name, **kwargs):
  248. super(FederationSendServlet, self).__init__(
  249. handler, server_name=server_name, **kwargs
  250. )
  251. self.server_name = server_name
  252. # This is when someone is trying to send us a bunch of data.
  253. @defer.inlineCallbacks
  254. def on_PUT(self, origin, content, query, transaction_id):
  255. """ Called on PUT /send/<transaction_id>/
  256. Args:
  257. request (twisted.web.http.Request): The HTTP request.
  258. transaction_id (str): The transaction_id associated with this
  259. request. This is *not* None.
  260. Returns:
  261. Deferred: Results in a tuple of `(code, response)`, where
  262. `response` is a python dict to be converted into JSON that is
  263. used as the response body.
  264. """
  265. # Parse the request
  266. try:
  267. transaction_data = content
  268. logger.debug("Decoded %s: %s", transaction_id, str(transaction_data))
  269. logger.info(
  270. "Received txn %s from %s. (PDUs: %d, EDUs: %d)",
  271. transaction_id,
  272. origin,
  273. len(transaction_data.get("pdus", [])),
  274. len(transaction_data.get("edus", [])),
  275. )
  276. # We should ideally be getting this from the security layer.
  277. # origin = body["origin"]
  278. # Add some extra data to the transaction dict that isn't included
  279. # in the request body.
  280. transaction_data.update(
  281. transaction_id=transaction_id, destination=self.server_name
  282. )
  283. except Exception as e:
  284. logger.exception(e)
  285. defer.returnValue((400, {"error": "Invalid transaction"}))
  286. return
  287. try:
  288. code, response = yield self.handler.on_incoming_transaction(
  289. origin, transaction_data
  290. )
  291. except Exception:
  292. logger.exception("on_incoming_transaction failed")
  293. raise
  294. defer.returnValue((code, response))
  295. class FederationEventServlet(BaseFederationServlet):
  296. PATH = "/event/(?P<event_id>[^/]*)/?"
  297. # This is when someone asks for a data item for a given server data_id pair.
  298. def on_GET(self, origin, content, query, event_id):
  299. return self.handler.on_pdu_request(origin, event_id)
  300. class FederationStateServlet(BaseFederationServlet):
  301. PATH = "/state/(?P<context>[^/]*)/?"
  302. # This is when someone asks for all data for a given context.
  303. def on_GET(self, origin, content, query, context):
  304. return self.handler.on_context_state_request(
  305. origin,
  306. context,
  307. parse_string_from_args(query, "event_id", None, required=True),
  308. )
  309. class FederationStateIdsServlet(BaseFederationServlet):
  310. PATH = "/state_ids/(?P<room_id>[^/]*)/?"
  311. def on_GET(self, origin, content, query, room_id):
  312. return self.handler.on_state_ids_request(
  313. origin,
  314. room_id,
  315. parse_string_from_args(query, "event_id", None, required=True),
  316. )
  317. class FederationBackfillServlet(BaseFederationServlet):
  318. PATH = "/backfill/(?P<context>[^/]*)/?"
  319. def on_GET(self, origin, content, query, context):
  320. versions = [x.decode("ascii") for x in query[b"v"]]
  321. limit = parse_integer_from_args(query, "limit", None)
  322. if not limit:
  323. return defer.succeed((400, {"error": "Did not include limit param"}))
  324. return self.handler.on_backfill_request(origin, context, versions, limit)
  325. class FederationQueryServlet(BaseFederationServlet):
  326. PATH = "/query/(?P<query_type>[^/]*)"
  327. # This is when we receive a server-server Query
  328. def on_GET(self, origin, content, query, query_type):
  329. return self.handler.on_query_request(
  330. query_type,
  331. {k.decode("utf8"): v[0].decode("utf-8") for k, v in query.items()},
  332. )
  333. class FederationMakeJoinServlet(BaseFederationServlet):
  334. PATH = "/make_join/(?P<context>[^/]*)/(?P<user_id>[^/]*)"
  335. @defer.inlineCallbacks
  336. def on_GET(self, origin, _content, query, context, user_id):
  337. """
  338. Args:
  339. origin (unicode): The authenticated server_name of the calling server
  340. _content (None): (GETs don't have bodies)
  341. query (dict[bytes, list[bytes]]): Query params from the request.
  342. **kwargs (dict[unicode, unicode]): the dict mapping keys to path
  343. components as specified in the path match regexp.
  344. Returns:
  345. Deferred[(int, object)|None]: either (response code, response object) to
  346. return a JSON response, or None if the request has already been handled.
  347. """
  348. versions = query.get(b"ver")
  349. if versions is not None:
  350. supported_versions = [v.decode("utf-8") for v in versions]
  351. else:
  352. supported_versions = ["1"]
  353. content = yield self.handler.on_make_join_request(
  354. origin, context, user_id, supported_versions=supported_versions
  355. )
  356. defer.returnValue((200, content))
  357. class FederationMakeLeaveServlet(BaseFederationServlet):
  358. PATH = "/make_leave/(?P<context>[^/]*)/(?P<user_id>[^/]*)"
  359. @defer.inlineCallbacks
  360. def on_GET(self, origin, content, query, context, user_id):
  361. content = yield self.handler.on_make_leave_request(origin, context, user_id)
  362. defer.returnValue((200, content))
  363. class FederationSendLeaveServlet(BaseFederationServlet):
  364. PATH = "/send_leave/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
  365. @defer.inlineCallbacks
  366. def on_PUT(self, origin, content, query, room_id, event_id):
  367. content = yield self.handler.on_send_leave_request(origin, content, room_id)
  368. defer.returnValue((200, content))
  369. class FederationEventAuthServlet(BaseFederationServlet):
  370. PATH = "/event_auth/(?P<context>[^/]*)/(?P<event_id>[^/]*)"
  371. def on_GET(self, origin, content, query, context, event_id):
  372. return self.handler.on_event_auth(origin, context, event_id)
  373. class FederationSendJoinServlet(BaseFederationServlet):
  374. PATH = "/send_join/(?P<context>[^/]*)/(?P<event_id>[^/]*)"
  375. @defer.inlineCallbacks
  376. def on_PUT(self, origin, content, query, context, event_id):
  377. # TODO(paul): assert that context/event_id parsed from path actually
  378. # match those given in content
  379. content = yield self.handler.on_send_join_request(origin, content, context)
  380. defer.returnValue((200, content))
  381. class FederationV1InviteServlet(BaseFederationServlet):
  382. PATH = "/invite/(?P<context>[^/]*)/(?P<event_id>[^/]*)"
  383. @defer.inlineCallbacks
  384. def on_PUT(self, origin, content, query, context, event_id):
  385. # We don't get a room version, so we have to assume its EITHER v1 or
  386. # v2. This is "fine" as the only difference between V1 and V2 is the
  387. # state resolution algorithm, and we don't use that for processing
  388. # invites
  389. content = yield self.handler.on_invite_request(
  390. origin, content, room_version=RoomVersions.V1.identifier
  391. )
  392. # V1 federation API is defined to return a content of `[200, {...}]`
  393. # due to a historical bug.
  394. defer.returnValue((200, (200, content)))
  395. class FederationV2InviteServlet(BaseFederationServlet):
  396. PATH = "/invite/(?P<context>[^/]*)/(?P<event_id>[^/]*)"
  397. PREFIX = FEDERATION_V2_PREFIX
  398. @defer.inlineCallbacks
  399. def on_PUT(self, origin, content, query, context, event_id):
  400. # TODO(paul): assert that context/event_id parsed from path actually
  401. # match those given in content
  402. room_version = content["room_version"]
  403. event = content["event"]
  404. invite_room_state = content["invite_room_state"]
  405. # Synapse expects invite_room_state to be in unsigned, as it is in v1
  406. # API
  407. event.setdefault("unsigned", {})["invite_room_state"] = invite_room_state
  408. content = yield self.handler.on_invite_request(
  409. origin, event, room_version=room_version
  410. )
  411. defer.returnValue((200, content))
  412. class FederationThirdPartyInviteExchangeServlet(BaseFederationServlet):
  413. PATH = "/exchange_third_party_invite/(?P<room_id>[^/]*)"
  414. @defer.inlineCallbacks
  415. def on_PUT(self, origin, content, query, room_id):
  416. content = yield self.handler.on_exchange_third_party_invite_request(
  417. origin, room_id, content
  418. )
  419. defer.returnValue((200, content))
  420. class FederationClientKeysQueryServlet(BaseFederationServlet):
  421. PATH = "/user/keys/query"
  422. def on_POST(self, origin, content, query):
  423. return self.handler.on_query_client_keys(origin, content)
  424. class FederationUserDevicesQueryServlet(BaseFederationServlet):
  425. PATH = "/user/devices/(?P<user_id>[^/]*)"
  426. def on_GET(self, origin, content, query, user_id):
  427. return self.handler.on_query_user_devices(origin, user_id)
  428. class FederationClientKeysClaimServlet(BaseFederationServlet):
  429. PATH = "/user/keys/claim"
  430. @defer.inlineCallbacks
  431. def on_POST(self, origin, content, query):
  432. response = yield self.handler.on_claim_client_keys(origin, content)
  433. defer.returnValue((200, response))
  434. class FederationQueryAuthServlet(BaseFederationServlet):
  435. PATH = "/query_auth/(?P<context>[^/]*)/(?P<event_id>[^/]*)"
  436. @defer.inlineCallbacks
  437. def on_POST(self, origin, content, query, context, event_id):
  438. new_content = yield self.handler.on_query_auth_request(
  439. origin, content, context, event_id
  440. )
  441. defer.returnValue((200, new_content))
  442. class FederationGetMissingEventsServlet(BaseFederationServlet):
  443. # TODO(paul): Why does this path alone end with "/?" optional?
  444. PATH = "/get_missing_events/(?P<room_id>[^/]*)/?"
  445. @defer.inlineCallbacks
  446. def on_POST(self, origin, content, query, room_id):
  447. limit = int(content.get("limit", 10))
  448. earliest_events = content.get("earliest_events", [])
  449. latest_events = content.get("latest_events", [])
  450. content = yield self.handler.on_get_missing_events(
  451. origin,
  452. room_id=room_id,
  453. earliest_events=earliest_events,
  454. latest_events=latest_events,
  455. limit=limit,
  456. )
  457. defer.returnValue((200, content))
  458. class On3pidBindServlet(BaseFederationServlet):
  459. PATH = "/3pid/onbind"
  460. REQUIRE_AUTH = False
  461. @defer.inlineCallbacks
  462. def on_POST(self, origin, content, query):
  463. if "invites" in content:
  464. last_exception = None
  465. for invite in content["invites"]:
  466. try:
  467. if "signed" not in invite or "token" not in invite["signed"]:
  468. message = (
  469. "Rejecting received notification of third-"
  470. "party invite without signed: %s" % (invite,)
  471. )
  472. logger.info(message)
  473. raise SynapseError(400, message)
  474. yield self.handler.exchange_third_party_invite(
  475. invite["sender"],
  476. invite["mxid"],
  477. invite["room_id"],
  478. invite["signed"],
  479. )
  480. except Exception as e:
  481. last_exception = e
  482. if last_exception:
  483. raise last_exception
  484. defer.returnValue((200, {}))
  485. class OpenIdUserInfo(BaseFederationServlet):
  486. """
  487. Exchange a bearer token for information about a user.
  488. The response format should be compatible with:
  489. http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
  490. GET /openid/userinfo?access_token=ABDEFGH HTTP/1.1
  491. HTTP/1.1 200 OK
  492. Content-Type: application/json
  493. {
  494. "sub": "@userpart:example.org",
  495. }
  496. """
  497. PATH = "/openid/userinfo"
  498. REQUIRE_AUTH = False
  499. @defer.inlineCallbacks
  500. def on_GET(self, origin, content, query):
  501. token = query.get(b"access_token", [None])[0]
  502. if token is None:
  503. defer.returnValue(
  504. (401, {"errcode": "M_MISSING_TOKEN", "error": "Access Token required"})
  505. )
  506. return
  507. user_id = yield self.handler.on_openid_userinfo(token.decode("ascii"))
  508. if user_id is None:
  509. defer.returnValue(
  510. (
  511. 401,
  512. {
  513. "errcode": "M_UNKNOWN_TOKEN",
  514. "error": "Access Token unknown or expired",
  515. },
  516. )
  517. )
  518. defer.returnValue((200, {"sub": user_id}))
  519. class PublicRoomList(BaseFederationServlet):
  520. """
  521. Fetch the public room list for this server.
  522. This API returns information in the same format as /publicRooms on the
  523. client API, but will only ever include local public rooms and hence is
  524. intended for consumption by other home servers.
  525. GET /publicRooms HTTP/1.1
  526. HTTP/1.1 200 OK
  527. Content-Type: application/json
  528. {
  529. "chunk": [
  530. {
  531. "aliases": [
  532. "#test:localhost"
  533. ],
  534. "guest_can_join": false,
  535. "name": "test room",
  536. "num_joined_members": 3,
  537. "room_id": "!whkydVegtvatLfXmPN:localhost",
  538. "world_readable": false
  539. }
  540. ],
  541. "end": "END",
  542. "start": "START"
  543. }
  544. """
  545. PATH = "/publicRooms"
  546. def __init__(self, handler, authenticator, ratelimiter, server_name, allow_access):
  547. super(PublicRoomList, self).__init__(
  548. handler, authenticator, ratelimiter, server_name
  549. )
  550. self.allow_access = allow_access
  551. @defer.inlineCallbacks
  552. def on_GET(self, origin, content, query):
  553. if not self.allow_access:
  554. raise FederationDeniedError(origin)
  555. limit = parse_integer_from_args(query, "limit", 0)
  556. since_token = parse_string_from_args(query, "since", None)
  557. include_all_networks = parse_boolean_from_args(
  558. query, "include_all_networks", False
  559. )
  560. third_party_instance_id = parse_string_from_args(
  561. query, "third_party_instance_id", None
  562. )
  563. if include_all_networks:
  564. network_tuple = None
  565. elif third_party_instance_id:
  566. network_tuple = ThirdPartyInstanceID.from_string(third_party_instance_id)
  567. else:
  568. network_tuple = ThirdPartyInstanceID(None, None)
  569. data = yield self.handler.get_local_public_room_list(
  570. limit, since_token, network_tuple=network_tuple, from_federation=True
  571. )
  572. defer.returnValue((200, data))
  573. class FederationVersionServlet(BaseFederationServlet):
  574. PATH = "/version"
  575. REQUIRE_AUTH = False
  576. def on_GET(self, origin, content, query):
  577. return defer.succeed(
  578. (
  579. 200,
  580. {"server": {"name": "Synapse", "version": get_version_string(synapse)}},
  581. )
  582. )
  583. class FederationGroupsProfileServlet(BaseFederationServlet):
  584. """Get/set the basic profile of a group on behalf of a user
  585. """
  586. PATH = "/groups/(?P<group_id>[^/]*)/profile"
  587. @defer.inlineCallbacks
  588. def on_GET(self, origin, content, query, group_id):
  589. requester_user_id = parse_string_from_args(query, "requester_user_id")
  590. if get_domain_from_id(requester_user_id) != origin:
  591. raise SynapseError(403, "requester_user_id doesn't match origin")
  592. new_content = yield self.handler.get_group_profile(group_id, requester_user_id)
  593. defer.returnValue((200, new_content))
  594. @defer.inlineCallbacks
  595. def on_POST(self, origin, content, query, group_id):
  596. requester_user_id = parse_string_from_args(query, "requester_user_id")
  597. if get_domain_from_id(requester_user_id) != origin:
  598. raise SynapseError(403, "requester_user_id doesn't match origin")
  599. new_content = yield self.handler.update_group_profile(
  600. group_id, requester_user_id, content
  601. )
  602. defer.returnValue((200, new_content))
  603. class FederationGroupsSummaryServlet(BaseFederationServlet):
  604. PATH = "/groups/(?P<group_id>[^/]*)/summary"
  605. @defer.inlineCallbacks
  606. def on_GET(self, origin, content, query, group_id):
  607. requester_user_id = parse_string_from_args(query, "requester_user_id")
  608. if get_domain_from_id(requester_user_id) != origin:
  609. raise SynapseError(403, "requester_user_id doesn't match origin")
  610. new_content = yield self.handler.get_group_summary(group_id, requester_user_id)
  611. defer.returnValue((200, new_content))
  612. class FederationGroupsRoomsServlet(BaseFederationServlet):
  613. """Get the rooms in a group on behalf of a user
  614. """
  615. PATH = "/groups/(?P<group_id>[^/]*)/rooms"
  616. @defer.inlineCallbacks
  617. def on_GET(self, origin, content, query, group_id):
  618. requester_user_id = parse_string_from_args(query, "requester_user_id")
  619. if get_domain_from_id(requester_user_id) != origin:
  620. raise SynapseError(403, "requester_user_id doesn't match origin")
  621. new_content = yield self.handler.get_rooms_in_group(group_id, requester_user_id)
  622. defer.returnValue((200, new_content))
  623. class FederationGroupsAddRoomsServlet(BaseFederationServlet):
  624. """Add/remove room from group
  625. """
  626. PATH = "/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"
  627. @defer.inlineCallbacks
  628. def on_POST(self, origin, content, query, group_id, room_id):
  629. requester_user_id = parse_string_from_args(query, "requester_user_id")
  630. if get_domain_from_id(requester_user_id) != origin:
  631. raise SynapseError(403, "requester_user_id doesn't match origin")
  632. new_content = yield self.handler.add_room_to_group(
  633. group_id, requester_user_id, room_id, content
  634. )
  635. defer.returnValue((200, new_content))
  636. @defer.inlineCallbacks
  637. def on_DELETE(self, origin, content, query, group_id, room_id):
  638. requester_user_id = parse_string_from_args(query, "requester_user_id")
  639. if get_domain_from_id(requester_user_id) != origin:
  640. raise SynapseError(403, "requester_user_id doesn't match origin")
  641. new_content = yield self.handler.remove_room_from_group(
  642. group_id, requester_user_id, room_id
  643. )
  644. defer.returnValue((200, new_content))
  645. class FederationGroupsAddRoomsConfigServlet(BaseFederationServlet):
  646. """Update room config in group
  647. """
  648. PATH = (
  649. "/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"
  650. "/config/(?P<config_key>[^/]*)"
  651. )
  652. @defer.inlineCallbacks
  653. def on_POST(self, origin, content, query, group_id, room_id, config_key):
  654. requester_user_id = parse_string_from_args(query, "requester_user_id")
  655. if get_domain_from_id(requester_user_id) != origin:
  656. raise SynapseError(403, "requester_user_id doesn't match origin")
  657. result = yield self.groups_handler.update_room_in_group(
  658. group_id, requester_user_id, room_id, config_key, content
  659. )
  660. defer.returnValue((200, result))
  661. class FederationGroupsUsersServlet(BaseFederationServlet):
  662. """Get the users in a group on behalf of a user
  663. """
  664. PATH = "/groups/(?P<group_id>[^/]*)/users"
  665. @defer.inlineCallbacks
  666. def on_GET(self, origin, content, query, group_id):
  667. requester_user_id = parse_string_from_args(query, "requester_user_id")
  668. if get_domain_from_id(requester_user_id) != origin:
  669. raise SynapseError(403, "requester_user_id doesn't match origin")
  670. new_content = yield self.handler.get_users_in_group(group_id, requester_user_id)
  671. defer.returnValue((200, new_content))
  672. class FederationGroupsInvitedUsersServlet(BaseFederationServlet):
  673. """Get the users that have been invited to a group
  674. """
  675. PATH = "/groups/(?P<group_id>[^/]*)/invited_users"
  676. @defer.inlineCallbacks
  677. def on_GET(self, origin, content, query, group_id):
  678. requester_user_id = parse_string_from_args(query, "requester_user_id")
  679. if get_domain_from_id(requester_user_id) != origin:
  680. raise SynapseError(403, "requester_user_id doesn't match origin")
  681. new_content = yield self.handler.get_invited_users_in_group(
  682. group_id, requester_user_id
  683. )
  684. defer.returnValue((200, new_content))
  685. class FederationGroupsInviteServlet(BaseFederationServlet):
  686. """Ask a group server to invite someone to the group
  687. """
  688. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite"
  689. @defer.inlineCallbacks
  690. def on_POST(self, origin, content, query, group_id, user_id):
  691. requester_user_id = parse_string_from_args(query, "requester_user_id")
  692. if get_domain_from_id(requester_user_id) != origin:
  693. raise SynapseError(403, "requester_user_id doesn't match origin")
  694. new_content = yield self.handler.invite_to_group(
  695. group_id, user_id, requester_user_id, content
  696. )
  697. defer.returnValue((200, new_content))
  698. class FederationGroupsAcceptInviteServlet(BaseFederationServlet):
  699. """Accept an invitation from the group server
  700. """
  701. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/accept_invite"
  702. @defer.inlineCallbacks
  703. def on_POST(self, origin, content, query, group_id, user_id):
  704. if get_domain_from_id(user_id) != origin:
  705. raise SynapseError(403, "user_id doesn't match origin")
  706. new_content = yield self.handler.accept_invite(group_id, user_id, content)
  707. defer.returnValue((200, new_content))
  708. class FederationGroupsJoinServlet(BaseFederationServlet):
  709. """Attempt to join a group
  710. """
  711. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/join"
  712. @defer.inlineCallbacks
  713. def on_POST(self, origin, content, query, group_id, user_id):
  714. if get_domain_from_id(user_id) != origin:
  715. raise SynapseError(403, "user_id doesn't match origin")
  716. new_content = yield self.handler.join_group(group_id, user_id, content)
  717. defer.returnValue((200, new_content))
  718. class FederationGroupsRemoveUserServlet(BaseFederationServlet):
  719. """Leave or kick a user from the group
  720. """
  721. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove"
  722. @defer.inlineCallbacks
  723. def on_POST(self, origin, content, query, group_id, user_id):
  724. requester_user_id = parse_string_from_args(query, "requester_user_id")
  725. if get_domain_from_id(requester_user_id) != origin:
  726. raise SynapseError(403, "requester_user_id doesn't match origin")
  727. new_content = yield self.handler.remove_user_from_group(
  728. group_id, user_id, requester_user_id, content
  729. )
  730. defer.returnValue((200, new_content))
  731. class FederationGroupsLocalInviteServlet(BaseFederationServlet):
  732. """A group server has invited a local user
  733. """
  734. PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite"
  735. @defer.inlineCallbacks
  736. def on_POST(self, origin, content, query, group_id, user_id):
  737. if get_domain_from_id(group_id) != origin:
  738. raise SynapseError(403, "group_id doesn't match origin")
  739. new_content = yield self.handler.on_invite(group_id, user_id, content)
  740. defer.returnValue((200, new_content))
  741. class FederationGroupsRemoveLocalUserServlet(BaseFederationServlet):
  742. """A group server has removed a local user
  743. """
  744. PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove"
  745. @defer.inlineCallbacks
  746. def on_POST(self, origin, content, query, group_id, user_id):
  747. if get_domain_from_id(group_id) != origin:
  748. raise SynapseError(403, "user_id doesn't match origin")
  749. new_content = yield self.handler.user_removed_from_group(
  750. group_id, user_id, content
  751. )
  752. defer.returnValue((200, new_content))
  753. class FederationGroupsRenewAttestaionServlet(BaseFederationServlet):
  754. """A group or user's server renews their attestation
  755. """
  756. PATH = "/groups/(?P<group_id>[^/]*)/renew_attestation/(?P<user_id>[^/]*)"
  757. @defer.inlineCallbacks
  758. def on_POST(self, origin, content, query, group_id, user_id):
  759. # We don't need to check auth here as we check the attestation signatures
  760. new_content = yield self.handler.on_renew_attestation(
  761. group_id, user_id, content
  762. )
  763. defer.returnValue((200, new_content))
  764. class FederationGroupsSummaryRoomsServlet(BaseFederationServlet):
  765. """Add/remove a room from the group summary, with optional category.
  766. Matches both:
  767. - /groups/:group/summary/rooms/:room_id
  768. - /groups/:group/summary/categories/:category/rooms/:room_id
  769. """
  770. PATH = (
  771. "/groups/(?P<group_id>[^/]*)/summary"
  772. "(/categories/(?P<category_id>[^/]+))?"
  773. "/rooms/(?P<room_id>[^/]*)"
  774. )
  775. @defer.inlineCallbacks
  776. def on_POST(self, origin, content, query, group_id, category_id, room_id):
  777. requester_user_id = parse_string_from_args(query, "requester_user_id")
  778. if get_domain_from_id(requester_user_id) != origin:
  779. raise SynapseError(403, "requester_user_id doesn't match origin")
  780. if category_id == "":
  781. raise SynapseError(400, "category_id cannot be empty string")
  782. resp = yield self.handler.update_group_summary_room(
  783. group_id,
  784. requester_user_id,
  785. room_id=room_id,
  786. category_id=category_id,
  787. content=content,
  788. )
  789. defer.returnValue((200, resp))
  790. @defer.inlineCallbacks
  791. def on_DELETE(self, origin, content, query, group_id, category_id, room_id):
  792. requester_user_id = parse_string_from_args(query, "requester_user_id")
  793. if get_domain_from_id(requester_user_id) != origin:
  794. raise SynapseError(403, "requester_user_id doesn't match origin")
  795. if category_id == "":
  796. raise SynapseError(400, "category_id cannot be empty string")
  797. resp = yield self.handler.delete_group_summary_room(
  798. group_id, requester_user_id, room_id=room_id, category_id=category_id
  799. )
  800. defer.returnValue((200, resp))
  801. class FederationGroupsCategoriesServlet(BaseFederationServlet):
  802. """Get all categories for a group
  803. """
  804. PATH = "/groups/(?P<group_id>[^/]*)/categories/?"
  805. @defer.inlineCallbacks
  806. def on_GET(self, origin, content, query, group_id):
  807. requester_user_id = parse_string_from_args(query, "requester_user_id")
  808. if get_domain_from_id(requester_user_id) != origin:
  809. raise SynapseError(403, "requester_user_id doesn't match origin")
  810. resp = yield self.handler.get_group_categories(group_id, requester_user_id)
  811. defer.returnValue((200, resp))
  812. class FederationGroupsCategoryServlet(BaseFederationServlet):
  813. """Add/remove/get a category in a group
  814. """
  815. PATH = "/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)"
  816. @defer.inlineCallbacks
  817. def on_GET(self, origin, content, query, group_id, category_id):
  818. requester_user_id = parse_string_from_args(query, "requester_user_id")
  819. if get_domain_from_id(requester_user_id) != origin:
  820. raise SynapseError(403, "requester_user_id doesn't match origin")
  821. resp = yield self.handler.get_group_category(
  822. group_id, requester_user_id, category_id
  823. )
  824. defer.returnValue((200, resp))
  825. @defer.inlineCallbacks
  826. def on_POST(self, origin, content, query, group_id, category_id):
  827. requester_user_id = parse_string_from_args(query, "requester_user_id")
  828. if get_domain_from_id(requester_user_id) != origin:
  829. raise SynapseError(403, "requester_user_id doesn't match origin")
  830. if category_id == "":
  831. raise SynapseError(400, "category_id cannot be empty string")
  832. resp = yield self.handler.upsert_group_category(
  833. group_id, requester_user_id, category_id, content
  834. )
  835. defer.returnValue((200, resp))
  836. @defer.inlineCallbacks
  837. def on_DELETE(self, origin, content, query, group_id, category_id):
  838. requester_user_id = parse_string_from_args(query, "requester_user_id")
  839. if get_domain_from_id(requester_user_id) != origin:
  840. raise SynapseError(403, "requester_user_id doesn't match origin")
  841. if category_id == "":
  842. raise SynapseError(400, "category_id cannot be empty string")
  843. resp = yield self.handler.delete_group_category(
  844. group_id, requester_user_id, category_id
  845. )
  846. defer.returnValue((200, resp))
  847. class FederationGroupsRolesServlet(BaseFederationServlet):
  848. """Get roles in a group
  849. """
  850. PATH = "/groups/(?P<group_id>[^/]*)/roles/?"
  851. @defer.inlineCallbacks
  852. def on_GET(self, origin, content, query, group_id):
  853. requester_user_id = parse_string_from_args(query, "requester_user_id")
  854. if get_domain_from_id(requester_user_id) != origin:
  855. raise SynapseError(403, "requester_user_id doesn't match origin")
  856. resp = yield self.handler.get_group_roles(group_id, requester_user_id)
  857. defer.returnValue((200, resp))
  858. class FederationGroupsRoleServlet(BaseFederationServlet):
  859. """Add/remove/get a role in a group
  860. """
  861. PATH = "/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)"
  862. @defer.inlineCallbacks
  863. def on_GET(self, origin, content, query, group_id, role_id):
  864. requester_user_id = parse_string_from_args(query, "requester_user_id")
  865. if get_domain_from_id(requester_user_id) != origin:
  866. raise SynapseError(403, "requester_user_id doesn't match origin")
  867. resp = yield self.handler.get_group_role(group_id, requester_user_id, role_id)
  868. defer.returnValue((200, resp))
  869. @defer.inlineCallbacks
  870. def on_POST(self, origin, content, query, group_id, role_id):
  871. requester_user_id = parse_string_from_args(query, "requester_user_id")
  872. if get_domain_from_id(requester_user_id) != origin:
  873. raise SynapseError(403, "requester_user_id doesn't match origin")
  874. if role_id == "":
  875. raise SynapseError(400, "role_id cannot be empty string")
  876. resp = yield self.handler.update_group_role(
  877. group_id, requester_user_id, role_id, content
  878. )
  879. defer.returnValue((200, resp))
  880. @defer.inlineCallbacks
  881. def on_DELETE(self, origin, content, query, group_id, role_id):
  882. requester_user_id = parse_string_from_args(query, "requester_user_id")
  883. if get_domain_from_id(requester_user_id) != origin:
  884. raise SynapseError(403, "requester_user_id doesn't match origin")
  885. if role_id == "":
  886. raise SynapseError(400, "role_id cannot be empty string")
  887. resp = yield self.handler.delete_group_role(
  888. group_id, requester_user_id, role_id
  889. )
  890. defer.returnValue((200, resp))
  891. class FederationGroupsSummaryUsersServlet(BaseFederationServlet):
  892. """Add/remove a user from the group summary, with optional role.
  893. Matches both:
  894. - /groups/:group/summary/users/:user_id
  895. - /groups/:group/summary/roles/:role/users/:user_id
  896. """
  897. PATH = (
  898. "/groups/(?P<group_id>[^/]*)/summary"
  899. "(/roles/(?P<role_id>[^/]+))?"
  900. "/users/(?P<user_id>[^/]*)"
  901. )
  902. @defer.inlineCallbacks
  903. def on_POST(self, origin, content, query, group_id, role_id, user_id):
  904. requester_user_id = parse_string_from_args(query, "requester_user_id")
  905. if get_domain_from_id(requester_user_id) != origin:
  906. raise SynapseError(403, "requester_user_id doesn't match origin")
  907. if role_id == "":
  908. raise SynapseError(400, "role_id cannot be empty string")
  909. resp = yield self.handler.update_group_summary_user(
  910. group_id,
  911. requester_user_id,
  912. user_id=user_id,
  913. role_id=role_id,
  914. content=content,
  915. )
  916. defer.returnValue((200, resp))
  917. @defer.inlineCallbacks
  918. def on_DELETE(self, origin, content, query, group_id, role_id, user_id):
  919. requester_user_id = parse_string_from_args(query, "requester_user_id")
  920. if get_domain_from_id(requester_user_id) != origin:
  921. raise SynapseError(403, "requester_user_id doesn't match origin")
  922. if role_id == "":
  923. raise SynapseError(400, "role_id cannot be empty string")
  924. resp = yield self.handler.delete_group_summary_user(
  925. group_id, requester_user_id, user_id=user_id, role_id=role_id
  926. )
  927. defer.returnValue((200, resp))
  928. class FederationGroupsBulkPublicisedServlet(BaseFederationServlet):
  929. """Get roles in a group
  930. """
  931. PATH = "/get_groups_publicised"
  932. @defer.inlineCallbacks
  933. def on_POST(self, origin, content, query):
  934. resp = yield self.handler.bulk_get_publicised_groups(
  935. content["user_ids"], proxy=False
  936. )
  937. defer.returnValue((200, resp))
  938. class FederationGroupsSettingJoinPolicyServlet(BaseFederationServlet):
  939. """Sets whether a group is joinable without an invite or knock
  940. """
  941. PATH = "/groups/(?P<group_id>[^/]*)/settings/m.join_policy"
  942. @defer.inlineCallbacks
  943. def on_PUT(self, origin, content, query, group_id):
  944. requester_user_id = parse_string_from_args(query, "requester_user_id")
  945. if get_domain_from_id(requester_user_id) != origin:
  946. raise SynapseError(403, "requester_user_id doesn't match origin")
  947. new_content = yield self.handler.set_group_join_policy(
  948. group_id, requester_user_id, content
  949. )
  950. defer.returnValue((200, new_content))
  951. class RoomComplexityServlet(BaseFederationServlet):
  952. """
  953. Indicates to other servers how complex (and therefore likely
  954. resource-intensive) a public room this server knows about is.
  955. """
  956. PATH = "/rooms/(?P<room_id>[^/]*)/complexity"
  957. PREFIX = FEDERATION_UNSTABLE_PREFIX
  958. @defer.inlineCallbacks
  959. def on_GET(self, origin, content, query, room_id):
  960. store = self.handler.hs.get_datastore()
  961. is_public = yield store.is_room_world_readable_or_publicly_joinable(room_id)
  962. if not is_public:
  963. raise SynapseError(404, "Room not found", errcode=Codes.INVALID_PARAM)
  964. complexity = yield store.get_room_complexity(room_id)
  965. defer.returnValue((200, complexity))
  966. FEDERATION_SERVLET_CLASSES = (
  967. FederationSendServlet,
  968. FederationEventServlet,
  969. FederationStateServlet,
  970. FederationStateIdsServlet,
  971. FederationBackfillServlet,
  972. FederationQueryServlet,
  973. FederationMakeJoinServlet,
  974. FederationMakeLeaveServlet,
  975. FederationEventServlet,
  976. FederationSendJoinServlet,
  977. FederationSendLeaveServlet,
  978. FederationV1InviteServlet,
  979. FederationV2InviteServlet,
  980. FederationQueryAuthServlet,
  981. FederationGetMissingEventsServlet,
  982. FederationEventAuthServlet,
  983. FederationClientKeysQueryServlet,
  984. FederationUserDevicesQueryServlet,
  985. FederationClientKeysClaimServlet,
  986. FederationThirdPartyInviteExchangeServlet,
  987. On3pidBindServlet,
  988. FederationVersionServlet,
  989. RoomComplexityServlet,
  990. )
  991. OPENID_SERVLET_CLASSES = (OpenIdUserInfo,)
  992. ROOM_LIST_CLASSES = (PublicRoomList,)
  993. GROUP_SERVER_SERVLET_CLASSES = (
  994. FederationGroupsProfileServlet,
  995. FederationGroupsSummaryServlet,
  996. FederationGroupsRoomsServlet,
  997. FederationGroupsUsersServlet,
  998. FederationGroupsInvitedUsersServlet,
  999. FederationGroupsInviteServlet,
  1000. FederationGroupsAcceptInviteServlet,
  1001. FederationGroupsJoinServlet,
  1002. FederationGroupsRemoveUserServlet,
  1003. FederationGroupsSummaryRoomsServlet,
  1004. FederationGroupsCategoriesServlet,
  1005. FederationGroupsCategoryServlet,
  1006. FederationGroupsRolesServlet,
  1007. FederationGroupsRoleServlet,
  1008. FederationGroupsSummaryUsersServlet,
  1009. FederationGroupsAddRoomsServlet,
  1010. FederationGroupsAddRoomsConfigServlet,
  1011. FederationGroupsSettingJoinPolicyServlet,
  1012. )
  1013. GROUP_LOCAL_SERVLET_CLASSES = (
  1014. FederationGroupsLocalInviteServlet,
  1015. FederationGroupsRemoveLocalUserServlet,
  1016. FederationGroupsBulkPublicisedServlet,
  1017. )
  1018. GROUP_ATTESTATION_SERVLET_CLASSES = (FederationGroupsRenewAttestaionServlet,)
  1019. DEFAULT_SERVLET_GROUPS = (
  1020. "federation",
  1021. "room_list",
  1022. "group_server",
  1023. "group_local",
  1024. "group_attestation",
  1025. "openid",
  1026. )
  1027. def register_servlets(hs, resource, authenticator, ratelimiter, servlet_groups=None):
  1028. """Initialize and register servlet classes.
  1029. Will by default register all servlets. For custom behaviour, pass in
  1030. a list of servlet_groups to register.
  1031. Args:
  1032. hs (synapse.server.HomeServer): homeserver
  1033. resource (TransportLayerServer): resource class to register to
  1034. authenticator (Authenticator): authenticator to use
  1035. ratelimiter (util.ratelimitutils.FederationRateLimiter): ratelimiter to use
  1036. servlet_groups (list[str], optional): List of servlet groups to register.
  1037. Defaults to ``DEFAULT_SERVLET_GROUPS``.
  1038. """
  1039. if not servlet_groups:
  1040. servlet_groups = DEFAULT_SERVLET_GROUPS
  1041. if "federation" in servlet_groups:
  1042. for servletclass in FEDERATION_SERVLET_CLASSES:
  1043. servletclass(
  1044. handler=hs.get_federation_server(),
  1045. authenticator=authenticator,
  1046. ratelimiter=ratelimiter,
  1047. server_name=hs.hostname,
  1048. ).register(resource)
  1049. if "openid" in servlet_groups:
  1050. for servletclass in OPENID_SERVLET_CLASSES:
  1051. servletclass(
  1052. handler=hs.get_federation_server(),
  1053. authenticator=authenticator,
  1054. ratelimiter=ratelimiter,
  1055. server_name=hs.hostname,
  1056. ).register(resource)
  1057. if "room_list" in servlet_groups:
  1058. for servletclass in ROOM_LIST_CLASSES:
  1059. servletclass(
  1060. handler=hs.get_room_list_handler(),
  1061. authenticator=authenticator,
  1062. ratelimiter=ratelimiter,
  1063. server_name=hs.hostname,
  1064. allow_access=hs.config.allow_public_rooms_over_federation,
  1065. ).register(resource)
  1066. if "group_server" in servlet_groups:
  1067. for servletclass in GROUP_SERVER_SERVLET_CLASSES:
  1068. servletclass(
  1069. handler=hs.get_groups_server_handler(),
  1070. authenticator=authenticator,
  1071. ratelimiter=ratelimiter,
  1072. server_name=hs.hostname,
  1073. ).register(resource)
  1074. if "group_local" in servlet_groups:
  1075. for servletclass in GROUP_LOCAL_SERVLET_CLASSES:
  1076. servletclass(
  1077. handler=hs.get_groups_local_handler(),
  1078. authenticator=authenticator,
  1079. ratelimiter=ratelimiter,
  1080. server_name=hs.hostname,
  1081. ).register(resource)
  1082. if "group_attestation" in servlet_groups:
  1083. for servletclass in GROUP_ATTESTATION_SERVLET_CLASSES:
  1084. servletclass(
  1085. handler=hs.get_groups_attestation_renewer(),
  1086. authenticator=authenticator,
  1087. ratelimiter=ratelimiter,
  1088. server_name=hs.hostname,
  1089. ).register(resource)