Add 3PID invite support to spam checker

This commit is contained in:
Erik Johnston 2019-03-21 12:25:33 +00:00
parent b7d7d20a38
commit 164798ec32
7 changed files with 51 additions and 18 deletions

View file

@ -46,14 +46,19 @@ class SpamChecker(object):
return self.spam_checker.check_event_for_spam(event) return self.spam_checker.check_event_for_spam(event)
def user_may_invite(self, inviter_userid, invitee_userid, room_id, new_room): def user_may_invite(self, inviter_userid, invitee_userid, third_party_invite,
room_id, new_room):
"""Checks if a given user may send an invite """Checks if a given user may send an invite
If this method returns false, the invite will be rejected. If this method returns false, the invite will be rejected.
Args: Args:
inviter_userid (str) inviter_userid (str)
invitee_userid (str) invitee_userid (str|None): The user ID of the invitee. Is None
if this is a third party invite and the 3PID is not bound to a
user ID.
invitee_userid (dict|None): If a third party invite then is a dict
containing the medium and address of the invitee.
room_id (str) room_id (str)
new_room (bool): Wether the user is being invited to the room as new_room (bool): Wether the user is being invited to the room as
part of a room creation, if so the invitee would have been part of a room creation, if so the invitee would have been
@ -66,7 +71,7 @@ class SpamChecker(object):
return True return True
return self.spam_checker.user_may_invite( return self.spam_checker.user_may_invite(
inviter_userid, invitee_userid, room_id, new_room, inviter_userid, invitee_userid, third_party_invite, room_id, new_room,
) )
def user_may_create_room(self, userid, invite_list, third_party_invite_list, def user_may_create_room(self, userid, invite_list, third_party_invite_list,

View file

@ -1346,7 +1346,8 @@ class FederationHandler(BaseHandler):
raise SynapseError(403, "This server does not accept room invites") raise SynapseError(403, "This server does not accept room invites")
if not self.spam_checker.user_may_invite( if not self.spam_checker.user_may_invite(
event.sender, event.state_key, event.room_id, new_room=False, event.sender, event.state_key, None,
room_id=event.room_id, new_room=False,
): ):
raise SynapseError( raise SynapseError(
403, "This user is not permitted to send invites to this server/user" 403, "This user is not permitted to send invites to this server/user"

View file

@ -661,6 +661,7 @@ class RoomCreationHandler(BaseHandler):
id_server, id_server,
requester, requester,
txn_id=None, txn_id=None,
new_room=True,
) )
result = {"room_id": room_id} result = {"room_id": room_id}

View file

@ -425,7 +425,9 @@ class RoomMemberHandler(object):
block_invite = True block_invite = True
if not self.spam_checker.user_may_invite( if not self.spam_checker.user_may_invite(
requester.user.to_string(), target.to_string(), room_id, requester.user.to_string(), target.to_string(),
third_party_invite=None,
room_id=room_id,
new_room=new_room, new_room=new_room,
): ):
logger.info("Blocking invite due to spam checker") logger.info("Blocking invite due to spam checker")
@ -728,7 +730,8 @@ class RoomMemberHandler(object):
address, address,
id_server, id_server,
requester, requester,
txn_id txn_id,
new_room=False,
): ):
if self.config.block_non_admin_invites: if self.config.block_non_admin_invites:
is_requester_admin = yield self.auth.is_server_admin( is_requester_admin = yield self.auth.is_server_admin(
@ -744,6 +747,20 @@ class RoomMemberHandler(object):
id_server, medium, address id_server, medium, address
) )
if not self.spam_checker.user_may_invite(
requester.user.to_string(), invitee,
third_party_invite={
"medium": medium,
"address": address,
},
room_id=room_id,
new_room=new_room,
):
logger.info("Blocking invite due to spam checker")
raise SynapseError(
403, "Invites have been disabled on this server",
)
if invitee: if invitee:
yield self.update_membership( yield self.update_membership(
requester, requester,

View file

@ -666,7 +666,8 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
content["address"], content["address"],
content["id_server"], content["id_server"],
requester, requester,
txn_id txn_id,
new_room=False,
) )
defer.returnValue((200, {})) defer.returnValue((200, {}))
return return

View file

@ -74,13 +74,19 @@ class DomainRuleChecker(object):
""" """
return False return False
def user_may_invite(self, inviter_userid, invitee_userid, room_id, def user_may_invite(self, inviter_userid, invitee_userid, third_party_invite,
new_room): room_id, new_room):
"""Implements synapse.events.SpamChecker.user_may_invite """Implements synapse.events.SpamChecker.user_may_invite
""" """
if self.can_only_invite_during_room_creation and not new_room: if self.can_only_invite_during_room_creation and not new_room:
return False return False
if not self.can_invite_by_third_party_id and third_party_invite:
return False
if third_party_invite and not invitee_userid:
return True
inviter_domain = self._get_domain_from_id(inviter_userid) inviter_domain = self._get_domain_from_id(inviter_userid)
invitee_domain = self._get_domain_from_id(invitee_userid) invitee_domain = self._get_domain_from_id(invitee_userid)

View file

@ -35,13 +35,15 @@ class DomainRuleCheckerTestCase(unittest.TestCase):
} }
check = DomainRuleChecker(config) check = DomainRuleChecker(config)
self.assertTrue( self.assertTrue(
check.user_may_invite("test:source_one", "test:target_one", "room", False) check.user_may_invite(
"test:source_one", "test:target_one", None, "room", False,
)
) )
self.assertTrue( self.assertTrue(
check.user_may_invite("test:source_one", "test:target_two", "room", False) check.user_may_invite("test:source_one", "test:target_two", None, "room", False)
) )
self.assertTrue( self.assertTrue(
check.user_may_invite("test:source_two", "test:target_two", "room", False) check.user_may_invite("test:source_two", "test:target_two", None, "room", False)
) )
def test_disallowed(self): def test_disallowed(self):
@ -55,16 +57,16 @@ class DomainRuleCheckerTestCase(unittest.TestCase):
} }
check = DomainRuleChecker(config) check = DomainRuleChecker(config)
self.assertFalse( self.assertFalse(
check.user_may_invite("test:source_one", "test:target_three", "room", False) check.user_may_invite("test:source_one", "test:target_three", None, "room", False)
) )
self.assertFalse( self.assertFalse(
check.user_may_invite("test:source_two", "test:target_three", "room", False) check.user_may_invite("test:source_two", "test:target_three", None, "room", False)
) )
self.assertFalse( self.assertFalse(
check.user_may_invite("test:source_two", "test:target_one", "room", False) check.user_may_invite("test:source_two", "test:target_one", None, "room", False)
) )
self.assertFalse( self.assertFalse(
check.user_may_invite("test:source_four", "test:target_one", "room", False) check.user_may_invite("test:source_four", "test:target_one", None, "room", False)
) )
def test_default_allow(self): def test_default_allow(self):
@ -77,7 +79,7 @@ class DomainRuleCheckerTestCase(unittest.TestCase):
} }
check = DomainRuleChecker(config) check = DomainRuleChecker(config)
self.assertTrue( self.assertTrue(
check.user_may_invite("test:source_three", "test:target_one", "room", False) check.user_may_invite("test:source_three", "test:target_one", None, "room", False)
) )
def test_default_deny(self): def test_default_deny(self):
@ -90,7 +92,7 @@ class DomainRuleCheckerTestCase(unittest.TestCase):
} }
check = DomainRuleChecker(config) check = DomainRuleChecker(config)
self.assertFalse( self.assertFalse(
check.user_may_invite("test:source_three", "test:target_one", "room", False) check.user_may_invite("test:source_three", "test:target_one", None, "room", False)
) )
def test_config_parse(self): def test_config_parse(self):