Merge pull request #6015 from matrix-org/erikj/ratelimit_admin_redaction
Allow use of different ratelimits for admin redactions.pull/6020/head
commit
cbcbfe64a2
|
@ -0,0 +1 @@
|
|||
Add config option to increase ratelimits for room admins redacting messages.
|
|
@ -518,6 +518,9 @@ 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 for ratelimiting redactions by room admins. If this is not explicitly
|
||||
# set then it uses the same ratelimiting as per rc_message. This is useful
|
||||
# to allow room admins to deal with abuse quickly.
|
||||
#
|
||||
# The defaults are as shown below.
|
||||
#
|
||||
|
@ -539,6 +542,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
|||
# failed_attempts:
|
||||
# per_second: 0.17
|
||||
# burst_count: 3
|
||||
#
|
||||
#rc_admin_redaction:
|
||||
# per_second: 1
|
||||
# burst_count: 50
|
||||
|
||||
|
||||
# Ratelimiting settings for incoming federation
|
||||
|
|
|
@ -80,6 +80,12 @@ class RatelimitConfig(Config):
|
|||
"federation_rr_transactions_per_room_per_second", 50
|
||||
)
|
||||
|
||||
rc_admin_redaction = config.get("rc_admin_redaction")
|
||||
if rc_admin_redaction:
|
||||
self.rc_admin_redaction = RateLimitConfig(rc_admin_redaction)
|
||||
else:
|
||||
self.rc_admin_redaction = None
|
||||
|
||||
def generate_config_section(self, **kwargs):
|
||||
return """\
|
||||
## Ratelimiting ##
|
||||
|
@ -102,6 +108,9 @@ class RatelimitConfig(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 for ratelimiting redactions by room admins. If this is not explicitly
|
||||
# set then it uses the same ratelimiting as per rc_message. This is useful
|
||||
# to allow room admins to deal with abuse quickly.
|
||||
#
|
||||
# The defaults are as shown below.
|
||||
#
|
||||
|
@ -123,6 +132,10 @@ class RatelimitConfig(Config):
|
|||
# failed_attempts:
|
||||
# per_second: 0.17
|
||||
# burst_count: 3
|
||||
#
|
||||
#rc_admin_redaction:
|
||||
# per_second: 1
|
||||
# burst_count: 50
|
||||
|
||||
|
||||
# Ratelimiting settings for incoming federation
|
||||
|
|
|
@ -45,6 +45,7 @@ class BaseHandler(object):
|
|||
self.state_handler = hs.get_state_handler()
|
||||
self.distributor = hs.get_distributor()
|
||||
self.ratelimiter = hs.get_ratelimiter()
|
||||
self.admin_redaction_ratelimiter = hs.get_admin_redaction_ratelimiter()
|
||||
self.clock = hs.get_clock()
|
||||
self.hs = hs
|
||||
|
||||
|
@ -53,7 +54,7 @@ class BaseHandler(object):
|
|||
self.event_builder_factory = hs.get_event_builder_factory()
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def ratelimit(self, requester, update=True):
|
||||
def ratelimit(self, requester, update=True, is_admin_redaction=False):
|
||||
"""Ratelimits requests.
|
||||
|
||||
Args:
|
||||
|
@ -62,6 +63,9 @@ class BaseHandler(object):
|
|||
Set to False when doing multiple checks for one request (e.g.
|
||||
to check up front if we would reject the request), and set to
|
||||
True for the last call for a given request.
|
||||
is_admin_redaction (bool): Whether this is a room admin/moderator
|
||||
redacting an event. If so then we may apply different
|
||||
ratelimits depending on config.
|
||||
|
||||
Raises:
|
||||
LimitExceededError if the request should be ratelimited
|
||||
|
@ -90,16 +94,33 @@ class BaseHandler(object):
|
|||
messages_per_second = override.messages_per_second
|
||||
burst_count = override.burst_count
|
||||
else:
|
||||
messages_per_second = self.hs.config.rc_message.per_second
|
||||
burst_count = self.hs.config.rc_message.burst_count
|
||||
# We default to different values if this is an admin redaction and
|
||||
# the config is set
|
||||
if is_admin_redaction and self.hs.config.rc_admin_redaction:
|
||||
messages_per_second = self.hs.config.rc_admin_redaction.per_second
|
||||
burst_count = self.hs.config.rc_admin_redaction.burst_count
|
||||
else:
|
||||
messages_per_second = self.hs.config.rc_message.per_second
|
||||
burst_count = self.hs.config.rc_message.burst_count
|
||||
|
||||
allowed, time_allowed = self.ratelimiter.can_do_action(
|
||||
user_id,
|
||||
time_now,
|
||||
rate_hz=messages_per_second,
|
||||
burst_count=burst_count,
|
||||
update=update,
|
||||
)
|
||||
if is_admin_redaction and self.hs.config.rc_admin_redaction:
|
||||
# If we have separate config for admin redactions we use a separate
|
||||
# ratelimiter
|
||||
allowed, time_allowed = self.admin_redaction_ratelimiter.can_do_action(
|
||||
user_id,
|
||||
time_now,
|
||||
rate_hz=messages_per_second,
|
||||
burst_count=burst_count,
|
||||
update=update,
|
||||
)
|
||||
else:
|
||||
allowed, time_allowed = self.ratelimiter.can_do_action(
|
||||
user_id,
|
||||
time_now,
|
||||
rate_hz=messages_per_second,
|
||||
burst_count=burst_count,
|
||||
update=update,
|
||||
)
|
||||
if not allowed:
|
||||
raise LimitExceededError(
|
||||
retry_after_ms=int(1000 * (time_allowed - time_now))
|
||||
|
|
|
@ -729,7 +729,27 @@ class EventCreationHandler(object):
|
|||
assert not self.config.worker_app
|
||||
|
||||
if ratelimit:
|
||||
yield self.base_handler.ratelimit(requester)
|
||||
# We check if this is a room admin redacting an event so that we
|
||||
# can apply different ratelimiting. We do this by simply checking
|
||||
# it's not a self-redaction (to avoid having to look up whether the
|
||||
# user is actually admin or not).
|
||||
is_admin_redaction = False
|
||||
if event.type == EventTypes.Redaction:
|
||||
original_event = yield self.store.get_event(
|
||||
event.redacts,
|
||||
check_redacted=False,
|
||||
get_prev_content=False,
|
||||
allow_rejected=False,
|
||||
allow_none=True,
|
||||
)
|
||||
|
||||
is_admin_redaction = (
|
||||
original_event and event.sender != original_event.sender
|
||||
)
|
||||
|
||||
yield self.base_handler.ratelimit(
|
||||
requester, is_admin_redaction=is_admin_redaction
|
||||
)
|
||||
|
||||
yield self.base_handler.maybe_kick_guest_users(event, context)
|
||||
|
||||
|
|
|
@ -221,6 +221,7 @@ class HomeServer(object):
|
|||
self.clock = Clock(reactor)
|
||||
self.distributor = Distributor()
|
||||
self.ratelimiter = Ratelimiter()
|
||||
self.admin_redaction_ratelimiter = Ratelimiter()
|
||||
self.registration_ratelimiter = Ratelimiter()
|
||||
|
||||
self.datastore = None
|
||||
|
@ -279,6 +280,9 @@ class HomeServer(object):
|
|||
def get_registration_ratelimiter(self):
|
||||
return self.registration_ratelimiter
|
||||
|
||||
def get_admin_redaction_ratelimiter(self):
|
||||
return self.admin_redaction_ratelimiter
|
||||
|
||||
def build_federation_client(self):
|
||||
return FederationClient(self)
|
||||
|
||||
|
|
|
@ -30,6 +30,14 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
sync.register_servlets,
|
||||
]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
config = self.default_config()
|
||||
|
||||
config["rc_message"] = {"per_second": 0.2, "burst_count": 10}
|
||||
config["rc_admin_redaction"] = {"per_second": 1, "burst_count": 100}
|
||||
|
||||
return self.setup_test_homeserver(config=config)
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
# register a couple of users
|
||||
self.mod_user_id = self.register_user("user1", "pass")
|
||||
|
@ -177,3 +185,20 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
self._redact_event(
|
||||
self.other_access_token, self.room_id, create_event_id, expect_code=403
|
||||
)
|
||||
|
||||
def test_redact_event_as_moderator_ratelimit(self):
|
||||
"""Tests that the correct ratelimiting is applied to redactions
|
||||
"""
|
||||
|
||||
message_ids = []
|
||||
# as a regular user, send messages to redact
|
||||
for _ in range(20):
|
||||
b = self.helper.send(room_id=self.room_id, tok=self.other_access_token)
|
||||
message_ids.append(b["event_id"])
|
||||
self.reactor.advance(10) # To get around ratelimits
|
||||
|
||||
# as the moderator, send a bunch of redactions
|
||||
for msg_id in message_ids:
|
||||
# These should all succeed, even though this would be denied by
|
||||
# the standard message ratelimiter
|
||||
self._redact_event(self.mod_access_token, self.room_id, msg_id)
|
||||
|
|
Loading…
Reference in New Issue