mirror of
https://github.com/element-hq/synapse.git
synced 2026-03-29 06:39:55 +00:00
Add policy server checks on membership events too
This commit is contained in:
@@ -139,6 +139,9 @@ class FederationClient(FederationBase):
|
||||
self.server_name = hs.hostname
|
||||
self.signing_key = hs.signing_key
|
||||
|
||||
# Avoid a cyclic dependency between this and the room policy handler itself
|
||||
self._get_room_policy_handler = hs.get_room_policy_handler
|
||||
|
||||
# Cache mapping `event_id` to a tuple of the event itself and the `pull_origin`
|
||||
# (which server we pulled the event from)
|
||||
self._get_pdu_cache: ExpiringCache[str, tuple[EventBase, str]] = ExpiringCache(
|
||||
@@ -1317,6 +1320,11 @@ class FederationClient(FederationBase):
|
||||
|
||||
pdu = event_from_pdu_json(pdu_dict, room_version)
|
||||
|
||||
# Verify the policy server has allowed the event, raising a SynapseError if not.
|
||||
# We use `raise_if_not_allowed` instead of asking for a signature because the
|
||||
# event we received might already have a policy server signature.
|
||||
await self._get_room_policy_handler().raise_if_not_allowed(pdu)
|
||||
|
||||
# Check signatures are correct.
|
||||
try:
|
||||
pdu = await self._check_sigs_and_hash(room_version, pdu)
|
||||
|
||||
@@ -151,6 +151,7 @@ class FederationServer(FederationBase):
|
||||
self._state_storage_controller = hs.get_storage_controllers().state
|
||||
|
||||
self.device_handler = hs.get_device_handler()
|
||||
self._room_policy_handler = hs.get_room_policy_handler()
|
||||
|
||||
# Ensure the following handlers are loaded since they register callbacks
|
||||
# with FederationHandlerRegistry.
|
||||
@@ -741,6 +742,12 @@ class FederationServer(FederationBase):
|
||||
pdu.event_id,
|
||||
)
|
||||
raise SynapseError(403, Codes.FORBIDDEN)
|
||||
|
||||
# Verify the policy server has allowed the event, raising a SynapseError if not.
|
||||
# We use `raise_if_not_allowed` instead of asking for a signature because the
|
||||
# event we received might already have a policy server signature.
|
||||
await self._room_policy_handler.raise_if_not_allowed(pdu)
|
||||
|
||||
try:
|
||||
pdu = await self._check_sigs_and_hash(room_version, pdu)
|
||||
except InvalidEventSignatureError as e:
|
||||
@@ -1019,6 +1026,11 @@ class FederationServer(FederationBase):
|
||||
)
|
||||
)
|
||||
|
||||
# Verify the policy server has allowed the event, raising a SynapseError if not.
|
||||
# We use `raise_if_not_allowed` instead of asking for a signature because the
|
||||
# event we received might already have a policy server signature.
|
||||
await self._room_policy_handler.raise_if_not_allowed(event)
|
||||
|
||||
try:
|
||||
event = await self._check_sigs_and_hash(room_version, event)
|
||||
except InvalidEventSignatureError as e:
|
||||
|
||||
@@ -107,6 +107,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||
self.account_data_handler = hs.get_account_data_handler()
|
||||
self.event_auth_handler = hs.get_event_auth_handler()
|
||||
self._worker_lock_handler = hs.get_worker_locks_handler()
|
||||
self._room_policy_handler = hs.get_room_policy_handler()
|
||||
|
||||
self._membership_types_to_include_profile_data_in = {
|
||||
Membership.JOIN,
|
||||
@@ -496,6 +497,16 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||
outlier=outlier,
|
||||
delay_id=delay_id,
|
||||
)
|
||||
|
||||
# Before we persist the event, run the event through the policy server
|
||||
# to ensure the membership action can happen. This will raise a
|
||||
# SynapseError if the policy server refused it, and otherwise append a
|
||||
# signature to the event.
|
||||
await self._room_policy_handler.ask_policy_server_to_sign_event(
|
||||
event,
|
||||
verify=True,
|
||||
)
|
||||
|
||||
context = await unpersisted_context.persist(event)
|
||||
prev_state_ids = await context.get_prev_state_ids(
|
||||
StateFilter.from_types([(EventTypes.Member, user_id)])
|
||||
|
||||
@@ -151,8 +151,40 @@ class RoomPolicyHandler:
|
||||
Returns:
|
||||
bool: True if the event is allowed in the room, False otherwise.
|
||||
"""
|
||||
try:
|
||||
await self.raise_if_not_allowed(event)
|
||||
except Exception as ex:
|
||||
# We probably caught either a refusal to sign, an invalid signature, or
|
||||
# some other transient or network error. These are all rejection cases.
|
||||
logger.warning("Failed to get a signature from the policy server: %s", ex)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def raise_if_not_allowed(self, event: EventBase) -> None:
|
||||
"""
|
||||
Check if the given event is allowed in the room by the policy server, and raise
|
||||
a SynapseError if not.
|
||||
|
||||
Note: This will *not* raise errors if the room's policy server is Synapse itself.
|
||||
This is because Synapse can't be a policy server (currently).
|
||||
|
||||
If no policy server is configured in the room, this does not raise. Similarly, if
|
||||
the policy server is invalid in any way (not joined, not a server, etc), this
|
||||
does not raise.
|
||||
|
||||
If a valid and contactable policy server is configured in the room, this raises
|
||||
a SynapseError if that server suggests the event is spammy, and returns nothing
|
||||
otherwise.
|
||||
|
||||
Args:
|
||||
event: The event to check. This should be a fully-formed PDU.
|
||||
|
||||
Raises:
|
||||
SynapseError: If the event is spammy according to the policy server.
|
||||
"""
|
||||
if self._is_policy_server_state_event(event):
|
||||
return True # always allow policy server change events
|
||||
return # always allow policy server change events
|
||||
|
||||
policy_server = await self._get_policy_server(event.room_id)
|
||||
if policy_server is None:
|
||||
@@ -165,18 +197,13 @@ class RoomPolicyHandler:
|
||||
event, policy_server.server_name, policy_server.public_key
|
||||
)
|
||||
if valid:
|
||||
return True # valid signature == allow
|
||||
return # valid signature == allow
|
||||
|
||||
# We couldn't save the HTTP hit, so do that hit.
|
||||
try:
|
||||
await self.ask_policy_server_to_sign_event(event, verify=True)
|
||||
except Exception as ex:
|
||||
# We probably caught either a refusal to sign, an invalid signature, or
|
||||
# some other transient or network error. These are all rejection cases.
|
||||
logger.warning("Failed to get a signature from the policy server: %s", ex)
|
||||
return False
|
||||
# This raises the SynapseError we mentioned in the docstring
|
||||
await self.ask_policy_server_to_sign_event(event, verify=True)
|
||||
|
||||
return True # passed all verifications and checks, so allow
|
||||
return # passed all verifications and checks, so allow
|
||||
|
||||
async def _verify_policy_server_signature(
|
||||
self, event: EventBase, policy_server: str, public_key: str
|
||||
|
||||
Reference in New Issue
Block a user