diff --git a/synapse/handlers/space_summary.py b/synapse/handlers/space_summary.py index 981649659ab9..366e6211e56d 100644 --- a/synapse/handlers/space_summary.py +++ b/synapse/handlers/space_summary.py @@ -448,6 +448,12 @@ async def _is_room_accessible( # If there's no state for the room, it isn't known. if not state_ids: + # The user might have a pending invite for the room. + if requester and await self._store.get_invite_for_local_user_in_room( + requester, room_id + ): + return True + logger.info("room %s is unknown, omitting from summary", room_id) return False @@ -499,12 +505,11 @@ async def _is_room_accessible( # If this is a request over federation, check if the host is in the room or # has a user who could join the room. elif origin: - if await self._event_auth_handler.check_host_in_room(room_id, origin): + if await self._event_auth_handler.check_host_in_room( + room_id, origin + ) or await self._store.is_host_invited(room_id, origin): return True - # TODO This does not handle if a server has a pending invite to the - # room. - # Alternately, if the host has a user in any of the spaces specified # for access, then the host can see this room (and should do filtering # if the requester cannot see it). diff --git a/synapse/storage/databases/main/roommember.py b/synapse/storage/databases/main/roommember.py index 2796354a1f58..4d82c4c26d0f 100644 --- a/synapse/storage/databases/main/roommember.py +++ b/synapse/storage/databases/main/roommember.py @@ -703,13 +703,22 @@ async def _get_joined_profiles_from_event_ids(self, event_ids: Iterable[str]): @cached(max_entries=10000) async def is_host_joined(self, room_id: str, host: str) -> bool: + return await self._check_host_room_membership(room_id, host, Membership.JOIN) + + @cached(max_entries=10000) + async def is_host_invited(self, room_id: str, host: str) -> bool: + return await self._check_host_room_membership(room_id, host, Membership.INVITE) + + async def _check_host_room_membership( + self, room_id: str, host: str, membership: str + ) -> bool: if "%" in host or "_" in host: raise Exception("Invalid host name") sql = """ SELECT state_key FROM current_state_events AS c INNER JOIN room_memberships AS m USING (event_id) - WHERE m.membership = 'join' + WHERE m.membership = ? AND type = 'm.room.member' AND c.room_id = ? AND state_key LIKE ? @@ -722,7 +731,7 @@ async def is_host_joined(self, room_id: str, host: str) -> bool: like_clause = "%:" + host rows = await self.db_pool.execute( - "is_host_joined", None, sql, room_id, like_clause + "is_host_joined", None, sql, membership, room_id, like_clause ) if not rows: diff --git a/tests/handlers/test_space_summary.py b/tests/handlers/test_space_summary.py index 61d0c3d5bd71..3f73ad7f9478 100644 --- a/tests/handlers/test_space_summary.py +++ b/tests/handlers/test_space_summary.py @@ -19,11 +19,13 @@ EventTypes, HistoryVisibility, JoinRules, + Membership, RestrictedJoinRuleTypes, RoomTypes, ) from synapse.api.errors import AuthError from synapse.api.room_versions import RoomVersions +from synapse.events import make_event_from_dict from synapse.handlers.space_summary import _child_events_comparison_key from synapse.rest import admin from synapse.rest.client.v1 import login, room @@ -225,6 +227,9 @@ def test_filtering(self): knock_room = self._create_room_with_join_rule( JoinRules.KNOCK, room_version=RoomVersions.V7.identifier ) + not_invited_room = self._create_room_with_join_rule(JoinRules.INVITE) + invited_room = self._create_room_with_join_rule(JoinRules.INVITE) + self.helper.invite(invited_room, targ=user2, tok=self.token) restricted_room = self._create_room_with_join_rule( JoinRules.MSC3083_RESTRICTED, room_version=RoomVersions.MSC3083.identifier, @@ -248,7 +253,8 @@ def test_filtering(self): body={"history_visibility": HistoryVisibility.WORLD_READABLE}, tok=self.token, ) - joined_room = self._create_room_with_join_rule(JoinRules.PUBLIC) + joined_room = self._create_room_with_join_rule(JoinRules.INVITE) + self.helper.invite(joined_room, targ=user2, tok=self.token) self.helper.join(joined_room, user2, tok=token2) # Join the space. @@ -262,6 +268,7 @@ def test_filtering(self): self.room, public_room, knock_room, + invited_room, restricted_accessible_room, world_readable_room, joined_room, @@ -273,6 +280,8 @@ def test_filtering(self): (self.space, self.room), (self.space, public_room), (self.space, knock_room), + (self.space, not_invited_room), + (self.space, invited_room), (self.space, restricted_room), (self.space, restricted_accessible_room), (self.space, world_readable_room), @@ -395,11 +404,33 @@ def test_fed_filtering(self): # Create a few rooms which will have different properties. public_room = "#public:" + fed_hostname knock_room = "#knock:" + fed_hostname + not_invited_room = "#not_invited:" + fed_hostname + invited_room = "#invited:" + fed_hostname restricted_room = "#restricted:" + fed_hostname restricted_accessible_room = "#restricted_accessible:" + fed_hostname world_readable_room = "#world_readable:" + fed_hostname joined_room = self.helper.create_room_as(self.user, tok=self.token) + # Poke an invite over federation into the database. + fed_handler = self.hs.get_federation_handler() + event = make_event_from_dict( + { + "room_id": invited_room, + "event_id": "!abcd:" + fed_hostname, + "type": EventTypes.Member, + "sender": "@remote:" + fed_hostname, + "state_key": self.user, + "content": {"membership": Membership.INVITE}, + "prev_events": [], + "auth_events": [], + "depth": 1, + "origin_server_ts": 1234, + } + ) + self.get_success( + fed_handler.on_invite_request(fed_hostname, event, RoomVersions.V6) + ) + async def summarize_remote_room( _self, room, suggested_only, max_children, exclude_rooms ): @@ -409,13 +440,21 @@ async def summarize_remote_room( "room_id": public_room, "world_readable": False, "join_rules": JoinRules.PUBLIC, - "allowed_spaces": [], }, { "room_id": knock_room, "world_readable": False, "join_rules": JoinRules.KNOCK, - "allowed_spaces": [], + }, + { + "room_id": not_invited_room, + "world_readable": False, + "join_rules": JoinRules.INVITE, + }, + { + "room_id": invited_room, + "world_readable": False, + "join_rules": JoinRules.INVITE, }, { "room_id": restricted_room, @@ -481,6 +520,7 @@ async def summarize_remote_room( subspace, public_room, knock_room, + invited_room, restricted_accessible_room, world_readable_room, joined_room, @@ -493,6 +533,8 @@ async def summarize_remote_room( (self.space, subspace), (subspace, public_room), (subspace, knock_room), + (subspace, not_invited_room), + (subspace, invited_room), (subspace, restricted_room), (subspace, restricted_accessible_room), (subspace, world_readable_room),