Refactor checking restricted join rules (#10007)

To be more consistent with similar code. The check now automatically
raises an AuthError instead of passing back a boolean. It also absorbs
some shared logic between callers.
This commit is contained in:
Patrick Cloke 2021-05-18 12:17:04 -04:00 committed by GitHub
parent 4d6e5a5e99
commit ac6bfcd52f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 50 deletions

View file

@ -0,0 +1 @@
Experimental support to allow a user who could join a restricted room to view it in the spaces summary.

View file

@ -11,10 +11,12 @@
# 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 from typing import TYPE_CHECKING, Optional
from synapse.api.constants import EventTypes, JoinRules from synapse.api.constants import EventTypes, JoinRules, Membership
from synapse.api.errors import AuthError
from synapse.api.room_versions import RoomVersion from synapse.api.room_versions import RoomVersion
from synapse.events import EventBase
from synapse.types import StateMap from synapse.types import StateMap
if TYPE_CHECKING: if TYPE_CHECKING:
@ -29,44 +31,58 @@ class EventAuthHandler:
def __init__(self, hs: "HomeServer"): def __init__(self, hs: "HomeServer"):
self._store = hs.get_datastore() self._store = hs.get_datastore()
async def can_join_without_invite( async def check_restricted_join_rules(
self, state_ids: StateMap[str], room_version: RoomVersion, user_id: str self,
) -> bool: state_ids: StateMap[str],
room_version: RoomVersion,
user_id: str,
prev_member_event: Optional[EventBase],
) -> None:
""" """
Check whether a user can join a room without an invite. Check whether a user can join a room without an invite due to restricted join rules.
When joining a room with restricted joined rules (as defined in MSC3083), When joining a room with restricted joined rules (as defined in MSC3083),
the membership of spaces must be checked during join. the membership of spaces must be checked during a room join.
Args: Args:
state_ids: The state of the room as it currently is. state_ids: The state of the room as it currently is.
room_version: The room version of the room being joined. room_version: The room version of the room being joined.
user_id: The user joining the room. user_id: The user joining the room.
prev_member_event: The current membership event for this user.
Returns: Raises:
True if the user can join the room, false otherwise. AuthError if the user cannot join the room.
""" """
# If the member is invited or currently joined, then nothing to do.
if prev_member_event and (
prev_member_event.membership in (Membership.JOIN, Membership.INVITE)
):
return
# 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 True return
# 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 True 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: if join_rules_event.content.get("join_rule") != JoinRules.MSC3083_RESTRICTED:
return True 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):
return False allowed_spaces = ()
# Get the list of joined rooms and see if there's an overlap. # 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) 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.
for space in allowed_spaces: for space in allowed_spaces:
@ -80,7 +96,10 @@ class EventAuthHandler:
# The user was joined to one of the spaces specified, they can join # The user was joined to one of the spaces specified, they can join
# this room! # this room!
if space_id in joined_rooms: if space_id in joined_rooms:
return True return
# The user was not in any of the required spaces. # The user was not in any of the required spaces.
return False raise AuthError(
403,
"You do not belong to any of the required spaces to join this room.",
)

View file

@ -1668,27 +1668,16 @@ class FederationHandler(BaseHandler):
# Check if the user is already in the room or invited to the room. # Check if the user is already in the room or invited to the room.
user_id = event.state_key user_id = event.state_key
prev_member_event_id = prev_state_ids.get((EventTypes.Member, user_id), None) prev_member_event_id = prev_state_ids.get((EventTypes.Member, user_id), None)
newly_joined = True prev_member_event = None
user_is_invited = False
if prev_member_event_id: if prev_member_event_id:
prev_member_event = await self.store.get_event(prev_member_event_id) prev_member_event = await self.store.get_event(prev_member_event_id)
newly_joined = prev_member_event.membership != Membership.JOIN
user_is_invited = prev_member_event.membership == Membership.INVITE
# If the member is not already in the room, and not invited, check if # Check if the member should be allowed access via membership in a space.
# they should be allowed access via membership in a space. await self._event_auth_handler.check_restricted_join_rules(
if (
newly_joined
and not user_is_invited
and not await self._event_auth_handler.can_join_without_invite(
prev_state_ids, prev_state_ids,
event.room_version, event.room_version,
user_id, user_id,
) prev_member_event,
):
raise AuthError(
403,
"You do not belong to any of the required spaces to join this room.",
) )
# Persist the event. # Persist the event.

View file

@ -260,24 +260,14 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
if event.membership == Membership.JOIN: if event.membership == Membership.JOIN:
newly_joined = True newly_joined = True
user_is_invited = False prev_member_event = None
if prev_member_event_id: if prev_member_event_id:
prev_member_event = await self.store.get_event(prev_member_event_id) prev_member_event = await self.store.get_event(prev_member_event_id)
newly_joined = prev_member_event.membership != Membership.JOIN newly_joined = prev_member_event.membership != Membership.JOIN
user_is_invited = prev_member_event.membership == Membership.INVITE
# If the member is not already in the room and is not accepting an invite, # Check if the member should be allowed access via membership in a space.
# check if they should be allowed access via membership in a space. await self.event_auth_handler.check_restricted_join_rules(
if ( prev_state_ids, event.room_version, user_id, prev_member_event
newly_joined
and not user_is_invited
and not await self.event_auth_handler.can_join_without_invite(
prev_state_ids, event.room_version, user_id
)
):
raise AuthError(
403,
"You do not belong to any of the required spaces to join this room.",
) )
# Only rate-limit if the user actually joined the room, otherwise we'll end # Only rate-limit if the user actually joined the room, otherwise we'll end