Beispiel #1
0
    def test_find_account_rate_limiting(self) -> None:
        def assert_func(result: HttpResponse) -> None:
            self.assertEqual(result.status_code, 429)
            self.assert_in_response("Rate limit exceeded.", result)

        with rate_limit_rule(1, 5, domain="find_account_by_ip"):
            RateLimitedIPAddr("127.0.0.1",
                              domain="find_account_by_ip").clear_history()
            self.do_test_hit_ratelimits(
                lambda: self.client_post("/accounts/find/",
                                         {"emails": "*****@*****.**"}),
                assert_func=assert_func,
            )

        # Now test whether submitting multiple emails is handled correctly.
        # The limit is set to 10 per second, so 5 requests with 2 emails
        # submitted in each should be allowed.
        with rate_limit_rule(1, 10, domain="find_account_by_ip"):
            RateLimitedIPAddr("127.0.0.1",
                              domain="find_account_by_ip").clear_history()
            self.do_test_hit_ratelimits(
                lambda: self.
                client_post("/accounts/find/",
                            {"emails": "[email protected],[email protected]"}),
                assert_func=assert_func,
            )
Beispiel #2
0
    def test_used_in_tornado(self) -> None:
        user_profile = self.example_user("hamlet")
        ip_addr = "192.168.0.123"
        with self.settings(RUNNING_INSIDE_TORNADO=True):
            user_obj = RateLimitedUser(user_profile, domain="api_by_user")
            ip_obj = RateLimitedIPAddr(ip_addr, domain="api_by_ip")
        self.assertEqual(user_obj.backend, TornadoInMemoryRateLimiterBackend)
        self.assertEqual(ip_obj.backend, TornadoInMemoryRateLimiterBackend)

        with self.settings(RUNNING_INSIDE_TORNADO=True):
            user_obj = RateLimitedUser(user_profile, domain="some_domain")
            ip_obj = RateLimitedIPAddr(ip_addr, domain="some_domain")
        self.assertEqual(user_obj.backend, RedisRateLimiterBackend)
        self.assertEqual(ip_obj.backend, RedisRateLimiterBackend)
Beispiel #3
0
    def test_tor_file_not_found(self) -> None:
        for ip in ["1.2.3.4", "5.6.7.8", "tor-exit-node"]:
            RateLimitedIPAddr(ip, domain="api_by_ip").clear_history()

        with self.tor_mock(
                side_effect=FileNotFoundError("File not found")) as tor_open:
            # If we cannot get a list of TOR exit nodes, then
            # rate-limiting works as normal, per-IP
            with self.assertLogs("zerver.lib.rate_limiter",
                                 level="WARNING") as log_mock:
                self.do_test_hit_ratelimits(
                    lambda: self.send_unauthed_api_request(REMOTE_ADDR=
                                                           "1.2.3.4"))
                resp = self.send_unauthed_api_request(REMOTE_ADDR="5.6.7.8")
                self.assertNotEqual(resp.status_code, 429)

        # Tries twice before hitting the circuit-breaker, and stopping trying
        tor_open.assert_has_calls([
            mock.call(settings.TOR_EXIT_NODE_FILE_PATH, "rb"),
            mock.call(settings.TOR_EXIT_NODE_FILE_PATH, "rb"),
        ])

        self.assert_length(log_mock.output, 8)
        self.assertEqual(
            log_mock.output[0:2],
            [
                "WARNING:zerver.lib.rate_limiter:Failed to fetch TOR exit node list: {}"
                .format("File not found")
            ] * 2,
        )
        self.assertIn(
            'Failed to fetch TOR exit node list: Circuit "get_tor_ips" OPEN',
            log_mock.output[3],
        )
Beispiel #4
0
def rate_limit_rule(range_seconds: int, num_requests: int, domain: str) -> Iterator[None]:
    RateLimitedIPAddr("127.0.0.1", domain=domain).clear_history()
    add_ratelimit_rule(range_seconds, num_requests, domain=domain)
    try:
        yield
    finally:
        # We need this in a finally block to ensure the test cleans up after itself
        # even in case of failure, to avoid polluting the rules state.
        remove_ratelimit_rule(range_seconds, num_requests, domain=domain)
Beispiel #5
0
 def test_hit_ratelimits_as_ip(self) -> None:
     add_ratelimit_rule(1, 5, domain="api_by_ip")
     try:
         RateLimitedIPAddr("127.0.0.1").clear_history()
         self.do_test_hit_ratelimits(self.send_unauthed_api_request)
     finally:
         # We need this in a finally block to ensure the test cleans up after itself
         # even in case of failure, to avoid polluting the rules state.
         remove_ratelimit_rule(1, 5, domain="api_by_ip")
Beispiel #6
0
    def test_create_realm_rate_limiting(self) -> None:
        def assert_func(result: HttpResponse) -> None:
            self.assertEqual(result.status_code, 429)
            self.assert_in_response("Rate limit exceeded.", result)

        with self.settings(OPEN_REALM_CREATION=True):
            RateLimitedIPAddr("127.0.0.1",
                              domain="create_realm_by_ip").clear_history()
            self.do_test_hit_ratelimits(
                lambda: self.client_post("/new/", {"email": "*****@*****.**"}),
                assert_func=assert_func,
            )
Beispiel #7
0
    def test_tor_file_empty(self) -> None:
        for ip in ["1.2.3.4", "5.6.7.8", "tor-exit-node"]:
            RateLimitedIPAddr(ip, domain="api_by_ip").clear_history()

        # An empty list of IPs is treated as some error in parsing the
        # input, and as such should not be cached; rate-limiting
        # should work as normal, per-IP
        with self.tor_mock(read_data=[]) as tor_open:
            with self.assertLogs("zerver.lib.rate_limiter", level="WARNING"):
                self.do_test_hit_ratelimits(
                    lambda: self.send_unauthed_api_request(REMOTE_ADDR="1.2.3.4")
                )
                resp = self.send_unauthed_api_request(REMOTE_ADDR="5.6.7.8")
                self.assertNotEqual(resp.status_code, 429)

        # Was not cached, so tried to read twice before hitting the
        # circuit-breaker, and stopping trying
        tor_open().read.assert_has_calls([mock.call(), mock.call()])
Beispiel #8
0
    def test_tor_ip_limits(self) -> None:
        request_count = 0
        for ip in ["1.2.3.4", "5.6.7.8", "tor-exit-node"]:
            RateLimitedIPAddr(ip, domain="api_by_ip").clear_history()

        def alternate_requests() -> HttpResponse:
            nonlocal request_count
            request_count += 1
            if request_count % 2 == 1:
                return self.send_unauthed_api_request(REMOTE_ADDR="1.2.3.4")
            else:
                return self.send_unauthed_api_request(REMOTE_ADDR="5.6.7.8")

        with self.tor_mock(read_data=["1.2.3.4", "5.6.7.8"]) as tor_open:
            self.do_test_hit_ratelimits(alternate_requests)

        # This is only read once, despite being used on each request
        tor_open.assert_called_once_with(settings.TOR_EXIT_NODE_FILE_PATH, "rb")
        tor_open().read.assert_called_once()
Beispiel #9
0
def rate_limit_ip(request: HttpRequest, ip_addr: str, domain: str) -> None:
    RateLimitedIPAddr(ip_addr, domain=domain).rate_limit_request(request)
Beispiel #10
0
 def test_hit_ratelimits_as_ip(self) -> None:
     RateLimitedIPAddr("127.0.0.1").clear_history()
     self.do_test_hit_ratelimits(self.send_unauthed_api_request)