diff --git a/changelog.d/11.feature b/changelog.d/11.feature new file mode 100644 index 0000000000..362e4b1efd --- /dev/null +++ b/changelog.d/11.feature @@ -0,0 +1 @@ +Allow server admins to configure a custom global rate-limiting for third party invites. \ No newline at end of file diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index 63051dd56f..b4713b687e 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -568,6 +568,8 @@ log_config: "CONFDIR/SERVERNAME.log.config" # - one for login that ratelimits login requests based on the account the # client is attempting to log into, based on the amount of failed login # attempts for this account. +# - one that ratelimits third-party invites requests based on the account +# that's making the requests. # # The defaults are as shown below. # @@ -589,6 +591,10 @@ log_config: "CONFDIR/SERVERNAME.log.config" # failed_attempts: # per_second: 0.17 # burst_count: 3 +# +#rc_third_party_invite: +# per_second: 0.2 +# burst_count: 10 # Ratelimiting settings for incoming federation diff --git a/synapse/config/ratelimiting.py b/synapse/config/ratelimiting.py index 5a9adac480..2a4fe43406 100644 --- a/synapse/config/ratelimiting.py +++ b/synapse/config/ratelimiting.py @@ -68,6 +68,9 @@ def read_config(self, config): ) self.rc_registration = RateLimitConfig(config.get("rc_registration", {})) + self.rc_third_party_invite = RateLimitConfig( + config.get("rc_third_party_invite", {}) + ) rc_login_config = config.get("rc_login", {}) self.rc_login_address = RateLimitConfig(rc_login_config.get("address", {})) @@ -102,6 +105,8 @@ def default_config(self, **kwargs): # - one for login that ratelimits login requests based on the account the # client is attempting to log into, based on the amount of failed login # attempts for this account. + # - one that ratelimits third-party invites requests based on the account + # that's making the requests. # # The defaults are as shown below. # @@ -123,6 +128,10 @@ def default_config(self, **kwargs): # failed_attempts: # per_second: 0.17 # burst_count: 3 + # + #rc_third_party_invite: + # per_second: 0.2 + # burst_count: 10 # Ratelimiting settings for incoming federation diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index e940e4183b..790aeba9f5 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -26,12 +26,11 @@ import synapse.types from synapse.api.constants import EventTypes, Membership from synapse.api.errors import AuthError, Codes, ProxiedRequestError, SynapseError +from synapse.api.ratelimiting import Ratelimiter from synapse.types import RoomID, UserID from synapse.util.async_helpers import Linearizer from synapse.util.distributor import user_joined_room, user_left_room -from ._base import BaseHandler - logger = logging.getLogger(__name__) id_server_scheme = "https://" @@ -74,11 +73,7 @@ def __init__(self, hs): self.rewrite_identity_server_urls = self.config.rewrite_identity_server_urls self._enable_lookup = hs.config.enable_3pid_lookup self.allow_per_room_profiles = self.config.allow_per_room_profiles - - # This is only used to get at ratelimit function, and - # maybe_kick_guest_users. It's fine there are multiple of these as - # it doesn't store state. - self.base_handler = BaseHandler(hs) + self.ratelimiter = Ratelimiter() @abc.abstractmethod def _remote_join(self, requester, remote_room_hosts, room_id, user, content): @@ -773,7 +768,12 @@ def do_3pid_invite( # We need to rate limit *before* we send out any 3PID invites, so we # can't just rely on the standard ratelimiting of events. - yield self.base_handler.ratelimit(requester) + self.ratelimiter.ratelimit( + requester.user.to_string(), time_now_s=self.hs.clock.time(), + rate_hz=self.hs.config.rc_third_party_invite.per_second, + burst_count=self.hs.config.rc_third_party_invite.burst_count, + update=True, + ) can_invite = yield self.third_party_event_rules.check_threepid_can_be_invited( medium, address, room_id, diff --git a/tests/rest/client/test_room_access_rules.py b/tests/rest/client/test_room_access_rules.py index 7d3ba0ee2a..13caea3b01 100644 --- a/tests/rest/client/test_room_access_rules.py +++ b/tests/rest/client/test_room_access_rules.py @@ -326,6 +326,12 @@ def test_direct(self): expect_code=200, ) + # Disable the 3pid invite ratelimiter + burst = self.hs.config.rc_third_party_invite.burst_count + per_second = self.hs.config.rc_third_party_invite.per_second + self.hs.config.rc_third_party_invite.burst_count = 10 + self.hs.config.rc_third_party_invite.per_second = 0.1 + # We can't send a 3PID invite to a room that already has two members. self.send_threepid_invite( address="test@allowed_domain", @@ -354,6 +360,9 @@ def test_direct(self): expected_code=403, ) + self.hs.config.rc_third_party_invite.burst_count = burst + self.hs.config.rc_third_party_invite.per_second = per_second + def test_unrestricted(self): """Tests that, in unrestricted mode, we can invite whoever we want, but we can only change the power level of users that wouldn't be forbidden in restricted