Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 2df82ae

Browse files
Half-Shotclokeperikjohnston
authored andcommitted
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: Patrick Cloke <[email protected]> Co-authored-by: Erik Johnston <[email protected]>
1 parent 3234d5c commit 2df82ae

File tree

4 files changed

+119
-6
lines changed

4 files changed

+119
-6
lines changed

changelog.d/8139.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixes a bug where appservices with ratelimiting disabled would still be ratelimited when joining rooms. This bug was introduced in v1.19.0.

synapse/api/ratelimiting.py

+37
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from typing import Any, Optional, Tuple
1818

1919
from synapse.api.errors import LimitExceededError
20+
from synapse.types import Requester
2021
from synapse.util import Clock
2122

2223

@@ -43,6 +44,42 @@ def __init__(self, clock: Clock, rate_hz: float, burst_count: int):
4344
# * The rate_hz of this particular entry. This can vary per request
4445
self.actions = OrderedDict() # type: OrderedDict[Any, Tuple[float, int, float]]
4546

47+
def can_requester_do_action(
48+
self,
49+
requester: Requester,
50+
rate_hz: Optional[float] = None,
51+
burst_count: Optional[int] = None,
52+
update: bool = True,
53+
_time_now_s: Optional[int] = None,
54+
) -> Tuple[bool, float]:
55+
"""Can the requester perform the action?
56+
57+
Args:
58+
requester: The requester to key off when rate limiting. The user property
59+
will be used.
60+
rate_hz: The long term number of actions that can be performed in a second.
61+
Overrides the value set during instantiation if set.
62+
burst_count: How many actions that can be performed before being limited.
63+
Overrides the value set during instantiation if set.
64+
update: Whether to count this check as performing the action
65+
_time_now_s: The current time. Optional, defaults to the current time according
66+
to self.clock. Only used by tests.
67+
68+
Returns:
69+
A tuple containing:
70+
* A bool indicating if they can perform the action now
71+
* The reactor timestamp for when the action can be performed next.
72+
-1 if rate_hz is less than or equal to zero
73+
"""
74+
# Disable rate limiting of users belonging to any AS that is configured
75+
# not to be rate limited in its registration file (rate_limited: true|false).
76+
if requester.app_service and not requester.app_service.is_rate_limited():
77+
return True, -1.0
78+
79+
return self.can_do_action(
80+
requester.user.to_string(), rate_hz, burst_count, update, _time_now_s
81+
)
82+
4683
def can_do_action(
4784
self,
4885
key: Any,

synapse/handlers/room_member.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,10 @@ async def _update_membership(
459459

460460
if is_host_in_room:
461461
time_now_s = self.clock.time()
462-
allowed, time_allowed = self._join_rate_limiter_local.can_do_action(
463-
requester.user.to_string(),
464-
)
462+
(
463+
allowed,
464+
time_allowed,
465+
) = self._join_rate_limiter_local.can_requester_do_action(requester,)
465466

466467
if not allowed:
467468
raise LimitExceededError(
@@ -470,9 +471,10 @@ async def _update_membership(
470471

471472
else:
472473
time_now_s = self.clock.time()
473-
allowed, time_allowed = self._join_rate_limiter_remote.can_do_action(
474-
requester.user.to_string(),
475-
)
474+
(
475+
allowed,
476+
time_allowed,
477+
) = self._join_rate_limiter_remote.can_requester_do_action(requester,)
476478

477479
if not allowed:
478480
raise LimitExceededError(

tests/api/test_ratelimiting.py

+73
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from synapse.api.ratelimiting import LimitExceededError, Ratelimiter
2+
from synapse.appservice import ApplicationService
3+
from synapse.types import create_requester
24

35
from tests import unittest
46

@@ -18,6 +20,77 @@ def test_allowed_via_can_do_action(self):
1820
self.assertTrue(allowed)
1921
self.assertEquals(20.0, time_allowed)
2022

23+
def test_allowed_user_via_can_requester_do_action(self):
24+
user_requester = create_requester("@user:example.com")
25+
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
26+
allowed, time_allowed = limiter.can_requester_do_action(
27+
user_requester, _time_now_s=0
28+
)
29+
self.assertTrue(allowed)
30+
self.assertEquals(10.0, time_allowed)
31+
32+
allowed, time_allowed = limiter.can_requester_do_action(
33+
user_requester, _time_now_s=5
34+
)
35+
self.assertFalse(allowed)
36+
self.assertEquals(10.0, time_allowed)
37+
38+
allowed, time_allowed = limiter.can_requester_do_action(
39+
user_requester, _time_now_s=10
40+
)
41+
self.assertTrue(allowed)
42+
self.assertEquals(20.0, time_allowed)
43+
44+
def test_allowed_appservice_ratelimited_via_can_requester_do_action(self):
45+
appservice = ApplicationService(
46+
None, "example.com", id="foo", rate_limited=True,
47+
)
48+
as_requester = create_requester("@user:example.com", app_service=appservice)
49+
50+
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
51+
allowed, time_allowed = limiter.can_requester_do_action(
52+
as_requester, _time_now_s=0
53+
)
54+
self.assertTrue(allowed)
55+
self.assertEquals(10.0, time_allowed)
56+
57+
allowed, time_allowed = limiter.can_requester_do_action(
58+
as_requester, _time_now_s=5
59+
)
60+
self.assertFalse(allowed)
61+
self.assertEquals(10.0, time_allowed)
62+
63+
allowed, time_allowed = limiter.can_requester_do_action(
64+
as_requester, _time_now_s=10
65+
)
66+
self.assertTrue(allowed)
67+
self.assertEquals(20.0, time_allowed)
68+
69+
def test_allowed_appservice_via_can_requester_do_action(self):
70+
appservice = ApplicationService(
71+
None, "example.com", id="foo", rate_limited=False,
72+
)
73+
as_requester = create_requester("@user:example.com", app_service=appservice)
74+
75+
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
76+
allowed, time_allowed = limiter.can_requester_do_action(
77+
as_requester, _time_now_s=0
78+
)
79+
self.assertTrue(allowed)
80+
self.assertEquals(-1, time_allowed)
81+
82+
allowed, time_allowed = limiter.can_requester_do_action(
83+
as_requester, _time_now_s=5
84+
)
85+
self.assertTrue(allowed)
86+
self.assertEquals(-1, time_allowed)
87+
88+
allowed, time_allowed = limiter.can_requester_do_action(
89+
as_requester, _time_now_s=10
90+
)
91+
self.assertTrue(allowed)
92+
self.assertEquals(-1, time_allowed)
93+
2194
def test_allowed_via_ratelimit(self):
2295
limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
2396

0 commit comments

Comments
 (0)