resource_limits_server_notices.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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 six import iteritems
  17. from twisted.internet import defer
  18. from synapse.api.constants import (
  19. EventTypes,
  20. ServerNoticeLimitReached,
  21. ServerNoticeMsgType,
  22. )
  23. from synapse.api.errors import AuthError, ResourceLimitError, SynapseError
  24. from synapse.server_notices.server_notices_manager import SERVER_NOTICE_ROOM_TAG
  25. logger = logging.getLogger(__name__)
  26. class ResourceLimitsServerNotices(object):
  27. """ Keeps track of whether the server has reached it's resource limit and
  28. ensures that the client is kept up to date.
  29. """
  30. def __init__(self, hs):
  31. """
  32. Args:
  33. hs (synapse.server.HomeServer):
  34. """
  35. self._server_notices_manager = hs.get_server_notices_manager()
  36. self._store = hs.get_datastore()
  37. self._auth = hs.get_auth()
  38. self._config = hs.config
  39. self._resouce_limited = False
  40. self._message_handler = hs.get_message_handler()
  41. self._state = hs.get_state_handler()
  42. self._notifier = hs.get_notifier()
  43. @defer.inlineCallbacks
  44. def maybe_send_server_notice_to_user(self, user_id):
  45. """Check if we need to send a notice to this user, this will be true in
  46. two cases.
  47. 1. The server has reached its limit does not reflect this
  48. 2. The room state indicates that the server has reached its limit when
  49. actually the server is fine
  50. Args:
  51. user_id (str): user to check
  52. Returns:
  53. Deferred
  54. """
  55. if self._config.hs_disabled is True:
  56. return
  57. if self._config.limit_usage_by_mau is False:
  58. return
  59. if not self._server_notices_manager.is_enabled():
  60. # Don't try and send server notices unles they've been enabled
  61. return
  62. timestamp = yield self._store.user_last_seen_monthly_active(user_id)
  63. if timestamp is None:
  64. # This user will be blocked from receiving the notice anyway.
  65. # In practice, not sure we can ever get here
  66. return
  67. # Determine current state of room
  68. room_id = yield self._server_notices_manager.get_notice_room_for_user(user_id)
  69. if not room_id:
  70. logger.warn("Failed to get server notices room")
  71. return
  72. yield self._check_and_set_tags(user_id, room_id)
  73. currently_blocked, ref_events = yield self._is_room_currently_blocked(room_id)
  74. try:
  75. # Normally should always pass in user_id if you have it, but in
  76. # this case are checking what would happen to other users if they
  77. # were to arrive.
  78. try:
  79. yield self._auth.check_auth_blocking()
  80. is_auth_blocking = False
  81. except ResourceLimitError as e:
  82. is_auth_blocking = True
  83. event_content = e.msg
  84. event_limit_type = e.limit_type
  85. if currently_blocked and not is_auth_blocking:
  86. # Room is notifying of a block, when it ought not to be.
  87. # Remove block notification
  88. content = {
  89. "pinned": ref_events
  90. }
  91. yield self._server_notices_manager.send_notice(
  92. user_id, content, EventTypes.Pinned, '',
  93. )
  94. elif not currently_blocked and is_auth_blocking:
  95. # Room is not notifying of a block, when it ought to be.
  96. # Add block notification
  97. content = {
  98. 'body': event_content,
  99. 'msgtype': ServerNoticeMsgType,
  100. 'server_notice_type': ServerNoticeLimitReached,
  101. 'admin_contact': self._config.admin_contact,
  102. 'limit_type': event_limit_type
  103. }
  104. event = yield self._server_notices_manager.send_notice(
  105. user_id, content, EventTypes.Message,
  106. )
  107. content = {
  108. "pinned": [
  109. event.event_id,
  110. ]
  111. }
  112. yield self._server_notices_manager.send_notice(
  113. user_id, content, EventTypes.Pinned, '',
  114. )
  115. except SynapseError as e:
  116. logger.error("Error sending resource limits server notice: %s", e)
  117. @defer.inlineCallbacks
  118. def _check_and_set_tags(self, user_id, room_id):
  119. """
  120. Since server notices rooms were originally not with tags,
  121. important to check that tags have been set correctly
  122. Args:
  123. user_id(str): the user in question
  124. room_id(str): the server notices room for that user
  125. """
  126. tags = yield self._store.get_tags_for_room(user_id, room_id)
  127. need_to_set_tag = True
  128. if tags:
  129. if SERVER_NOTICE_ROOM_TAG in tags:
  130. # tag already present, nothing to do here
  131. need_to_set_tag = False
  132. if need_to_set_tag:
  133. max_id = yield self._store.add_tag_to_room(
  134. user_id, room_id, SERVER_NOTICE_ROOM_TAG, {}
  135. )
  136. self._notifier.on_new_event(
  137. "account_data_key", max_id, users=[user_id]
  138. )
  139. @defer.inlineCallbacks
  140. def _is_room_currently_blocked(self, room_id):
  141. """
  142. Determines if the room is currently blocked
  143. Args:
  144. room_id(str): The room id of the server notices room
  145. Returns:
  146. bool: Is the room currently blocked
  147. list: The list of pinned events that are unrelated to limit blocking
  148. This list can be used as a convenience in the case where the block
  149. is to be lifted and the remaining pinned event references need to be
  150. preserved
  151. """
  152. currently_blocked = False
  153. pinned_state_event = None
  154. try:
  155. pinned_state_event = yield self._state.get_current_state(
  156. room_id, event_type=EventTypes.Pinned
  157. )
  158. except AuthError:
  159. # The user has yet to join the server notices room
  160. pass
  161. referenced_events = []
  162. if pinned_state_event is not None:
  163. referenced_events = list(pinned_state_event.content.get('pinned', []))
  164. events = yield self._store.get_events(referenced_events)
  165. for event_id, event in iteritems(events):
  166. if event.type != EventTypes.Message:
  167. continue
  168. if event.content.get("msgtype") == ServerNoticeMsgType:
  169. currently_blocked = True
  170. # remove event in case we need to disable blocking later on.
  171. if event_id in referenced_events:
  172. referenced_events.remove(event.event_id)
  173. defer.returnValue((currently_blocked, referenced_events))