membership.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2018 New Vector 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. import logging
  16. from twisted.internet import defer
  17. from synapse.http.servlet import parse_json_object_from_request
  18. from synapse.replication.http._base import ReplicationEndpoint
  19. from synapse.types import Requester, UserID
  20. from synapse.util.distributor import user_joined_room, user_left_room
  21. logger = logging.getLogger(__name__)
  22. class ReplicationRemoteJoinRestServlet(ReplicationEndpoint):
  23. """Does a remote join for the given user to the given room
  24. Request format:
  25. POST /_synapse/replication/remote_join/:room_id/:user_id
  26. {
  27. "requester": ...,
  28. "remote_room_hosts": [...],
  29. "content": { ... }
  30. }
  31. """
  32. NAME = "remote_join"
  33. PATH_ARGS = ("room_id", "user_id")
  34. def __init__(self, hs):
  35. super(ReplicationRemoteJoinRestServlet, self).__init__(hs)
  36. self.federation_handler = hs.get_handlers().federation_handler
  37. self.store = hs.get_datastore()
  38. self.clock = hs.get_clock()
  39. @staticmethod
  40. def _serialize_payload(requester, room_id, user_id, remote_room_hosts, content):
  41. """
  42. Args:
  43. requester(Requester)
  44. room_id (str)
  45. user_id (str)
  46. remote_room_hosts (list[str]): Servers to try and join via
  47. content(dict): The event content to use for the join event
  48. """
  49. return {
  50. "requester": requester.serialize(),
  51. "remote_room_hosts": remote_room_hosts,
  52. "content": content,
  53. }
  54. @defer.inlineCallbacks
  55. def _handle_request(self, request, room_id, user_id):
  56. content = parse_json_object_from_request(request)
  57. remote_room_hosts = content["remote_room_hosts"]
  58. event_content = content["content"]
  59. requester = Requester.deserialize(self.store, content["requester"])
  60. if requester.user:
  61. request.authenticated_entity = requester.user.to_string()
  62. logger.info("remote_join: %s into room: %s", user_id, room_id)
  63. yield self.federation_handler.do_invite_join(
  64. remote_room_hosts, room_id, user_id, event_content
  65. )
  66. return (200, {})
  67. class ReplicationRemoteRejectInviteRestServlet(ReplicationEndpoint):
  68. """Rejects the invite for the user and room.
  69. Request format:
  70. POST /_synapse/replication/remote_reject_invite/:room_id/:user_id
  71. {
  72. "requester": ...,
  73. "remote_room_hosts": [...],
  74. }
  75. """
  76. NAME = "remote_reject_invite"
  77. PATH_ARGS = ("room_id", "user_id")
  78. def __init__(self, hs):
  79. super(ReplicationRemoteRejectInviteRestServlet, self).__init__(hs)
  80. self.federation_handler = hs.get_handlers().federation_handler
  81. self.store = hs.get_datastore()
  82. self.clock = hs.get_clock()
  83. @staticmethod
  84. def _serialize_payload(requester, room_id, user_id, remote_room_hosts):
  85. """
  86. Args:
  87. requester(Requester)
  88. room_id (str)
  89. user_id (str)
  90. remote_room_hosts (list[str]): Servers to try and reject via
  91. """
  92. return {
  93. "requester": requester.serialize(),
  94. "remote_room_hosts": remote_room_hosts,
  95. }
  96. @defer.inlineCallbacks
  97. def _handle_request(self, request, room_id, user_id):
  98. content = parse_json_object_from_request(request)
  99. remote_room_hosts = content["remote_room_hosts"]
  100. requester = Requester.deserialize(self.store, content["requester"])
  101. if requester.user:
  102. request.authenticated_entity = requester.user.to_string()
  103. logger.info("remote_reject_invite: %s out of room: %s", user_id, room_id)
  104. try:
  105. event = yield self.federation_handler.do_remotely_reject_invite(
  106. remote_room_hosts, room_id, user_id
  107. )
  108. ret = event.get_pdu_json()
  109. except Exception as e:
  110. # if we were unable to reject the exception, just mark
  111. # it as rejected on our end and plough ahead.
  112. #
  113. # The 'except' clause is very broad, but we need to
  114. # capture everything from DNS failures upwards
  115. #
  116. logger.warn("Failed to reject invite: %s", e)
  117. yield self.store.locally_reject_invite(user_id, room_id)
  118. ret = {}
  119. return (200, ret)
  120. class ReplicationUserJoinedLeftRoomRestServlet(ReplicationEndpoint):
  121. """Notifies that a user has joined or left the room
  122. Request format:
  123. POST /_synapse/replication/membership_change/:room_id/:user_id/:change
  124. {}
  125. """
  126. NAME = "membership_change"
  127. PATH_ARGS = ("room_id", "user_id", "change")
  128. CACHE = False # No point caching as should return instantly.
  129. def __init__(self, hs):
  130. super(ReplicationUserJoinedLeftRoomRestServlet, self).__init__(hs)
  131. self.registeration_handler = hs.get_registration_handler()
  132. self.store = hs.get_datastore()
  133. self.clock = hs.get_clock()
  134. self.distributor = hs.get_distributor()
  135. @staticmethod
  136. def _serialize_payload(room_id, user_id, change):
  137. """
  138. Args:
  139. room_id (str)
  140. user_id (str)
  141. change (str): Either "joined" or "left"
  142. """
  143. assert change in ("joined", "left")
  144. return {}
  145. def _handle_request(self, request, room_id, user_id, change):
  146. logger.info("user membership change: %s in %s", user_id, room_id)
  147. user = UserID.from_string(user_id)
  148. if change == "joined":
  149. user_joined_room(self.distributor, user, room_id)
  150. elif change == "left":
  151. user_left_room(self.distributor, user, room_id)
  152. else:
  153. raise Exception("Unrecognized change: %r", change)
  154. return (200, {})
  155. def register_servlets(hs, http_server):
  156. ReplicationRemoteJoinRestServlet(hs).register(http_server)
  157. ReplicationRemoteRejectInviteRestServlet(hs).register(http_server)
  158. ReplicationUserJoinedLeftRoomRestServlet(hs).register(http_server)