Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Allow a user who could join a restricted room to see it in spaces summary. #9922

Merged
merged 18 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/9922.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Experimental support to allow a user who could join a restricted room to view it in the spaces summary.
2 changes: 1 addition & 1 deletion synapse/federation/transport/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1428,7 +1428,7 @@ async def on_POST(
)

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
)


Expand Down
90 changes: 69 additions & 21 deletions synapse/handlers/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Collection, Tuple

from synapse.api.constants import EventTypes, JoinRules
from synapse.api.room_versions import RoomVersion
Expand All @@ -29,46 +29,42 @@ class EventAuthHandler:
def __init__(self, hs: "HomeServer"):
self._store = hs.get_datastore()

async def can_join_without_invite(
self, state_ids: StateMap[str], room_version: RoomVersion, user_id: str
) -> bool:
async def get_allowed_spaces(
clokep marked this conversation as resolved.
Show resolved Hide resolved
self, state_ids: StateMap[str], room_version: RoomVersion
) -> Tuple[bool, Collection[str]]:
"""
Check whether a user can join a room without an invite.

When joining a room with restricted joined rules (as defined in MSC3083),
the membership of spaces must be checked during join.
Generate a list of spaces allow access to a room.
clokep marked this conversation as resolved.
Show resolved Hide resolved

Args:
state_ids: The state of the room as it currently is.
room_version: The room version of the room being joined.
user_id: The user joining the room.
room_version: The room version of the room to query.

Returns:
True if the user can join the room, false otherwise.
A tuple:
True if spaces give access to the room.
A collection of spaces (if any) which provide membership to the room.
"""
# This only applies to room versions which support the new join rule.
if not room_version.msc3083_join_rules:
return True
return False, ()

# 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 True
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)
if join_rules_event.content.get("join_rule") != JoinRules.MSC3083_RESTRICTED:
return True
return False, ()

# If allowed is of the wrong form, then only allow invited users.
allowed_spaces = join_rules_event.content.get("allow", [])
if not isinstance(allowed_spaces, list):
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)
return True, ()

# Pull out the other room IDs, invalid data gets filtered.
# Pull out the room IDs, invalid data gets filtered.
result = []
for space in allowed_spaces:
if not isinstance(space, dict):
continue
Expand All @@ -79,8 +75,60 @@ async def can_join_without_invite(

# The user was joined to one of the spaces specified, they can join
# this room!
if space_id in joined_rooms:
result.append(space_id)

return True, result

async def user_in_rooms(self, room_ids: Collection[str], user_id: str) -> bool:
"""
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 required spaces.
# The user was not in any of the rooms.
return False

async def can_join_without_invite(
self, state_ids: StateMap[str], room_version: RoomVersion, user_id: str
) -> bool:
"""
Check whether a user can join a room without an invite.

When joining a room with restricted joined rules (as defined in MSC3083),
the membership of spaces must be checked during join.

Args:
state_ids: The state of the room as it currently is.
room_version: The room version of the room being joined.
user_id: The user joining the room.

Returns:
True if the user can join the room, false otherwise.
"""
# Get whether spaces allow access to the room and the allowed spaces.
allow_via_spaces, allowed_spaces = await self.get_allowed_spaces(
state_ids, room_version
)
# Access via spaces doesn't apply to this room.
if not allow_via_spaces:
return True

# If the user was joined to one of the spaces specified, they can join
# this room!
return await self.user_in_rooms(allowed_spaces, user_id)
Loading