summary refs log tree commit diff
path: root/synapse/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/handlers')
-rw-r--r--synapse/handlers/account_validity.py32
-rw-r--r--synapse/handlers/auth.py2
-rw-r--r--synapse/handlers/deactivate_account.py37
-rw-r--r--synapse/handlers/federation.py9
-rw-r--r--synapse/handlers/message.py2
-rw-r--r--synapse/handlers/profile.py50
-rw-r--r--synapse/handlers/room_member.py16
7 files changed, 134 insertions, 14 deletions
diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py

index 396f0059f7..947237d7da 100644 --- a/synapse/handlers/account_validity.py +++ b/synapse/handlers/account_validity.py
@@ -42,6 +42,8 @@ class AccountValidityHandler(object): self.clock = self.hs.get_clock() self._account_validity = self.hs.config.account_validity + self._show_users_in_user_directory = self.hs.config.show_users_in_user_directory + self.profile_handler = self.hs.get_profile_handler() if self._account_validity.renew_by_email_enabled and load_jinja2_templates: # Don't do email-specific configuration if renewal by email is disabled. @@ -74,6 +76,12 @@ class AccountValidityHandler(object): 30 * 60 * 1000, ) + # Check every hour to remove expired users from the user directory + self.clock.looping_call( + self._mark_expired_users_as_inactive, + 60 * 60 * 1000, + ) + @defer.inlineCallbacks def send_renewal_emails(self): """Gets the list of users whose account is expiring in the amount of time @@ -261,4 +269,28 @@ class AccountValidityHandler(object): email_sent=email_sent, ) + # Check if renewed users should be reintroduced to the user directory + if self._show_users_in_user_directory: + # Show the user in the directory again by setting them to active + yield self.profile_handler.set_active(UserID.from_string(user_id), True, True) + defer.returnValue(expiration_ts) + + @defer.inlineCallbacks + def _mark_expired_users_as_inactive(self): + """Iterate over expired users. Mark them as inactive in order to hide them from the + user directory. + + Returns: + Deferred + """ + # Get expired users + expired_user_ids = yield self.store.get_expired_users() + expired_users = [ + UserID.from_string(user_id) + for user_id in expired_user_ids + ] + + # Mark each one as non-active + for user in expired_users: + yield self.profile_handler.set_active(user, False, True) diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index a0cf37a9f9..9a2ff177a6 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py
@@ -420,7 +420,7 @@ class AuthHandler(BaseHandler): # TODO: get this from the homeserver rather than creating a new one for # each request try: - client = self.hs.get_simple_http_client() + client = self.hs.get_proxied_http_client() resp_body = yield client.post_urlencoded_get_json( self.hs.config.recaptcha_siteverify_api, args={ diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 7233605d21..ac8f75d256 100644 --- a/synapse/handlers/deactivate_account.py +++ b/synapse/handlers/deactivate_account.py
@@ -121,6 +121,10 @@ class DeactivateAccountHandler(BaseHandler): # parts users from rooms (if it isn't already running) self._start_user_parting() + # Reject all pending invites for the user, so that the user doesn't show up in the + # "invited" section of rooms' members list. + yield self._reject_pending_invites_for_user(user_id) + # Remove all information on the user from the account_validity table. if self._account_validity_enabled: yield self.store.delete_account_validity_for_user(user_id) @@ -137,6 +141,39 @@ class DeactivateAccountHandler(BaseHandler): defer.returnValue(identity_server_supports_unbinding) + @defer.inlineCallbacks + def _reject_pending_invites_for_user(self, user_id): + """Reject pending invites addressed to a given user ID. + + Args: + user_id (str): The user ID to reject pending invites for. + """ + user = UserID.from_string(user_id) + pending_invites = yield self.store.get_invited_rooms_for_user(user_id) + + for room in pending_invites: + try: + yield self._room_member_handler.update_membership( + create_requester(user), + user, + room.room_id, + "leave", + ratelimit=False, + require_consent=False, + ) + logger.info( + "Rejected invite for deactivated user %r in room %r", + user_id, + room.room_id, + ) + except Exception: + logger.exception( + "Failed to reject invite for user %r in room %r:" + " ignoring and continuing", + user_id, + room.room_id, + ) + def _start_user_parting(self): """ Start the process that goes through the table of users diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index e96edb8bbf..35528eb48a 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py
@@ -2671,7 +2671,7 @@ class FederationHandler(BaseHandler): ) try: - self.auth.check_from_context(room_version, event, context) + yield self.auth.check_from_context(room_version, event, context) except AuthError as e: logger.warn("Denying third party invite %r because %s", event, e) raise e @@ -2699,7 +2699,12 @@ class FederationHandler(BaseHandler): original_invite_id, allow_none=True ) if original_invite: - display_name = original_invite.content["display_name"] + # If the m.room.third_party_invite event's content is empty, it means the + # invite has been revoked. In this case, we don't have to raise an error here + # because the auth check will fail on the invite (because it's not able to + # fetch public keys from the m.room.third_party_invite event's content, which + # is empty). + display_name = original_invite.content.get("display_name") event_dict["content"]["third_party_invite"]["display_name"] = display_name else: logger.info( diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index d75fb2a078..eb750d65d8 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py
@@ -130,7 +130,7 @@ class MessageHandler(object): raise NotFoundError("Can't find event for token %s" % (at_token, )) visible_events = yield filter_events_for_client( - self.store, user_id, last_events, + self.store, user_id, last_events, apply_retention_policies=False ) event = last_events[0] diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index 5c493b8d63..584f804986 100644 --- a/synapse/handlers/profile.py +++ b/synapse/handlers/profile.py
@@ -63,6 +63,9 @@ class BaseProfileHandler(BaseHandler): self.http_client = hs.get_simple_http_client() + self.max_avatar_size = hs.config.max_avatar_size + self.allowed_avatar_mimetypes = hs.config.allowed_avatar_mimetypes + if hs.config.worker_app is None: self.clock.looping_call( self._start_update_remote_profile_cache, self.PROFILE_UPDATE_MS, @@ -368,6 +371,35 @@ class BaseProfileHandler(BaseHandler): 400, "Avatar URL is too long (max %i)" % (MAX_AVATAR_URL_LEN, ), ) + # Enforce a max avatar size if one is defined + if self.max_avatar_size or self.allowed_avatar_mimetypes: + media_id = self._validate_and_parse_media_id_from_avatar_url(new_avatar_url) + + # Check that this media exists locally + media_info = yield self.store.get_local_media(media_id) + if not media_info: + raise SynapseError( + 400, "Unknown media id supplied", errcode=Codes.NOT_FOUND + ) + + # Ensure avatar does not exceed max allowed avatar size + media_size = media_info["media_length"] + if self.max_avatar_size and media_size > self.max_avatar_size: + raise SynapseError( + 400, "Avatars must be less than %s bytes in size" % + (self.max_avatar_size,), errcode=Codes.TOO_LARGE, + ) + + # Ensure the avatar's file type is allowed + if ( + self.allowed_avatar_mimetypes + and media_info["media_type"] not in self.allowed_avatar_mimetypes + ): + raise SynapseError( + 400, "Avatar file type '%s' not allowed" % + media_info["media_type"], + ) + yield self.store.set_profile_avatar_url( target_user.localpart, new_avatar_url, new_batchnum, ) @@ -383,6 +415,20 @@ class BaseProfileHandler(BaseHandler): # start a profile replication push run_in_background(self._replicate_profiles) + def _validate_and_parse_media_id_from_avatar_url(self, mxc): + """Validate and parse a provided avatar url and return the local media id + + Args: + mxc (str): A mxc URL + + Returns: + str: The ID of the media + """ + avatar_pieces = mxc.split("/") + if len(avatar_pieces) != 4 or avatar_pieces[0] != "mxc:": + raise SynapseError(400, "Invalid avatar URL '%s' supplied" % mxc) + return avatar_pieces[-1] + @defer.inlineCallbacks def on_profile_query(self, args): user = UserID.from_string(args["user_id"]) @@ -441,7 +487,7 @@ class BaseProfileHandler(BaseHandler): @defer.inlineCallbacks def check_profile_query_allowed(self, target_user, requester=None): """Checks whether a profile query is allowed. If the - 'require_auth_for_profile_requests' config flag is set to True and a + 'limit_profile_requests_to_known_users' config flag is set to True and a 'requester' is provided, the query is only allowed if the two users share a room. @@ -459,7 +505,7 @@ class BaseProfileHandler(BaseHandler): # be None when this function is called outside of a profile query, e.g. # when building a membership event. In this case, we must allow the # lookup. - if not self.hs.config.require_auth_for_profile_requests or not requester: + if not self.hs.config.limit_profile_requests_to_known_users or not requester: return # Always allow the user to query their own profile. diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py
index e940e4183b..790aeba9f5 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py
@@ -26,12 +26,11 @@ import synapse.server import synapse.types from synapse.api.constants import EventTypes, Membership from synapse.api.errors import AuthError, Codes, ProxiedRequestError, SynapseError +from synapse.api.ratelimiting import Ratelimiter from synapse.types import RoomID, UserID from synapse.util.async_helpers import Linearizer from synapse.util.distributor import user_joined_room, user_left_room -from ._base import BaseHandler - logger = logging.getLogger(__name__) id_server_scheme = "https://" @@ -74,11 +73,7 @@ class RoomMemberHandler(object): self.rewrite_identity_server_urls = self.config.rewrite_identity_server_urls self._enable_lookup = hs.config.enable_3pid_lookup self.allow_per_room_profiles = self.config.allow_per_room_profiles - - # This is only used to get at ratelimit function, and - # maybe_kick_guest_users. It's fine there are multiple of these as - # it doesn't store state. - self.base_handler = BaseHandler(hs) + self.ratelimiter = Ratelimiter() @abc.abstractmethod def _remote_join(self, requester, remote_room_hosts, room_id, user, content): @@ -773,7 +768,12 @@ class RoomMemberHandler(object): # We need to rate limit *before* we send out any 3PID invites, so we # can't just rely on the standard ratelimiting of events. - yield self.base_handler.ratelimit(requester) + self.ratelimiter.ratelimit( + requester.user.to_string(), time_now_s=self.hs.clock.time(), + rate_hz=self.hs.config.rc_third_party_invite.per_second, + burst_count=self.hs.config.rc_third_party_invite.burst_count, + update=True, + ) can_invite = yield self.third_party_event_rules.check_threepid_can_be_invited( medium, address, room_id,