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

Commit f12e1f0

Browse files
authored
Merge pull request #5440 from matrix-org/babolivier/third_party_event_rules
Allow server admins to define implementations of extra rules for allowing or denying incoming events
2 parents 9ca4ae7 + f874b16 commit f12e1f0

File tree

9 files changed

+284
-4
lines changed

9 files changed

+284
-4
lines changed

changelog.d/5440.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow server admins to define implementations of extra rules for allowing or denying incoming events.

docs/sample_config.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,3 +1351,16 @@ password_config:
13511351
# alias: "*"
13521352
# room_id: "*"
13531353
# action: allow
1354+
1355+
1356+
# Server admins can define a Python module that implements extra rules for
1357+
# allowing or denying incoming events. In order to work, this module needs to
1358+
# override the methods defined in synapse/events/third_party_rules.py.
1359+
#
1360+
# This feature is designed to be used in closed federations only, where each
1361+
# participating server enforces the same rules.
1362+
#
1363+
#third_party_event_rules:
1364+
# module: "my_custom_project.SuperRulesSet"
1365+
# config:
1366+
# example_option: 'things'

synapse/config/homeserver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from .server_notices_config import ServerNoticesConfig
3939
from .spam_checker import SpamCheckerConfig
4040
from .stats import StatsConfig
41+
from .third_party_event_rules import ThirdPartyRulesConfig
4142
from .tls import TlsConfig
4243
from .user_directory import UserDirectoryConfig
4344
from .voip import VoipConfig
@@ -73,5 +74,6 @@ class HomeServerConfig(
7374
StatsConfig,
7475
ServerNoticesConfig,
7576
RoomDirectoryConfig,
77+
ThirdPartyRulesConfig,
7678
):
7779
pass
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2019 The Matrix.org Foundation C.I.C.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from synapse.util.module_loader import load_module
17+
18+
from ._base import Config
19+
20+
21+
class ThirdPartyRulesConfig(Config):
22+
def read_config(self, config):
23+
self.third_party_event_rules = None
24+
25+
provider = config.get("third_party_event_rules", None)
26+
if provider is not None:
27+
self.third_party_event_rules = load_module(provider)
28+
29+
def default_config(self, **kwargs):
30+
return """\
31+
# Server admins can define a Python module that implements extra rules for
32+
# allowing or denying incoming events. In order to work, this module needs to
33+
# override the methods defined in synapse/events/third_party_rules.py.
34+
#
35+
# This feature is designed to be used in closed federations only, where each
36+
# participating server enforces the same rules.
37+
#
38+
#third_party_event_rules:
39+
# module: "my_custom_project.SuperRulesSet"
40+
# config:
41+
# example_option: 'things'
42+
"""
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2019 The Matrix.org Foundation C.I.C.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from twisted.internet import defer
17+
18+
19+
class ThirdPartyEventRules(object):
20+
"""Allows server admins to provide a Python module implementing an extra set of rules
21+
to apply when processing events.
22+
23+
This is designed to help admins of closed federations with enforcing custom
24+
behaviours.
25+
"""
26+
27+
def __init__(self, hs):
28+
self.third_party_rules = None
29+
30+
self.store = hs.get_datastore()
31+
32+
module = None
33+
config = None
34+
if hs.config.third_party_event_rules:
35+
module, config = hs.config.third_party_event_rules
36+
37+
if module is not None:
38+
self.third_party_rules = module(config=config)
39+
40+
@defer.inlineCallbacks
41+
def check_event_allowed(self, event, context):
42+
"""Check if a provided event should be allowed in the given context.
43+
44+
Args:
45+
event (synapse.events.EventBase): The event to be checked.
46+
context (synapse.events.snapshot.EventContext): The context of the event.
47+
48+
Returns:
49+
defer.Deferred(bool), True if the event should be allowed, False if not.
50+
"""
51+
if self.third_party_rules is None:
52+
defer.returnValue(True)
53+
54+
prev_state_ids = yield context.get_prev_state_ids(self.store)
55+
56+
# Retrieve the state events from the database.
57+
state_events = {}
58+
for key, event_id in prev_state_ids.items():
59+
state_events[key] = yield self.store.get_event(event_id, allow_none=True)
60+
61+
ret = yield self.third_party_rules.check_event_allowed(event, state_events)
62+
defer.returnValue(ret)

synapse/handlers/federation.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# Copyright 2014-2016 OpenMarket Ltd
3-
# Copyright 2018 New Vector Ltd
3+
# Copyright 2017-2018 New Vector Ltd
4+
# Copyright 2019 The Matrix.org Foundation C.I.C.
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
67
# you may not use this file except in compliance with the License.
@@ -33,6 +34,7 @@
3334
from synapse.api.errors import (
3435
AuthError,
3536
CodeMessageException,
37+
Codes,
3638
FederationDeniedError,
3739
FederationError,
3840
RequestSendFailed,
@@ -127,6 +129,8 @@ def __init__(self, hs):
127129
self.room_queues = {}
128130
self._room_pdu_linearizer = Linearizer("fed_room_pdu")
129131

132+
self.third_party_event_rules = hs.get_third_party_event_rules()
133+
130134
@defer.inlineCallbacks
131135
def on_receive_pdu(
132136
self, origin, pdu, sent_to_us_directly=False,
@@ -1258,6 +1262,15 @@ def on_make_join_request(self, room_id, user_id):
12581262
logger.warn("Failed to create join %r because %s", event, e)
12591263
raise e
12601264

1265+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
1266+
event, context,
1267+
)
1268+
if not event_allowed:
1269+
logger.info("Creation of join %s forbidden by third-party rules", event)
1270+
raise SynapseError(
1271+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
1272+
)
1273+
12611274
# The remote hasn't signed it yet, obviously. We'll do the full checks
12621275
# when we get the event back in `on_send_join_request`
12631276
yield self.auth.check_from_context(
@@ -1300,6 +1313,15 @@ def on_send_join_request(self, origin, pdu):
13001313
origin, event
13011314
)
13021315

1316+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
1317+
event, context,
1318+
)
1319+
if not event_allowed:
1320+
logger.info("Sending of join %s forbidden by third-party rules", event)
1321+
raise SynapseError(
1322+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
1323+
)
1324+
13031325
logger.debug(
13041326
"on_send_join_request: After _handle_new_event: %s, sigs: %s",
13051327
event.event_id,
@@ -1458,6 +1480,15 @@ def on_make_leave_request(self, room_id, user_id):
14581480
builder=builder,
14591481
)
14601482

1483+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
1484+
event, context,
1485+
)
1486+
if not event_allowed:
1487+
logger.warning("Creation of leave %s forbidden by third-party rules", event)
1488+
raise SynapseError(
1489+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
1490+
)
1491+
14611492
try:
14621493
# The remote hasn't signed it yet, obviously. We'll do the full checks
14631494
# when we get the event back in `on_send_leave_request`
@@ -1484,10 +1515,19 @@ def on_send_leave_request(self, origin, pdu):
14841515

14851516
event.internal_metadata.outlier = False
14861517

1487-
yield self._handle_new_event(
1518+
context = yield self._handle_new_event(
14881519
origin, event
14891520
)
14901521

1522+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
1523+
event, context,
1524+
)
1525+
if not event_allowed:
1526+
logger.info("Sending of leave %s forbidden by third-party rules", event)
1527+
raise SynapseError(
1528+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
1529+
)
1530+
14911531
logger.debug(
14921532
"on_send_leave_request: After _handle_new_event: %s, sigs: %s",
14931533
event.event_id,
@@ -2550,6 +2590,18 @@ def exchange_third_party_invite(
25502590
builder=builder
25512591
)
25522592

2593+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
2594+
event, context,
2595+
)
2596+
if not event_allowed:
2597+
logger.info(
2598+
"Creation of threepid invite %s forbidden by third-party rules",
2599+
event,
2600+
)
2601+
raise SynapseError(
2602+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
2603+
)
2604+
25532605
event, context = yield self.add_display_name_to_third_party_invite(
25542606
room_version, event_dict, event, context
25552607
)
@@ -2598,6 +2650,18 @@ def on_exchange_third_party_invite_request(self, origin, room_id, event_dict):
25982650
builder=builder,
25992651
)
26002652

2653+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
2654+
event, context,
2655+
)
2656+
if not event_allowed:
2657+
logger.warning(
2658+
"Exchange of threepid invite %s forbidden by third-party rules",
2659+
event,
2660+
)
2661+
raise SynapseError(
2662+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
2663+
)
2664+
26012665
event, context = yield self.add_display_name_to_third_party_invite(
26022666
room_version, event_dict, event, context
26032667
)

synapse/handlers/message.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright 2014 - 2016 OpenMarket Ltd
3-
# Copyright 2017 - 2018 New Vector Ltd
2+
# Copyright 2014-2016 OpenMarket Ltd
3+
# Copyright 2017-2018 New Vector Ltd
4+
# Copyright 2019 The Matrix.org Foundation C.I.C.
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
67
# you may not use this file except in compliance with the License.
@@ -248,6 +249,7 @@ def __init__(self, hs):
248249
self.action_generator = hs.get_action_generator()
249250

250251
self.spam_checker = hs.get_spam_checker()
252+
self.third_party_event_rules = hs.get_third_party_event_rules()
251253

252254
self._block_events_without_consent_error = (
253255
self.config.block_events_without_consent_error
@@ -658,6 +660,14 @@ def handle_new_client_event(
658660
else:
659661
room_version = yield self.store.get_room_version(event.room_id)
660662

663+
event_allowed = yield self.third_party_event_rules.check_event_allowed(
664+
event, context,
665+
)
666+
if not event_allowed:
667+
raise SynapseError(
668+
403, "This event is not allowed in this context", Codes.FORBIDDEN,
669+
)
670+
661671
try:
662672
yield self.auth.check_from_context(room_version, event, context)
663673
except AuthError as err:

synapse/server.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# Copyright 2014-2016 OpenMarket Ltd
3+
# Copyright 2017-2018 New Vector Ltd
4+
# Copyright 2019 The Matrix.org Foundation C.I.C.
35
#
46
# Licensed under the Apache License, Version 2.0 (the "License");
57
# you may not use this file except in compliance with the License.
@@ -35,6 +37,7 @@
3537
from synapse.crypto.keyring import Keyring
3638
from synapse.events.builder import EventBuilderFactory
3739
from synapse.events.spamcheck import SpamChecker
40+
from synapse.events.third_party_rules import ThirdPartyEventRules
3841
from synapse.events.utils import EventClientSerializer
3942
from synapse.federation.federation_client import FederationClient
4043
from synapse.federation.federation_server import (
@@ -178,6 +181,7 @@ def build_DEPENDENCY(self)
178181
'groups_attestation_renewer',
179182
'secrets',
180183
'spam_checker',
184+
'third_party_event_rules',
181185
'room_member_handler',
182186
'federation_registry',
183187
'server_notices_manager',
@@ -483,6 +487,9 @@ def build_stats_handler(self):
483487
def build_spam_checker(self):
484488
return SpamChecker(self)
485489

490+
def build_third_party_event_rules(self):
491+
return ThirdPartyEventRules(self)
492+
486493
def build_room_member_handler(self):
487494
if self.config.worker_app:
488495
return RoomMemberWorkerHandler(self)

0 commit comments

Comments
 (0)