Skip to content
Snippets Groups Projects
Unverified Commit cbbf9126 authored by Will Hunt's avatar Will Hunt Committed by GitHub
Browse files

Do not apply ratelimiting on joins to appservices (#8139)


Add new method ratelimiter.can_requester_do_action and ensure that appservices are exempt from being ratelimited.

Co-authored-by: default avatarPatrick Cloke <clokep@users.noreply.github.com>
Co-authored-by: default avatarErik Johnston <erik@matrix.org>
parent 09fd0eda
No related branches found
No related tags found
No related merge requests found
Fixes a bug where appservices with ratelimiting disabled would still be ratelimited when joining rooms. This bug was introduced in v1.19.0.
...@@ -17,6 +17,7 @@ from collections import OrderedDict ...@@ -17,6 +17,7 @@ from collections import OrderedDict
from typing import Any, Optional, Tuple from typing import Any, Optional, Tuple
from synapse.api.errors import LimitExceededError from synapse.api.errors import LimitExceededError
from synapse.types import Requester
from synapse.util import Clock from synapse.util import Clock
...@@ -43,6 +44,42 @@ class Ratelimiter(object): ...@@ -43,6 +44,42 @@ class Ratelimiter(object):
# * The rate_hz of this particular entry. This can vary per request # * The rate_hz of this particular entry. This can vary per request
self.actions = OrderedDict() # type: OrderedDict[Any, Tuple[float, int, float]] self.actions = OrderedDict() # type: OrderedDict[Any, Tuple[float, int, float]]
def can_requester_do_action(
self,
requester: Requester,
rate_hz: Optional[float] = None,
burst_count: Optional[int] = None,
update: bool = True,
_time_now_s: Optional[int] = None,
) -> Tuple[bool, float]:
"""Can the requester perform the action?
Args:
requester: The requester to key off when rate limiting. The user property
will be used.
rate_hz: The long term number of actions that can be performed in a second.
Overrides the value set during instantiation if set.
burst_count: How many actions that can be performed before being limited.
Overrides the value set during instantiation if set.
update: Whether to count this check as performing the action
_time_now_s: The current time. Optional, defaults to the current time according
to self.clock. Only used by tests.
Returns:
A tuple containing:
* A bool indicating if they can perform the action now
* The reactor timestamp for when the action can be performed next.
-1 if rate_hz is less than or equal to zero
"""
# Disable rate limiting of users belonging to any AS that is configured
# not to be rate limited in its registration file (rate_limited: true|false).
if requester.app_service and not requester.app_service.is_rate_limited():
return True, -1.0
return self.can_do_action(
requester.user.to_string(), rate_hz, burst_count, update, _time_now_s
)
def can_do_action( def can_do_action(
self, self,
key: Any, key: Any,
......
...@@ -491,9 +491,10 @@ class RoomMemberHandler(object): ...@@ -491,9 +491,10 @@ class RoomMemberHandler(object):
if is_host_in_room: if is_host_in_room:
time_now_s = self.clock.time() time_now_s = self.clock.time()
allowed, time_allowed = self._join_rate_limiter_local.can_do_action( (
requester.user.to_string(), allowed,
) time_allowed,
) = self._join_rate_limiter_local.can_requester_do_action(requester,)
if not allowed: if not allowed:
raise LimitExceededError( raise LimitExceededError(
...@@ -502,9 +503,10 @@ class RoomMemberHandler(object): ...@@ -502,9 +503,10 @@ class RoomMemberHandler(object):
else: else:
time_now_s = self.clock.time() time_now_s = self.clock.time()
allowed, time_allowed = self._join_rate_limiter_remote.can_do_action( (
requester.user.to_string(), allowed,
) time_allowed,
) = self._join_rate_limiter_remote.can_requester_do_action(requester,)
if not allowed: if not allowed:
raise LimitExceededError( raise LimitExceededError(
......
from synapse.api.ratelimiting import LimitExceededError, Ratelimiter from synapse.api.ratelimiting import LimitExceededError, Ratelimiter
from synapse.appservice import ApplicationService
from synapse.types import create_requester
from tests import unittest from tests import unittest
...@@ -18,6 +20,77 @@ class TestRatelimiter(unittest.TestCase): ...@@ -18,6 +20,77 @@ class TestRatelimiter(unittest.TestCase):
self.assertTrue(allowed) self.assertTrue(allowed)
self.assertEquals(20.0, time_allowed) self.assertEquals(20.0, time_allowed)
def test_allowed_user_via_can_requester_do_action(self):
user_requester = create_requester("@user:example.com")
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
allowed, time_allowed = limiter.can_requester_do_action(
user_requester, _time_now_s=0
)
self.assertTrue(allowed)
self.assertEquals(10.0, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
user_requester, _time_now_s=5
)
self.assertFalse(allowed)
self.assertEquals(10.0, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
user_requester, _time_now_s=10
)
self.assertTrue(allowed)
self.assertEquals(20.0, time_allowed)
def test_allowed_appservice_ratelimited_via_can_requester_do_action(self):
appservice = ApplicationService(
None, "example.com", id="foo", rate_limited=True,
)
as_requester = create_requester("@user:example.com", app_service=appservice)
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=0
)
self.assertTrue(allowed)
self.assertEquals(10.0, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=5
)
self.assertFalse(allowed)
self.assertEquals(10.0, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=10
)
self.assertTrue(allowed)
self.assertEquals(20.0, time_allowed)
def test_allowed_appservice_via_can_requester_do_action(self):
appservice = ApplicationService(
None, "example.com", id="foo", rate_limited=False,
)
as_requester = create_requester("@user:example.com", app_service=appservice)
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=0
)
self.assertTrue(allowed)
self.assertEquals(-1, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=5
)
self.assertTrue(allowed)
self.assertEquals(-1, time_allowed)
allowed, time_allowed = limiter.can_requester_do_action(
as_requester, _time_now_s=10
)
self.assertTrue(allowed)
self.assertEquals(-1, time_allowed)
def test_allowed_via_ratelimit(self): def test_allowed_via_ratelimit(self):
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1) limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment