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

Move maybe_kick_guest_users out of BaseHandler #10744

Merged
merged 5 commits into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from all 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/10744.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Move `kick_guest_users` into `RoomMemberHandler`.
9 changes: 9 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ class EventContentFields:
# The creator of the room, as used in `m.room.create` events.
ROOM_CREATOR = "creator"

# Used in m.room.guest_access events.
GUEST_ACCESS = "guest_access"

# Used on normal messages to indicate they were historically imported after the fact
MSC2716_HISTORICAL = "org.matrix.msc2716.historical"
# For "insertion" events to indicate what the next chunk ID should be in
Expand Down Expand Up @@ -235,5 +238,11 @@ class HistoryVisibility:
WORLD_READABLE = "world_readable"


class GuestAccess:
CAN_JOIN = "can_join"
# anything that is not "can_join" is considered "forbidden", but for completeness:
FORBIDDEN = "forbidden"


class ReadReceiptEventFields:
MSC2285_HIDDEN = "org.matrix.msc2285.hidden"
68 changes: 0 additions & 68 deletions synapse/handlers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
import logging
from typing import TYPE_CHECKING, Optional

import synapse.types
from synapse.api.constants import EventTypes, Membership
from synapse.api.ratelimiting import Ratelimiter
from synapse.types import UserID

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -115,68 +112,3 @@ async def ratelimit(self, requester, update=True, is_admin_redaction=False):
burst_count=burst_count,
update=update,
)

async def maybe_kick_guest_users(self, event, context=None):
# Technically this function invalidates current_state by changing it.
# Hopefully this isn't that important to the caller.
if event.type == EventTypes.GuestAccess:
guest_access = event.content.get("guest_access", "forbidden")
if guest_access != "can_join":
if context:
current_state_ids = await context.get_current_state_ids()
current_state_dict = await self.store.get_events(
list(current_state_ids.values())
)
current_state = list(current_state_dict.values())
else:
current_state_map = await self.state_handler.get_current_state(
event.room_id
)
current_state = list(current_state_map.values())

logger.info("maybe_kick_guest_users %r", current_state)
await self.kick_guest_users(current_state)

async def kick_guest_users(self, current_state):
for member_event in current_state:
try:
if member_event.type != EventTypes.Member:
continue

target_user = UserID.from_string(member_event.state_key)
if not self.hs.is_mine(target_user):
continue

if member_event.content["membership"] not in {
Membership.JOIN,
Membership.INVITE,
}:
continue

if (
"kind" not in member_event.content
or member_event.content["kind"] != "guest"
):
continue

# We make the user choose to leave, rather than have the
# event-sender kick them. This is partially because we don't
# need to worry about power levels, and partially because guest
# users are a concept which doesn't hugely work over federation,
# and having homeservers have their own users leave keeps more
# of that decision-making and control local to the guest-having
# homeserver.
requester = synapse.types.create_requester(
target_user, is_guest=True, authenticated_entity=self.server_name
)
handler = self.hs.get_room_member_handler()
await handler.update_membership(
requester,
target_user,
member_event.room_id,
"leave",
ratelimit=False,
require_consent=False,
)
except Exception as e:
logger.exception("Error kicking guest user: %s" % (e,))
17 changes: 14 additions & 3 deletions synapse/handlers/federation_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from synapse.api.constants import (
EventContentFields,
EventTypes,
GuestAccess,
Membership,
RejectedReason,
RoomEncryptionAlgorithms,
Expand Down Expand Up @@ -1321,9 +1322,7 @@ async def _check_event_auth(

if not context.rejected:
await self._check_for_soft_fail(event, state, backfilled, origin=origin)

if event.type == EventTypes.GuestAccess and not context.rejected:
await self.maybe_kick_guest_users(event)
await self._maybe_kick_guest_users(event)

# If we are going to send this event over federation we precaclculate
# the joined hosts.
Expand All @@ -1334,6 +1333,18 @@ async def _check_event_auth(

return context

async def _maybe_kick_guest_users(self, event: EventBase) -> None:
if event.type != EventTypes.GuestAccess:
return

guest_access = event.content.get(EventContentFields.GUEST_ACCESS)
if guest_access == GuestAccess.CAN_JOIN:
return

current_state_map = await self.state_handler.get_current_state(event.room_id)
current_state = list(current_state_map.values())
await self.hs.get_room_member_handler().kick_guest_users(current_state)

async def _check_for_soft_fail(
self,
event: EventBase,
Expand Down
27 changes: 25 additions & 2 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from synapse.api.constants import (
EventContentFields,
EventTypes,
GuestAccess,
Membership,
RelationTypes,
UserTypes,
Expand Down Expand Up @@ -426,7 +427,7 @@ def __init__(self, hs: "HomeServer"):

self.send_event = ReplicationSendEventRestServlet.make_client(hs)

# This is only used to get at ratelimit function, and maybe_kick_guest_users
# This is only used to get at ratelimit function
self.base_handler = BaseHandler(hs)

# We arbitrarily limit concurrent event creation for a room to 5.
Expand Down Expand Up @@ -1306,7 +1307,7 @@ async def persist_and_notify_client_event(
requester, is_admin_redaction=is_admin_redaction
)

await self.base_handler.maybe_kick_guest_users(event, context)
await self._maybe_kick_guest_users(event, context)

if event.type == EventTypes.CanonicalAlias:
# Validate a newly added alias or newly added alt_aliases.
Expand Down Expand Up @@ -1471,6 +1472,28 @@ def _notify():

return event

async def _maybe_kick_guest_users(
self, event: EventBase, context: EventContext
) -> None:
if event.type != EventTypes.GuestAccess:
return

guest_access = event.content.get(EventContentFields.GUEST_ACCESS)
if guest_access == GuestAccess.CAN_JOIN:
return

current_state_ids = await context.get_current_state_ids()

# since this is a client-generated event, it cannot be an outlier and we must
# therefore have the state ids.
assert current_state_ids is not None
current_state_dict = await self.store.get_events(
list(current_state_ids.values())
)
current_state = list(current_state_dict.values())
logger.info("maybe_kick_guest_users %r", current_state)
await self.hs.get_room_member_handler().kick_guest_users(current_state)

async def _bump_active_time(self, user: UserID) -> None:
try:
presence = self.hs.get_presence_handler()
Expand Down
5 changes: 4 additions & 1 deletion synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
from typing import TYPE_CHECKING, Any, Awaitable, Dict, List, Optional, Tuple

from synapse.api.constants import (
EventContentFields,
EventTypes,
GuestAccess,
HistoryVisibility,
JoinRules,
Membership,
Expand Down Expand Up @@ -988,7 +990,8 @@ async def send(etype: str, content: JsonDict, **kwargs) -> int:
if config["guest_can_join"]:
if (EventTypes.GuestAccess, "") not in initial_state:
last_sent_stream_id = await send(
etype=EventTypes.GuestAccess, content={"guest_access": "can_join"}
etype=EventTypes.GuestAccess,
content={EventContentFields.GUEST_ACCESS: GuestAccess.CAN_JOIN},
)

for (etype, state_key), content in initial_state.items():
Expand Down
12 changes: 9 additions & 3 deletions synapse/handlers/room_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
import msgpack
from unpaddedbase64 import decode_base64, encode_base64

from synapse.api.constants import EventTypes, HistoryVisibility, JoinRules
from synapse.api.constants import (
EventContentFields,
EventTypes,
GuestAccess,
HistoryVisibility,
JoinRules,
)
from synapse.api.errors import (
Codes,
HttpResponseException,
Expand Down Expand Up @@ -336,8 +342,8 @@ async def generate_room_entry(
guest_event = current_state.get((EventTypes.GuestAccess, ""))
guest = None
if guest_event:
guest = guest_event.content.get("guest_access", None)
result["guest_can_join"] = guest == "can_join"
guest = guest_event.content.get(EventContentFields.GUEST_ACCESS)
result["guest_can_join"] = guest == GuestAccess.CAN_JOIN

avatar_event = current_state.get(("m.room.avatar", ""))
if avatar_event:
Expand Down
65 changes: 59 additions & 6 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
AccountDataTypes,
EventContentFields,
EventTypes,
GuestAccess,
Membership,
)
from synapse.api.errors import (
Expand All @@ -44,6 +45,7 @@
RoomID,
StateMap,
UserID,
create_requester,
get_domain_from_id,
)
from synapse.util.async_helpers import Linearizer
Expand All @@ -70,6 +72,7 @@ def __init__(self, hs: "HomeServer"):
self.auth = hs.get_auth()
self.state_handler = hs.get_state_handler()
self.config = hs.config
self._server_name = hs.hostname

self.federation_handler = hs.get_federation_handler()
self.directory_handler = hs.get_directory_handler()
Expand Down Expand Up @@ -115,9 +118,8 @@ def __init__(self, hs: "HomeServer"):
burst_count=hs.config.ratelimiting.rc_invites_per_user.burst_count,
)

# This is only used to get at ratelimit function, and
# maybe_kick_guest_users. It's fine there are multiple of these as
# it doesn't store state.
# This is only used to get at the ratelimit function. It's fine there are
# multiple of these as it doesn't store state.
self.base_handler = BaseHandler(hs)

@abc.abstractmethod
Expand Down Expand Up @@ -1095,10 +1097,62 @@ async def _can_guest_join(self, current_state_ids: StateMap[str]) -> bool:
return bool(
guest_access
and guest_access.content
and "guest_access" in guest_access.content
and guest_access.content["guest_access"] == "can_join"
and guest_access.content.get(EventContentFields.GUEST_ACCESS)
== GuestAccess.CAN_JOIN
)

async def kick_guest_users(self, current_state: Iterable[EventBase]) -> None:
"""Kick any local guest users from the room.

This is called when the room state changes from guests allowed to not-allowed.

Params:
current_state: the current state of the room. We will iterate this to look
for guest users to kick.
"""
for member_event in current_state:
try:
if member_event.type != EventTypes.Member:
continue

if not self.hs.is_mine_id(member_event.state_key):
continue

if member_event.content["membership"] not in {
Membership.JOIN,
Membership.INVITE,
}:
continue

if (
"kind" not in member_event.content
or member_event.content["kind"] != "guest"
):
continue

# We make the user choose to leave, rather than have the
# event-sender kick them. This is partially because we don't
# need to worry about power levels, and partially because guest
# users are a concept which doesn't hugely work over federation,
# and having homeservers have their own users leave keeps more
# of that decision-making and control local to the guest-having
# homeserver.
target_user = UserID.from_string(member_event.state_key)
requester = create_requester(
target_user, is_guest=True, authenticated_entity=self._server_name
)
handler = self.hs.get_room_member_handler()
await handler.update_membership(
requester,
target_user,
member_event.room_id,
"leave",
ratelimit=False,
require_consent=False,
)
except Exception as e:
logger.exception("Error kicking guest user: %s" % (e,))

async def lookup_room_alias(
self, room_alias: RoomAlias
) -> Tuple[RoomID, List[str]]:
Expand Down Expand Up @@ -1352,7 +1406,6 @@ def __init__(self, hs: "HomeServer"):

self.distributor = hs.get_distributor()
self.distributor.declare("user_left_room")
self._server_name = hs.hostname
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now redundant, since RoomMemberHandler has its own _server_name.


async def _is_remote_room_too_complex(
self, room_id: str, remote_room_hosts: List[str]
Expand Down
6 changes: 4 additions & 2 deletions synapse/handlers/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from typing_extensions import Counter as CounterType

from synapse.api.constants import EventTypes, Membership
from synapse.api.constants import EventContentFields, EventTypes, Membership
from synapse.metrics import event_processing_positions
from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.types import JsonDict
Expand Down Expand Up @@ -273,7 +273,9 @@ async def _handle_deltas(
elif typ == EventTypes.CanonicalAlias:
room_state["canonical_alias"] = event_content.get("alias")
elif typ == EventTypes.GuestAccess:
room_state["guest_access"] = event_content.get("guest_access")
room_state["guest_access"] = event_content.get(
EventContentFields.GUEST_ACCESS
)

for room_id, state in room_to_state_updates.items():
logger.debug("Updating room_stats_state for %s: %s", room_id, state)
Expand Down