summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Morgan <1342360+anoadragon453@users.noreply.github.com>2019-04-16 16:41:01 +0100
committerErik Johnston <erikj@jki.re>2019-04-16 16:41:01 +0100
commite6218e48800a613e6c8f3d008cd90e4a01194fe5 (patch)
treeb60dd24b637723a8cee735d67d80a3be713f89d9
parentMerge branch 'develop' into dinsic (diff)
downloadsynapse-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.py8
-rw-r--r--synapse/handlers/federation.py3
-rw-r--r--synapse/handlers/room_member.py6
-rw-r--r--synapse/rulecheck/domain_rule_checker.py15
-rw-r--r--synapse/storage/room.py18
-rw-r--r--tests/rulecheck/test_domainrulecheck.py22
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,