diff options
author | Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> | 2019-04-16 16:41:01 +0100 |
---|---|---|
committer | Erik Johnston <erikj@jki.re> | 2019-04-16 16:41:01 +0100 |
commit | e6218e48800a613e6c8f3d008cd90e4a01194fe5 (patch) | |
tree | b60dd24b637723a8cee735d67d80a3be713f89d9 | |
parent | Merge branch 'develop' into dinsic (diff) | |
download | synapse-e6218e48800a613e6c8f3d008cd90e4a01194fe5.tar.xz |
[DINSIC] Block internal users from inviting external users to a public room (#5061)
Co-Authored-By: babolivier <contact@brendanabolivier.com>
-rw-r--r-- | synapse/events/spamcheck.py | 8 | ||||
-rw-r--r-- | synapse/handlers/federation.py | 3 | ||||
-rw-r--r-- | synapse/handlers/room_member.py | 6 | ||||
-rw-r--r-- | synapse/rulecheck/domain_rule_checker.py | 15 | ||||
-rw-r--r-- | synapse/storage/room.py | 18 | ||||
-rw-r--r-- | tests/rulecheck/test_domainrulecheck.py | 22 |
6 files changed, 69 insertions, 3 deletions
diff --git a/synapse/events/spamcheck.py b/synapse/events/spamcheck.py index 93242658ba..b8ccced43b 100644 --- a/synapse/events/spamcheck.py +++ b/synapse/events/spamcheck.py @@ -47,7 +47,7 @@ class SpamChecker(object): return self.spam_checker.check_event_for_spam(event) def user_may_invite(self, inviter_userid, invitee_userid, third_party_invite, - room_id, new_room): + room_id, new_room, published_room): """Checks if a given user may send an invite If this method returns false, the invite will be rejected. @@ -60,9 +60,12 @@ class SpamChecker(object): third_party_invite (dict|None): If a third party invite then is a dict containing the medium and address of the invitee. room_id (str) - new_room (bool): Wether the user is being invited to the room as + new_room (bool): Whether the user is being invited to the room as part of a room creation, if so the invitee would have been included in the call to `user_may_create_room`. + published_room (bool): Whether the room the user is being invited + to has been published in the local homeserver's public room + directory. Returns: bool: True if the user may send an invite, otherwise False @@ -72,6 +75,7 @@ class SpamChecker(object): return self.spam_checker.user_may_invite( inviter_userid, invitee_userid, third_party_invite, room_id, new_room, + published_room, ) def user_may_create_room(self, userid, invite_list, third_party_invite_list, diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index b568d0627c..469f271849 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -1340,9 +1340,12 @@ class FederationHandler(BaseHandler): if self.hs.config.block_non_admin_invites: raise SynapseError(403, "This server does not accept room invites") + is_published = yield self.store.is_room_published(event.room_id) + if not self.spam_checker.user_may_invite( event.sender, event.state_key, None, room_id=event.room_id, new_room=False, + published_room=is_published, ): raise SynapseError( 403, "This user is not permitted to send invites to this server/user" diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index ee4a17597e..254b028fd7 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -426,11 +426,14 @@ class RoomMemberHandler(object): ) block_invite = True + is_published = yield self.store.is_room_published(room_id) + if not self.spam_checker.user_may_invite( requester.user.to_string(), target.to_string(), third_party_invite=None, room_id=room_id, new_room=new_room, + published_room=is_published, ): logger.info("Blocking invite due to spam checker") block_invite = True @@ -758,6 +761,8 @@ class RoomMemberHandler(object): id_server, medium, address ) + is_published = yield self.store.is_room_published(room_id) + if not self.spam_checker.user_may_invite( requester.user.to_string(), invitee, third_party_invite={ @@ -766,6 +771,7 @@ class RoomMemberHandler(object): }, room_id=room_id, new_room=new_room, + published_room=is_published, ): logger.info("Blocking invite due to spam checker") raise SynapseError( diff --git a/synapse/rulecheck/domain_rule_checker.py b/synapse/rulecheck/domain_rule_checker.py index bcec465ef4..212cc212cc 100644 --- a/synapse/rulecheck/domain_rule_checker.py +++ b/synapse/rulecheck/domain_rule_checker.py @@ -46,6 +46,10 @@ class DomainRuleChecker(object): # domain mapping rules above. can_only_invite_during_room_creation: false + # Prevent local users from inviting users from certain domains to + # rooms published in the room directory. + domains_prevented_from_being_invited_to_published_rooms: [] + # Allow third party invites can_invite_by_third_party_id: true @@ -68,6 +72,9 @@ class DomainRuleChecker(object): self.can_invite_by_third_party_id = config.get( "can_invite_by_third_party_id", True, ) + self.domains_prevented_from_being_invited_to_published_rooms = config.get( + "domains_prevented_from_being_invited_to_published_rooms", [], + ) def check_event_for_spam(self, event): """Implements synapse.events.SpamChecker.check_event_for_spam @@ -75,7 +82,7 @@ class DomainRuleChecker(object): return False def user_may_invite(self, inviter_userid, invitee_userid, third_party_invite, - room_id, new_room): + room_id, new_room, published_room=False): """Implements synapse.events.SpamChecker.user_may_invite """ if self.can_only_invite_during_room_creation and not new_room: @@ -95,6 +102,12 @@ class DomainRuleChecker(object): if inviter_domain not in self.domain_mapping: return self.default + if ( + published_room and + invitee_domain in self.domains_prevented_from_being_invited_to_published_rooms + ): + return False + return invitee_domain in self.domain_mapping[inviter_domain] def user_may_create_room(self, userid, invite_list, third_party_invite_list, diff --git a/synapse/storage/room.py b/synapse/storage/room.py index fe9d79d792..87854ae08c 100644 --- a/synapse/storage/room.py +++ b/synapse/storage/room.py @@ -171,6 +171,24 @@ class RoomWorkerStore(SQLBaseStore): desc="is_room_blocked", ) + @defer.inlineCallbacks + def is_room_published(self, room_id): + """Check whether a room has been published in the local public room + directory. + + Args: + room_id (str) + Returns: + bool: Whether the room is currently published in the room directory + """ + # Get room information + room_info = yield self.get_room(room_id) + if not room_info: + defer.returnValue(False) + + # Check the is_public value + defer.returnValue(room_info.get("is_public", False)) + @cachedInlineCallbacks(max_entries=10000) def get_ratelimit_for_user(self, user_id): """Check if there are any overrides for ratelimiting for the given diff --git a/tests/rulecheck/test_domainrulecheck.py b/tests/rulecheck/test_domainrulecheck.py index 803d680cec..e3167aa06b 100644 --- a/tests/rulecheck/test_domainrulecheck.py +++ b/tests/rulecheck/test_domainrulecheck.py @@ -32,6 +32,7 @@ class DomainRuleCheckerTestCase(unittest.TestCase): "source_one": ["target_one", "target_two"], "source_two": ["target_two"], }, + "domains_prevented_from_being_invited_to_published_rooms": ["target_two"] } check = DomainRuleChecker(config) self.assertTrue( @@ -50,6 +51,20 @@ class DomainRuleCheckerTestCase(unittest.TestCase): ) ) + # User can invite internal user to a published room + self.assertTrue( + check.user_may_invite( + "test:source_one", "test1:target_one", None, "room", False, True, + ) + ) + + # User can invite external user to a non-published room + self.assertTrue( + check.user_may_invite( + "test:source_one", "test:target_two", None, "room", False, False, + ) + ) + def test_disallowed(self): config = { "default": True, @@ -81,6 +96,13 @@ class DomainRuleCheckerTestCase(unittest.TestCase): ) ) + # User cannot invite external user to a published room + self.assertTrue( + check.user_may_invite( + "test:source_one", "test:target_two", None, "room", False, True, + ) + ) + def test_default_allow(self): config = { "default": True, |