3131 Membership ,
3232 RelationTypes ,
3333)
34- from synapse .api .errors import Codes , HttpResponseException
34+ from synapse .api .errors import Code , Codes , HttpResponseException
3535from synapse .handlers .pagination import PurgeStatus
3636from synapse .rest import admin
3737from synapse .rest .client import account , directory , login , profile , room , sync
3838from synapse .server import HomeServer
39+ from synapse .spam_checker_api import ALLOW , Decision
3940from synapse .types import JsonDict , RoomAlias , UserID , create_requester
4041from synapse .util import Clock
4142from synapse .util .stringutils import random_string
@@ -676,9 +677,9 @@ def test_post_room_invitees_ratelimit(self) -> None:
676677 channel = self .make_request ("POST" , "/createRoom" , content )
677678 self .assertEqual (200 , channel .code )
678679
679- def test_spam_checker_may_join_room (self ) -> None :
680+ def test_spam_checker_may_join_room_old (self ) -> None :
680681 """Tests that the user_may_join_room spam checker callback is correctly bypassed
681- when creating a new room.
682+ when creating a new room (old-style API, returning a boolean) .
682683 """
683684
684685 async def user_may_join_room (
@@ -700,6 +701,29 @@ async def user_may_join_room(
700701
701702 self .assertEqual (join_mock .call_count , 0 )
702703
704+ def test_spam_checker_may_join_room (self ) -> None :
705+ """Tests that the user_may_join_room spam checker callback is correctly bypassed
706+ when creating a new room.
707+ """
708+
709+ async def user_may_join_room (
710+ mxid : str ,
711+ room_id : str ,
712+ is_invite : bool ,
713+ ) -> Decision :
714+ return Code .FORBIDDEN
715+
716+ join_mock = Mock (side_effect = user_may_join_room )
717+ self .hs .get_spam_checker ()._user_may_join_room_callbacks .append (join_mock )
718+
719+ channel = self .make_request (
720+ "POST" ,
721+ "/createRoom" ,
722+ {},
723+ )
724+ self .assertEqual (channel .code , 200 , channel .json_body )
725+
726+ self .assertEqual (join_mock .call_count , 0 )
703727
704728class RoomTopicTestCase (RoomBase ):
705729 """Tests /rooms/$room_id/topic REST events."""
@@ -910,9 +934,9 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
910934 self .room2 = self .helper .create_room_as (room_creator = self .user1 , tok = self .tok1 )
911935 self .room3 = self .helper .create_room_as (room_creator = self .user1 , tok = self .tok1 )
912936
913- def test_spam_checker_may_join_room (self ) -> None :
937+ def test_spam_checker_may_join_room_old (self ) -> None :
914938 """Tests that the user_may_join_room spam checker callback is correctly called
915- and blocks room joins when needed.
939+ and blocks room joins when needed (old-style API, return a boolean) .
916940 """
917941
918942 # Register a dummy callback. Make it allow all room joins for now.
@@ -967,6 +991,63 @@ async def user_may_join_room(
967991 return_value = False
968992 self .helper .join (self .room3 , self .user2 , expect_code = 403 , tok = self .tok2 )
969993
994+ def test_spam_checker_may_join_room (self ) -> None :
995+ """Tests that the user_may_join_room spam checker callback is correctly called
996+ and blocks room joins when needed.
997+ """
998+
999+ # Register a dummy callback. Make it allow all room joins for now.
1000+ return_value = ALLOW
1001+
1002+ async def user_may_join_room (
1003+ userid : str ,
1004+ room_id : str ,
1005+ is_invited : bool ,
1006+ ) -> Decision :
1007+ return return_value
1008+
1009+ callback_mock = Mock (side_effect = user_may_join_room )
1010+ self .hs .get_spam_checker ()._user_may_join_room_callbacks .append (callback_mock )
1011+
1012+ # Join a first room, without being invited to it.
1013+ self .helper .join (self .room1 , self .user2 , tok = self .tok2 )
1014+
1015+ # Check that the callback was called with the right arguments.
1016+ expected_call_args = (
1017+ (
1018+ self .user2 ,
1019+ self .room1 ,
1020+ False ,
1021+ ),
1022+ )
1023+ self .assertEqual (
1024+ callback_mock .call_args ,
1025+ expected_call_args ,
1026+ callback_mock .call_args ,
1027+ )
1028+
1029+ # Join a second room, this time with an invite for it.
1030+ self .helper .invite (self .room2 , self .user1 , self .user2 , tok = self .tok1 )
1031+ self .helper .join (self .room2 , self .user2 , tok = self .tok2 )
1032+
1033+ # Check that the callback was called with the right arguments.
1034+ expected_call_args = (
1035+ (
1036+ self .user2 ,
1037+ self .room2 ,
1038+ True ,
1039+ ),
1040+ )
1041+ self .assertEqual (
1042+ callback_mock .call_args ,
1043+ expected_call_args ,
1044+ callback_mock .call_args ,
1045+ )
1046+
1047+ # Now make the callback deny all room joins, and check that a join actually fails.
1048+ return_value = Code .FORBIDDEN
1049+ self .helper .join (self .room3 , self .user2 , expect_code = 403 , tok = self .tok2 )
1050+
9701051
9711052class RoomJoinRatelimitTestCase (RoomBase ):
9721053 user_id = "@sid1:red"
@@ -2586,7 +2667,7 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
25862667
25872668 self .room_id = self .helper .create_room_as (self .user_id , tok = self .tok )
25882669
2589- def test_threepid_invite_spamcheck (self ) -> None :
2670+ def test_threepid_invite_spamcheck_old (self ) -> None :
25902671 # Mock a few functions to prevent the test from failing due to failing to talk to
25912672 # a remote IS. We keep the mock for _mock_make_and_store_3pid_invite around so we
25922673 # can check its call_count later on during the test.
@@ -2640,3 +2721,58 @@ def test_threepid_invite_spamcheck(self) -> None:
26402721
26412722 # Also check that it stopped before calling _make_and_store_3pid_invite.
26422723 make_invite_mock .assert_called_once ()
2724+
2725+ def test_threepid_invite_spamcheck (self ) -> None :
2726+ # Mock a few functions to prevent the test from failing due to failing to talk to
2727+ # a remote IS. We keep the mock for _mock_make_and_store_3pid_invite around so we
2728+ # can check its call_count later on during the test.
2729+ make_invite_mock = Mock (return_value = make_awaitable (0 ))
2730+ self .hs .get_room_member_handler ()._make_and_store_3pid_invite = make_invite_mock
2731+ self .hs .get_identity_handler ().lookup_3pid = Mock (
2732+ return_value = make_awaitable (None ),
2733+ )
2734+
2735+ # Add a mock to the spamchecker callbacks for user_may_send_3pid_invite. Make it
2736+ # allow everything for now.
2737+ mock = Mock (return_value = make_awaitable (ALLOW ))
2738+ self .hs .get_spam_checker ()._user_may_send_3pid_invite_callbacks .append (mock )
2739+
2740+ # Send a 3PID invite into the room and check that it succeeded.
2741+ email_to_invite = "teresa@example.com"
2742+ channel = self .make_request (
2743+ method = "POST" ,
2744+ path = "/rooms/" + self .room_id + "/invite" ,
2745+ content = {
2746+ "id_server" : "example.com" ,
2747+ "id_access_token" : "sometoken" ,
2748+ "medium" : "email" ,
2749+ "address" : email_to_invite ,
2750+ },
2751+ access_token = self .tok ,
2752+ )
2753+ self .assertEqual (channel .code , 200 )
2754+
2755+ # Check that the callback was called with the right params.
2756+ mock .assert_called_with (self .user_id , "email" , email_to_invite , self .room_id )
2757+
2758+ # Check that the call to send the invite was made.
2759+ make_invite_mock .assert_called_once ()
2760+
2761+ # Now change the return value of the callback to deny any invite and test that
2762+ # we can't send the invite.
2763+ mock .return_value = make_awaitable (Code .FORBIDDEN )
2764+ channel = self .make_request (
2765+ method = "POST" ,
2766+ path = "/rooms/" + self .room_id + "/invite" ,
2767+ content = {
2768+ "id_server" : "example.com" ,
2769+ "id_access_token" : "sometoken" ,
2770+ "medium" : "email" ,
2771+ "address" : email_to_invite ,
2772+ },
2773+ access_token = self .tok ,
2774+ )
2775+ self .assertEqual (channel .code , 403 )
2776+
2777+ # Also check that it stopped before calling _make_and_store_3pid_invite.
2778+ make_invite_mock .assert_called_once ()
0 commit comments