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

Commit

Permalink
Merge pull request #5440 from matrix-org/babolivier/third_party_event…
Browse files Browse the repository at this point in the history
…_rules

Allow server admins to define implementations of extra rules for allowing or denying incoming events
  • Loading branch information
babolivier authored Jun 14, 2019
2 parents 9ca4ae7 + f874b16 commit f12e1f0
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 4 deletions.
1 change: 1 addition & 0 deletions changelog.d/5440.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow server admins to define implementations of extra rules for allowing or denying incoming events.
13 changes: 13 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1351,3 +1351,16 @@ password_config:
# alias: "*"
# room_id: "*"
# action: allow


# Server admins can define a Python module that implements extra rules for
# allowing or denying incoming events. In order to work, this module needs to
# override the methods defined in synapse/events/third_party_rules.py.
#
# This feature is designed to be used in closed federations only, where each
# participating server enforces the same rules.
#
#third_party_event_rules:
# module: "my_custom_project.SuperRulesSet"
# config:
# example_option: 'things'
2 changes: 2 additions & 0 deletions synapse/config/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from .server_notices_config import ServerNoticesConfig
from .spam_checker import SpamCheckerConfig
from .stats import StatsConfig
from .third_party_event_rules import ThirdPartyRulesConfig
from .tls import TlsConfig
from .user_directory import UserDirectoryConfig
from .voip import VoipConfig
Expand Down Expand Up @@ -73,5 +74,6 @@ class HomeServerConfig(
StatsConfig,
ServerNoticesConfig,
RoomDirectoryConfig,
ThirdPartyRulesConfig,
):
pass
42 changes: 42 additions & 0 deletions synapse/config/third_party_event_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 synapse.util.module_loader import load_module

from ._base import Config


class ThirdPartyRulesConfig(Config):
def read_config(self, config):
self.third_party_event_rules = None

provider = config.get("third_party_event_rules", None)
if provider is not None:
self.third_party_event_rules = load_module(provider)

def default_config(self, **kwargs):
return """\
# Server admins can define a Python module that implements extra rules for
# allowing or denying incoming events. In order to work, this module needs to
# override the methods defined in synapse/events/third_party_rules.py.
#
# This feature is designed to be used in closed federations only, where each
# participating server enforces the same rules.
#
#third_party_event_rules:
# module: "my_custom_project.SuperRulesSet"
# config:
# example_option: 'things'
"""
62 changes: 62 additions & 0 deletions synapse/events/third_party_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 twisted.internet import defer


class ThirdPartyEventRules(object):
"""Allows server admins to provide a Python module implementing an extra set of rules
to apply when processing events.
This is designed to help admins of closed federations with enforcing custom
behaviours.
"""

def __init__(self, hs):
self.third_party_rules = None

self.store = hs.get_datastore()

module = None
config = None
if hs.config.third_party_event_rules:
module, config = hs.config.third_party_event_rules

if module is not None:
self.third_party_rules = module(config=config)

@defer.inlineCallbacks
def check_event_allowed(self, event, context):
"""Check if a provided event should be allowed in the given context.
Args:
event (synapse.events.EventBase): The event to be checked.
context (synapse.events.snapshot.EventContext): The context of the event.
Returns:
defer.Deferred(bool), True if the event should be allowed, False if not.
"""
if self.third_party_rules is None:
defer.returnValue(True)

prev_state_ids = yield context.get_prev_state_ids(self.store)

# Retrieve the state events from the database.
state_events = {}
for key, event_id in prev_state_ids.items():
state_events[key] = yield self.store.get_event(event_id, allow_none=True)

ret = yield self.third_party_rules.check_event_allowed(event, state_events)
defer.returnValue(ret)
68 changes: 66 additions & 2 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
# Copyright 2018 New Vector Ltd
# Copyright 2017-2018 New Vector Ltd
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +34,7 @@
from synapse.api.errors import (
AuthError,
CodeMessageException,
Codes,
FederationDeniedError,
FederationError,
RequestSendFailed,
Expand Down Expand Up @@ -127,6 +129,8 @@ def __init__(self, hs):
self.room_queues = {}
self._room_pdu_linearizer = Linearizer("fed_room_pdu")

self.third_party_event_rules = hs.get_third_party_event_rules()

@defer.inlineCallbacks
def on_receive_pdu(
self, origin, pdu, sent_to_us_directly=False,
Expand Down Expand Up @@ -1258,6 +1262,15 @@ def on_make_join_request(self, room_id, user_id):
logger.warn("Failed to create join %r because %s", event, e)
raise e

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.info("Creation of join %s forbidden by third-party rules", event)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

# The remote hasn't signed it yet, obviously. We'll do the full checks
# when we get the event back in `on_send_join_request`
yield self.auth.check_from_context(
Expand Down Expand Up @@ -1300,6 +1313,15 @@ def on_send_join_request(self, origin, pdu):
origin, event
)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.info("Sending of join %s forbidden by third-party rules", event)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

logger.debug(
"on_send_join_request: After _handle_new_event: %s, sigs: %s",
event.event_id,
Expand Down Expand Up @@ -1458,6 +1480,15 @@ def on_make_leave_request(self, room_id, user_id):
builder=builder,
)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.warning("Creation of leave %s forbidden by third-party rules", event)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

try:
# The remote hasn't signed it yet, obviously. We'll do the full checks
# when we get the event back in `on_send_leave_request`
Expand All @@ -1484,10 +1515,19 @@ def on_send_leave_request(self, origin, pdu):

event.internal_metadata.outlier = False

yield self._handle_new_event(
context = yield self._handle_new_event(
origin, event
)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.info("Sending of leave %s forbidden by third-party rules", event)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

logger.debug(
"on_send_leave_request: After _handle_new_event: %s, sigs: %s",
event.event_id,
Expand Down Expand Up @@ -2550,6 +2590,18 @@ def exchange_third_party_invite(
builder=builder
)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.info(
"Creation of threepid invite %s forbidden by third-party rules",
event,
)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

event, context = yield self.add_display_name_to_third_party_invite(
room_version, event_dict, event, context
)
Expand Down Expand Up @@ -2598,6 +2650,18 @@ def on_exchange_third_party_invite_request(self, origin, room_id, event_dict):
builder=builder,
)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
logger.warning(
"Exchange of threepid invite %s forbidden by third-party rules",
event,
)
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

event, context = yield self.add_display_name_to_third_party_invite(
room_version, event_dict, event, context
)
Expand Down
14 changes: 12 additions & 2 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2014 - 2016 OpenMarket Ltd
# Copyright 2017 - 2018 New Vector Ltd
# Copyright 2014-2016 OpenMarket Ltd
# Copyright 2017-2018 New Vector Ltd
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -248,6 +249,7 @@ def __init__(self, hs):
self.action_generator = hs.get_action_generator()

self.spam_checker = hs.get_spam_checker()
self.third_party_event_rules = hs.get_third_party_event_rules()

self._block_events_without_consent_error = (
self.config.block_events_without_consent_error
Expand Down Expand Up @@ -658,6 +660,14 @@ def handle_new_client_event(
else:
room_version = yield self.store.get_room_version(event.room_id)

event_allowed = yield self.third_party_event_rules.check_event_allowed(
event, context,
)
if not event_allowed:
raise SynapseError(
403, "This event is not allowed in this context", Codes.FORBIDDEN,
)

try:
yield self.auth.check_from_context(room_version, event, context)
except AuthError as err:
Expand Down
7 changes: 7 additions & 0 deletions synapse/server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
# Copyright 2017-2018 New Vector Ltd
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,6 +37,7 @@
from synapse.crypto.keyring import Keyring
from synapse.events.builder import EventBuilderFactory
from synapse.events.spamcheck import SpamChecker
from synapse.events.third_party_rules import ThirdPartyEventRules
from synapse.events.utils import EventClientSerializer
from synapse.federation.federation_client import FederationClient
from synapse.federation.federation_server import (
Expand Down Expand Up @@ -178,6 +181,7 @@ def build_DEPENDENCY(self)
'groups_attestation_renewer',
'secrets',
'spam_checker',
'third_party_event_rules',
'room_member_handler',
'federation_registry',
'server_notices_manager',
Expand Down Expand Up @@ -483,6 +487,9 @@ def build_stats_handler(self):
def build_spam_checker(self):
return SpamChecker(self)

def build_third_party_event_rules(self):
return ThirdPartyEventRules(self)

def build_room_member_handler(self):
if self.config.worker_app:
return RoomMemberWorkerHandler(self)
Expand Down
Loading

0 comments on commit f12e1f0

Please sign in to comment.