Allow a user who could join a restricted room to see it in spaces summary. (#9922)
This finishes up the experimental implementation of MSC3083 by showing the restricted rooms in the spaces summary (from MSC2946).pull/10029/head
parent
d983ced596
commit
551d2c3f4b
|
@ -0,0 +1 @@
|
||||||
|
Experimental support to allow a user who could join a restricted room to view it in the spaces summary.
|
|
@ -1428,7 +1428,7 @@ class FederationSpaceSummaryServlet(BaseFederationServlet):
|
||||||
)
|
)
|
||||||
|
|
||||||
return 200, await self.handler.federation_space_summary(
|
return 200, await self.handler.federation_space_summary(
|
||||||
room_id, suggested_only, max_rooms_per_space, exclude_rooms
|
origin, room_id, suggested_only, max_rooms_per_space, exclude_rooms
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Collection, Optional
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, JoinRules, Membership
|
from synapse.api.constants import EventTypes, JoinRules, Membership
|
||||||
from synapse.api.errors import AuthError
|
from synapse.api.errors import AuthError
|
||||||
|
@ -59,32 +59,76 @@ class EventAuthHandler:
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# This is not a room with a restricted join rule, so we don't need to do the
|
||||||
|
# restricted room specific checks.
|
||||||
|
#
|
||||||
|
# Note: We'll be applying the standard join rule checks later, which will
|
||||||
|
# catch the cases of e.g. trying to join private rooms without an invite.
|
||||||
|
if not await self.has_restricted_join_rules(state_ids, room_version):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get the spaces which allow access to this room and check if the user is
|
||||||
|
# in any of them.
|
||||||
|
allowed_spaces = await self.get_spaces_that_allow_join(state_ids)
|
||||||
|
if not await self.is_user_in_rooms(allowed_spaces, user_id):
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
"You do not belong to any of the required spaces to join this room.",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def has_restricted_join_rules(
|
||||||
|
self, state_ids: StateMap[str], room_version: RoomVersion
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Return if the room has the proper join rules set for access via spaces.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state_ids: The state of the room as it currently is.
|
||||||
|
room_version: The room version of the room to query.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the proper room version and join rules are set for restricted access.
|
||||||
|
"""
|
||||||
# This only applies to room versions which support the new join rule.
|
# This only applies to room versions which support the new join rule.
|
||||||
if not room_version.msc3083_join_rules:
|
if not room_version.msc3083_join_rules:
|
||||||
return
|
return False
|
||||||
|
|
||||||
# If there's no join rule, then it defaults to invite (so this doesn't apply).
|
# If there's no join rule, then it defaults to invite (so this doesn't apply).
|
||||||
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
|
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
|
||||||
if not join_rules_event_id:
|
if not join_rules_event_id:
|
||||||
return
|
return False
|
||||||
|
|
||||||
|
# If the join rule is not restricted, this doesn't apply.
|
||||||
|
join_rules_event = await self._store.get_event(join_rules_event_id)
|
||||||
|
return join_rules_event.content.get("join_rule") == JoinRules.MSC3083_RESTRICTED
|
||||||
|
|
||||||
|
async def get_spaces_that_allow_join(
|
||||||
|
self, state_ids: StateMap[str]
|
||||||
|
) -> Collection[str]:
|
||||||
|
"""
|
||||||
|
Generate a list of spaces which allow access to a room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state_ids: The state of the room as it currently is.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A collection of spaces which provide membership to the room.
|
||||||
|
"""
|
||||||
|
# If there's no join rule, then it defaults to invite (so this doesn't apply).
|
||||||
|
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
|
||||||
|
if not join_rules_event_id:
|
||||||
|
return ()
|
||||||
|
|
||||||
# If the join rule is not restricted, this doesn't apply.
|
# If the join rule is not restricted, this doesn't apply.
|
||||||
join_rules_event = await self._store.get_event(join_rules_event_id)
|
join_rules_event = await self._store.get_event(join_rules_event_id)
|
||||||
if join_rules_event.content.get("join_rule") != JoinRules.MSC3083_RESTRICTED:
|
|
||||||
return
|
|
||||||
|
|
||||||
# If allowed is of the wrong form, then only allow invited users.
|
# If allowed is of the wrong form, then only allow invited users.
|
||||||
allowed_spaces = join_rules_event.content.get("allow", [])
|
allowed_spaces = join_rules_event.content.get("allow", [])
|
||||||
if not isinstance(allowed_spaces, list):
|
if not isinstance(allowed_spaces, list):
|
||||||
allowed_spaces = ()
|
return ()
|
||||||
|
|
||||||
# Get the list of joined rooms and see if there's an overlap.
|
|
||||||
if allowed_spaces:
|
|
||||||
joined_rooms = await self._store.get_rooms_for_user(user_id)
|
|
||||||
else:
|
|
||||||
joined_rooms = ()
|
|
||||||
|
|
||||||
# Pull out the other room IDs, invalid data gets filtered.
|
# Pull out the other room IDs, invalid data gets filtered.
|
||||||
|
result = []
|
||||||
for space in allowed_spaces:
|
for space in allowed_spaces:
|
||||||
if not isinstance(space, dict):
|
if not isinstance(space, dict):
|
||||||
continue
|
continue
|
||||||
|
@ -93,13 +137,31 @@ class EventAuthHandler:
|
||||||
if not isinstance(space_id, str):
|
if not isinstance(space_id, str):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# The user was joined to one of the spaces specified, they can join
|
result.append(space_id)
|
||||||
# this room!
|
|
||||||
if space_id in joined_rooms:
|
|
||||||
return
|
|
||||||
|
|
||||||
# The user was not in any of the required spaces.
|
return result
|
||||||
raise AuthError(
|
|
||||||
403,
|
async def is_user_in_rooms(self, room_ids: Collection[str], user_id: str) -> bool:
|
||||||
"You do not belong to any of the required spaces to join this room.",
|
"""
|
||||||
)
|
Check whether a user is a member of any of the provided rooms.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_ids: The rooms to check for membership.
|
||||||
|
user_id: The user to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the user is in any of the rooms, false otherwise.
|
||||||
|
"""
|
||||||
|
if not room_ids:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get the list of joined rooms and see if there's an overlap.
|
||||||
|
joined_rooms = await self._store.get_rooms_for_user(user_id)
|
||||||
|
|
||||||
|
# Check each room and see if the user is in it.
|
||||||
|
for room_id in room_ids:
|
||||||
|
if room_id in joined_rooms:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# The user was not in any of the rooms.
|
||||||
|
return False
|
||||||
|
|
|
@ -16,11 +16,16 @@ import itertools
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence, Set, Tuple, cast
|
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence, Set, Tuple
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
from synapse.api.constants import EventContentFields, EventTypes, HistoryVisibility
|
from synapse.api.constants import (
|
||||||
|
EventContentFields,
|
||||||
|
EventTypes,
|
||||||
|
HistoryVisibility,
|
||||||
|
Membership,
|
||||||
|
)
|
||||||
from synapse.api.errors import AuthError
|
from synapse.api.errors import AuthError
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase
|
||||||
from synapse.events.utils import format_event_for_client_v2
|
from synapse.events.utils import format_event_for_client_v2
|
||||||
|
@ -47,6 +52,7 @@ class SpaceSummaryHandler:
|
||||||
self._auth = hs.get_auth()
|
self._auth = hs.get_auth()
|
||||||
self._room_list_handler = hs.get_room_list_handler()
|
self._room_list_handler = hs.get_room_list_handler()
|
||||||
self._state_handler = hs.get_state_handler()
|
self._state_handler = hs.get_state_handler()
|
||||||
|
self._event_auth_handler = hs.get_event_auth_handler()
|
||||||
self._store = hs.get_datastore()
|
self._store = hs.get_datastore()
|
||||||
self._event_serializer = hs.get_event_client_serializer()
|
self._event_serializer = hs.get_event_client_serializer()
|
||||||
self._server_name = hs.hostname
|
self._server_name = hs.hostname
|
||||||
|
@ -111,28 +117,88 @@ class SpaceSummaryHandler:
|
||||||
max_children = max_rooms_per_space if processed_rooms else None
|
max_children = max_rooms_per_space if processed_rooms else None
|
||||||
|
|
||||||
if is_in_room:
|
if is_in_room:
|
||||||
rooms, events = await self._summarize_local_room(
|
room, events = await self._summarize_local_room(
|
||||||
requester, room_id, suggested_only, max_children
|
requester, None, room_id, suggested_only, max_children
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Query of local room %s returned events %s",
|
||||||
|
room_id,
|
||||||
|
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in events],
|
||||||
|
)
|
||||||
|
|
||||||
|
if room:
|
||||||
|
rooms_result.append(room)
|
||||||
else:
|
else:
|
||||||
rooms, events = await self._summarize_remote_room(
|
fed_rooms, fed_events = await self._summarize_remote_room(
|
||||||
queue_entry,
|
queue_entry,
|
||||||
suggested_only,
|
suggested_only,
|
||||||
max_children,
|
max_children,
|
||||||
exclude_rooms=processed_rooms,
|
exclude_rooms=processed_rooms,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(
|
# The results over federation might include rooms that the we,
|
||||||
"Query of %s returned rooms %s, events %s",
|
# as the requesting server, are allowed to see, but the requesting
|
||||||
queue_entry.room_id,
|
# user is not permitted see.
|
||||||
[room.get("room_id") for room in rooms],
|
#
|
||||||
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in events],
|
# Filter the returned results to only what is accessible to the user.
|
||||||
|
room_ids = set()
|
||||||
|
events = []
|
||||||
|
for room in fed_rooms:
|
||||||
|
fed_room_id = room.get("room_id")
|
||||||
|
if not fed_room_id or not isinstance(fed_room_id, str):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# The room should only be included in the summary if:
|
||||||
|
# a. the user is in the room;
|
||||||
|
# b. the room is world readable; or
|
||||||
|
# c. the user is in a space that has been granted access to
|
||||||
|
# the room.
|
||||||
|
#
|
||||||
|
# Note that we know the user is not in the root room (which is
|
||||||
|
# why the remote call was made in the first place), but the user
|
||||||
|
# could be in one of the children rooms and we just didn't know
|
||||||
|
# about the link.
|
||||||
|
include_room = room.get("world_readable") is True
|
||||||
|
|
||||||
|
# Check if the user is a member of any of the allowed spaces
|
||||||
|
# from the response.
|
||||||
|
allowed_spaces = room.get("allowed_spaces")
|
||||||
|
if (
|
||||||
|
not include_room
|
||||||
|
and allowed_spaces
|
||||||
|
and isinstance(allowed_spaces, list)
|
||||||
|
):
|
||||||
|
include_room = await self._event_auth_handler.is_user_in_rooms(
|
||||||
|
allowed_spaces, requester
|
||||||
)
|
)
|
||||||
|
|
||||||
rooms_result.extend(rooms)
|
# Finally, if this isn't the requested room, check ourselves
|
||||||
|
# if we can access the room.
|
||||||
|
if not include_room and fed_room_id != queue_entry.room_id:
|
||||||
|
include_room = await self._is_room_accessible(
|
||||||
|
fed_room_id, requester, None
|
||||||
|
)
|
||||||
|
|
||||||
# any rooms returned don't need visiting again
|
# The user can see the room, include it!
|
||||||
processed_rooms.update(cast(str, room.get("room_id")) for room in rooms)
|
if include_room:
|
||||||
|
rooms_result.append(room)
|
||||||
|
room_ids.add(fed_room_id)
|
||||||
|
|
||||||
|
# All rooms returned don't need visiting again (even if the user
|
||||||
|
# didn't have access to them).
|
||||||
|
processed_rooms.add(fed_room_id)
|
||||||
|
|
||||||
|
for event in fed_events:
|
||||||
|
if event.get("room_id") in room_ids:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Query of %s returned rooms %s, events %s",
|
||||||
|
room_id,
|
||||||
|
[room.get("room_id") for room in fed_rooms],
|
||||||
|
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in fed_events],
|
||||||
|
)
|
||||||
|
|
||||||
# the room we queried may or may not have been returned, but don't process
|
# the room we queried may or may not have been returned, but don't process
|
||||||
# it again, anyway.
|
# it again, anyway.
|
||||||
|
@ -158,10 +224,16 @@ class SpaceSummaryHandler:
|
||||||
)
|
)
|
||||||
processed_events.add(ev_key)
|
processed_events.add(ev_key)
|
||||||
|
|
||||||
|
# Before returning to the client, remove the allowed_spaces key for any
|
||||||
|
# rooms.
|
||||||
|
for room in rooms_result:
|
||||||
|
room.pop("allowed_spaces", None)
|
||||||
|
|
||||||
return {"rooms": rooms_result, "events": events_result}
|
return {"rooms": rooms_result, "events": events_result}
|
||||||
|
|
||||||
async def federation_space_summary(
|
async def federation_space_summary(
|
||||||
self,
|
self,
|
||||||
|
origin: str,
|
||||||
room_id: str,
|
room_id: str,
|
||||||
suggested_only: bool,
|
suggested_only: bool,
|
||||||
max_rooms_per_space: Optional[int],
|
max_rooms_per_space: Optional[int],
|
||||||
|
@ -171,6 +243,8 @@ class SpaceSummaryHandler:
|
||||||
Implementation of the space summary Federation API
|
Implementation of the space summary Federation API
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
origin: The server requesting the spaces summary.
|
||||||
|
|
||||||
room_id: room id to start the summary at
|
room_id: room id to start the summary at
|
||||||
|
|
||||||
suggested_only: whether we should only return children with the "suggested"
|
suggested_only: whether we should only return children with the "suggested"
|
||||||
|
@ -205,13 +279,14 @@ class SpaceSummaryHandler:
|
||||||
|
|
||||||
logger.debug("Processing room %s", room_id)
|
logger.debug("Processing room %s", room_id)
|
||||||
|
|
||||||
rooms, events = await self._summarize_local_room(
|
room, events = await self._summarize_local_room(
|
||||||
None, room_id, suggested_only, max_rooms_per_space
|
None, origin, room_id, suggested_only, max_rooms_per_space
|
||||||
)
|
)
|
||||||
|
|
||||||
processed_rooms.add(room_id)
|
processed_rooms.add(room_id)
|
||||||
|
|
||||||
rooms_result.extend(rooms)
|
if room:
|
||||||
|
rooms_result.append(room)
|
||||||
events_result.extend(events)
|
events_result.extend(events)
|
||||||
|
|
||||||
# add any children to the queue
|
# add any children to the queue
|
||||||
|
@ -222,10 +297,11 @@ class SpaceSummaryHandler:
|
||||||
async def _summarize_local_room(
|
async def _summarize_local_room(
|
||||||
self,
|
self,
|
||||||
requester: Optional[str],
|
requester: Optional[str],
|
||||||
|
origin: Optional[str],
|
||||||
room_id: str,
|
room_id: str,
|
||||||
suggested_only: bool,
|
suggested_only: bool,
|
||||||
max_children: Optional[int],
|
max_children: Optional[int],
|
||||||
) -> Tuple[Sequence[JsonDict], Sequence[JsonDict]]:
|
) -> Tuple[Optional[JsonDict], Sequence[JsonDict]]:
|
||||||
"""
|
"""
|
||||||
Generate a room entry and a list of event entries for a given room.
|
Generate a room entry and a list of event entries for a given room.
|
||||||
|
|
||||||
|
@ -233,6 +309,9 @@ class SpaceSummaryHandler:
|
||||||
requester:
|
requester:
|
||||||
The user requesting the summary, if it is a local request. None
|
The user requesting the summary, if it is a local request. None
|
||||||
if this is a federation request.
|
if this is a federation request.
|
||||||
|
origin:
|
||||||
|
The server requesting the summary, if it is a federation request.
|
||||||
|
None if this is a local request.
|
||||||
room_id: The room ID to summarize.
|
room_id: The room ID to summarize.
|
||||||
suggested_only: True if only suggested children should be returned.
|
suggested_only: True if only suggested children should be returned.
|
||||||
Otherwise, all children are returned.
|
Otherwise, all children are returned.
|
||||||
|
@ -247,8 +326,8 @@ class SpaceSummaryHandler:
|
||||||
An iterable of the sorted children events. This may be limited
|
An iterable of the sorted children events. This may be limited
|
||||||
to a maximum size or may include all children.
|
to a maximum size or may include all children.
|
||||||
"""
|
"""
|
||||||
if not await self._is_room_accessible(room_id, requester):
|
if not await self._is_room_accessible(room_id, requester, origin):
|
||||||
return (), ()
|
return None, ()
|
||||||
|
|
||||||
room_entry = await self._build_room_entry(room_id)
|
room_entry = await self._build_room_entry(room_id)
|
||||||
|
|
||||||
|
@ -272,7 +351,7 @@ class SpaceSummaryHandler:
|
||||||
event_format=format_event_for_client_v2,
|
event_format=format_event_for_client_v2,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return (room_entry,), events_result
|
return room_entry, events_result
|
||||||
|
|
||||||
async def _summarize_remote_room(
|
async def _summarize_remote_room(
|
||||||
self,
|
self,
|
||||||
|
@ -332,13 +411,17 @@ class SpaceSummaryHandler:
|
||||||
or ev.event_type == EventTypes.SpaceChild
|
or ev.event_type == EventTypes.SpaceChild
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _is_room_accessible(self, room_id: str, requester: Optional[str]) -> bool:
|
async def _is_room_accessible(
|
||||||
|
self, room_id: str, requester: Optional[str], origin: Optional[str]
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Calculate whether the room should be shown in the spaces summary.
|
Calculate whether the room should be shown in the spaces summary.
|
||||||
|
|
||||||
It should be included if:
|
It should be included if:
|
||||||
|
|
||||||
* The requester is joined or invited to the room.
|
* The requester is joined or invited to the room.
|
||||||
|
* The requester can join without an invite (per MSC3083).
|
||||||
|
* The origin server has any user that is joined or invited to the room.
|
||||||
* The history visibility is set to world readable.
|
* The history visibility is set to world readable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -346,31 +429,75 @@ class SpaceSummaryHandler:
|
||||||
requester:
|
requester:
|
||||||
The user requesting the summary, if it is a local request. None
|
The user requesting the summary, if it is a local request. None
|
||||||
if this is a federation request.
|
if this is a federation request.
|
||||||
|
origin:
|
||||||
|
The server requesting the summary, if it is a federation request.
|
||||||
|
None if this is a local request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if the room should be included in the spaces summary.
|
True if the room should be included in the spaces summary.
|
||||||
"""
|
"""
|
||||||
|
state_ids = await self._store.get_current_state_ids(room_id)
|
||||||
|
|
||||||
|
# If there's no state for the room, it isn't known.
|
||||||
|
if not state_ids:
|
||||||
|
logger.info("room %s is unknown, omitting from summary", room_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
room_version = await self._store.get_room_version(room_id)
|
||||||
|
|
||||||
# if we have an authenticated requesting user, first check if they are able to view
|
# if we have an authenticated requesting user, first check if they are able to view
|
||||||
# stripped state in the room.
|
# stripped state in the room.
|
||||||
if requester:
|
if requester:
|
||||||
try:
|
member_event_id = state_ids.get((EventTypes.Member, requester), None)
|
||||||
await self._auth.check_user_in_room(room_id, requester)
|
|
||||||
|
# If they're in the room they can see info on it.
|
||||||
|
member_event = None
|
||||||
|
if member_event_id:
|
||||||
|
member_event = await self._store.get_event(member_event_id)
|
||||||
|
if member_event.membership in (Membership.JOIN, Membership.INVITE):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Otherwise, check if they should be allowed access via membership in a space.
|
||||||
|
try:
|
||||||
|
await self._event_auth_handler.check_restricted_join_rules(
|
||||||
|
state_ids, room_version, requester, member_event
|
||||||
|
)
|
||||||
except AuthError:
|
except AuthError:
|
||||||
|
# The user doesn't have access due to spaces, but might have access
|
||||||
|
# another way. Keep trying.
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If this is a request over federation, check if the host is in the room or
|
||||||
|
# is in one of the spaces specified via the join rules.
|
||||||
|
elif origin:
|
||||||
|
if await self._auth.check_host_in_room(room_id, origin):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 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).
|
||||||
|
if await self._event_auth_handler.has_restricted_join_rules(
|
||||||
|
state_ids, room_version
|
||||||
|
):
|
||||||
|
allowed_spaces = (
|
||||||
|
await self._event_auth_handler.get_spaces_that_allow_join(state_ids)
|
||||||
|
)
|
||||||
|
for space_id in allowed_spaces:
|
||||||
|
if await self._auth.check_host_in_room(space_id, origin):
|
||||||
|
return True
|
||||||
|
|
||||||
# otherwise, check if the room is peekable
|
# otherwise, check if the room is peekable
|
||||||
hist_vis_ev = await self._state_handler.get_current_state(
|
hist_vis_event_id = state_ids.get((EventTypes.RoomHistoryVisibility, ""), None)
|
||||||
room_id, EventTypes.RoomHistoryVisibility, ""
|
if hist_vis_event_id:
|
||||||
)
|
hist_vis_ev = await self._store.get_event(hist_vis_event_id)
|
||||||
if hist_vis_ev:
|
|
||||||
hist_vis = hist_vis_ev.content.get("history_visibility")
|
hist_vis = hist_vis_ev.content.get("history_visibility")
|
||||||
if hist_vis == HistoryVisibility.WORLD_READABLE:
|
if hist_vis == HistoryVisibility.WORLD_READABLE:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"room %s is unpeekable and user %s is not a member, omitting from summary",
|
"room %s is unpeekable and user %s is not a member / not allowed to join, omitting from summary",
|
||||||
room_id,
|
room_id,
|
||||||
requester,
|
requester,
|
||||||
)
|
)
|
||||||
|
@ -395,6 +522,15 @@ class SpaceSummaryHandler:
|
||||||
if not room_type:
|
if not room_type:
|
||||||
room_type = create_event.content.get(EventContentFields.MSC1772_ROOM_TYPE)
|
room_type = create_event.content.get(EventContentFields.MSC1772_ROOM_TYPE)
|
||||||
|
|
||||||
|
room_version = await self._store.get_room_version(room_id)
|
||||||
|
allowed_spaces = None
|
||||||
|
if await self._event_auth_handler.has_restricted_join_rules(
|
||||||
|
current_state_ids, room_version
|
||||||
|
):
|
||||||
|
allowed_spaces = await self._event_auth_handler.get_spaces_that_allow_join(
|
||||||
|
current_state_ids
|
||||||
|
)
|
||||||
|
|
||||||
entry = {
|
entry = {
|
||||||
"room_id": stats["room_id"],
|
"room_id": stats["room_id"],
|
||||||
"name": stats["name"],
|
"name": stats["name"],
|
||||||
|
@ -408,6 +544,7 @@ class SpaceSummaryHandler:
|
||||||
"guest_can_join": stats["guest_access"] == "can_join",
|
"guest_can_join": stats["guest_access"] == "can_join",
|
||||||
"creation_ts": create_event.origin_server_ts,
|
"creation_ts": create_event.origin_server_ts,
|
||||||
"room_type": room_type,
|
"room_type": room_type,
|
||||||
|
"allowed_spaces": allowed_spaces,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Filter out Nones – rather omit the field altogether
|
# Filter out Nones – rather omit the field altogether
|
||||||
|
|
Loading…
Reference in New Issue