summary refs log tree commit diff
path: root/synapse/handlers/room_member.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--synapse/handlers/room_member.py281
1 files changed, 47 insertions, 234 deletions
diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py

index 51b9772329..a3a7326d94 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py
@@ -53,6 +53,7 @@ from synapse.metrics import event_processing_positions from synapse.metrics.background_process_metrics import run_as_background_process from synapse.replication.http.push import ReplicationCopyPusherRestServlet from synapse.storage.databases.main.state_deltas import StateDelta +from synapse.storage.invite_rule import InviteRule from synapse.types import ( JsonDict, Requester, @@ -98,7 +99,6 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): self.federation_handler = hs.get_federation_handler() self.directory_handler = hs.get_directory_handler() - self.identity_handler = hs.get_identity_handler() self.registration_handler = hs.get_registration_handler() self.profile_handler = hs.get_profile_handler() self.event_creation_handler = hs.get_event_creation_handler() @@ -122,7 +122,6 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): hs.get_module_api_callbacks().third_party_event_rules ) self._server_notices_mxid = self.config.servernotices.server_notices_mxid - self._enable_lookup = hs.config.registration.enable_3pid_lookup self.allow_per_room_profiles = self.config.server.allow_per_room_profiles self._join_rate_limiter_local = Ratelimiter( @@ -158,6 +157,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): store=self.store, clock=self.clock, cfg=hs.config.ratelimiting.rc_invites_per_room, + ratelimit_callbacks=hs.get_module_api_callbacks().ratelimit, ) # Ratelimiter for invites, keyed by recipient (across all rooms, all @@ -166,6 +166,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): store=self.store, clock=self.clock, cfg=hs.config.ratelimiting.rc_invites_per_user, + ratelimit_callbacks=hs.get_module_api_callbacks().ratelimit, ) # Ratelimiter for invites, keyed by issuer (across all rooms, all @@ -174,6 +175,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): store=self.store, clock=self.clock, cfg=hs.config.ratelimiting.rc_invites_per_issuer, + ratelimit_callbacks=hs.get_module_api_callbacks().ratelimit, ) self._third_party_invite_limiter = Ratelimiter( @@ -912,6 +914,21 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): additional_fields=block_invite_result[1], ) + # check the invitee's configuration and apply rules. Admins on the server can bypass. + if not is_requester_admin: + invite_config = await self.store.get_invite_config_for_user(target_id) + rule = invite_config.get_invite_rule(requester.user.to_string()) + if rule == InviteRule.BLOCK: + logger.info( + f"Automatically rejecting invite from {target_id} due to the the invite filtering rules of {requester.user}" + ) + raise SynapseError( + 403, + "You are not permitted to invite this user.", + errcode=Codes.INVITE_BLOCKED, + ) + # InviteRule.IGNORE is handled at the sync layer. + # An empty prev_events list is allowed as long as the auth_event_ids are present if prev_event_ids is not None: return await self._local_membership_update( @@ -1190,6 +1207,26 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): origin_server_ts=origin_server_ts, ) + async def check_for_any_membership_in_room( + self, *, user_id: str, room_id: str + ) -> None: + """ + Check if the user has any membership in the room and raise error if not. + + Args: + user_id: The user to check. + room_id: The room to check. + + Raises: + AuthError if the user doesn't have any membership in the room. + """ + result = await self.store.get_local_current_membership_for_user_in_room( + user_id=user_id, room_id=room_id + ) + + if result is None or result == (None, None): + raise AuthError(403, f"User {user_id} has no membership in room {room_id}") + async def _should_perform_remote_join( self, user_id: str, @@ -1302,11 +1339,11 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): # If this is going to be a local join, additional information must # be included in the event content in order to efficiently validate # the event. - content[EventContentFields.AUTHORISING_USER] = ( - await self.event_auth_handler.get_user_which_could_invite( - room_id, - state_before_join, - ) + content[ + EventContentFields.AUTHORISING_USER + ] = await self.event_auth_handler.get_user_which_could_invite( + room_id, + state_before_join, ) return False, [] @@ -1415,9 +1452,9 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): if requester is not None: sender = UserID.from_string(event.sender) - assert ( - sender == requester.user - ), "Sender (%s) must be same as requester (%s)" % (sender, requester.user) + assert sender == requester.user, ( + "Sender (%s) must be same as requester (%s)" % (sender, requester.user) + ) assert self.hs.is_mine(sender), "Sender must be our own: %s" % (sender,) else: requester = types.create_requester(target_user) @@ -1572,230 +1609,6 @@ class RoomMemberHandler(metaclass=abc.ABCMeta): return UserID.from_string(invite.sender) return None - async def do_3pid_invite( - self, - room_id: str, - inviter: UserID, - medium: str, - address: str, - id_server: str, - requester: Requester, - txn_id: Optional[str], - id_access_token: str, - prev_event_ids: Optional[List[str]] = None, - depth: Optional[int] = None, - ) -> Tuple[str, int]: - """Invite a 3PID to a room. - - Args: - room_id: The room to invite the 3PID to. - inviter: The user sending the invite. - medium: The 3PID's medium. - address: The 3PID's address. - id_server: The identity server to use. - requester: The user making the request. - txn_id: The transaction ID this is part of, or None if this is not - part of a transaction. - id_access_token: Identity server access token. - depth: Override the depth used to order the event in the DAG. - prev_event_ids: The event IDs to use as the prev events - Should normally be set to None, which will cause the depth to be calculated - based on the prev_events. - - Returns: - Tuple of event ID and stream ordering position - - Raises: - ShadowBanError if the requester has been shadow-banned. - """ - if self.config.server.block_non_admin_invites: - is_requester_admin = await self.auth.is_server_admin(requester) - if not is_requester_admin: - raise SynapseError( - 403, "Invites have been disabled on this server", Codes.FORBIDDEN - ) - - if requester.shadow_banned: - # We randomly sleep a bit just to annoy the requester. - await self.clock.sleep(random.randint(1, 10)) - raise ShadowBanError() - - # We need to rate limit *before* we send out any 3PID invites, so we - # can't just rely on the standard ratelimiting of events. - await self._third_party_invite_limiter.ratelimit(requester) - - can_invite = await self._third_party_event_rules.check_threepid_can_be_invited( - medium, address, room_id - ) - if not can_invite: - raise SynapseError( - 403, - "This third-party identifier can not be invited in this room", - Codes.FORBIDDEN, - ) - - if not self._enable_lookup: - raise SynapseError( - 403, "Looking up third-party identifiers is denied from this server" - ) - - invitee = await self.identity_handler.lookup_3pid( - id_server, medium, address, id_access_token - ) - - if invitee: - # Note that update_membership with an action of "invite" can raise - # a ShadowBanError, but this was done above already. - # We don't check the invite against the spamchecker(s) here (through - # user_may_invite) because we'll do it further down the line anyway (in - # update_membership_locked). - event_id, stream_id = await self.update_membership( - requester, UserID.from_string(invitee), room_id, "invite", txn_id=txn_id - ) - else: - # Check if the spamchecker(s) allow this invite to go through. - spam_check = ( - await self._spam_checker_module_callbacks.user_may_send_3pid_invite( - inviter_userid=requester.user.to_string(), - medium=medium, - address=address, - room_id=room_id, - ) - ) - if spam_check != self._spam_checker_module_callbacks.NOT_SPAM: - raise SynapseError( - 403, - "Cannot send threepid invite", - errcode=spam_check[0], - additional_fields=spam_check[1], - ) - - event, stream_id = await self._make_and_store_3pid_invite( - requester, - id_server, - medium, - address, - room_id, - inviter, - txn_id=txn_id, - id_access_token=id_access_token, - prev_event_ids=prev_event_ids, - depth=depth, - ) - event_id = event.event_id - - return event_id, stream_id - - async def _make_and_store_3pid_invite( - self, - requester: Requester, - id_server: str, - medium: str, - address: str, - room_id: str, - user: UserID, - txn_id: Optional[str], - id_access_token: str, - prev_event_ids: Optional[List[str]] = None, - depth: Optional[int] = None, - ) -> Tuple[EventBase, int]: - room_state = await self._storage_controllers.state.get_current_state( - room_id, - StateFilter.from_types( - [ - (EventTypes.Member, user.to_string()), - (EventTypes.CanonicalAlias, ""), - (EventTypes.Name, ""), - (EventTypes.Create, ""), - (EventTypes.JoinRules, ""), - (EventTypes.RoomAvatar, ""), - ] - ), - ) - - inviter_display_name = "" - inviter_avatar_url = "" - member_event = room_state.get((EventTypes.Member, user.to_string())) - if member_event: - inviter_display_name = member_event.content.get("displayname", "") - inviter_avatar_url = member_event.content.get("avatar_url", "") - - # if user has no display name, default to their MXID - if not inviter_display_name: - inviter_display_name = user.to_string() - - canonical_room_alias = "" - canonical_alias_event = room_state.get((EventTypes.CanonicalAlias, "")) - if canonical_alias_event: - canonical_room_alias = canonical_alias_event.content.get("alias", "") - - room_name = "" - room_name_event = room_state.get((EventTypes.Name, "")) - if room_name_event: - room_name = room_name_event.content.get("name", "") - - room_type = None - room_create_event = room_state.get((EventTypes.Create, "")) - if room_create_event: - room_type = room_create_event.content.get(EventContentFields.ROOM_TYPE) - - room_join_rules = "" - join_rules_event = room_state.get((EventTypes.JoinRules, "")) - if join_rules_event: - room_join_rules = join_rules_event.content.get("join_rule", "") - - room_avatar_url = "" - room_avatar_event = room_state.get((EventTypes.RoomAvatar, "")) - if room_avatar_event: - room_avatar_url = room_avatar_event.content.get("url", "") - - ( - token, - public_keys, - fallback_public_key, - display_name, - ) = await self.identity_handler.ask_id_server_for_third_party_invite( - requester=requester, - id_server=id_server, - medium=medium, - address=address, - room_id=room_id, - inviter_user_id=user.to_string(), - room_alias=canonical_room_alias, - room_avatar_url=room_avatar_url, - room_join_rules=room_join_rules, - room_name=room_name, - room_type=room_type, - inviter_display_name=inviter_display_name, - inviter_avatar_url=inviter_avatar_url, - id_access_token=id_access_token, - ) - - ( - event, - stream_id, - ) = await self.event_creation_handler.create_and_send_nonmember_event( - requester, - { - "type": EventTypes.ThirdPartyInvite, - "content": { - "display_name": display_name, - "public_keys": public_keys, - # For backwards compatibility: - "key_validity_url": fallback_public_key["key_validity_url"], - "public_key": fallback_public_key["public_key"], - }, - "room_id": room_id, - "sender": user.to_string(), - "state_key": token, - }, - ratelimit=False, - txn_id=txn_id, - prev_event_ids=prev_event_ids, - depth=depth, - ) - return event, stream_id - async def _is_host_in_room(self, partial_current_state_ids: StateMap[str]) -> bool: """Returns whether the homeserver is in the room based on its current state.