From 4b62d4e914d8ff7e21bcfbbc6572f1f2a363e066 Mon Sep 17 00:00:00 2001 From: Rory& Date: Fri, 25 Jul 2025 08:26:15 +0200 Subject: [PATCH 13/14] RequestRatelimiter: expose can_do_action --- synapse/api/ratelimiting.py | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/synapse/api/ratelimiting.py b/synapse/api/ratelimiting.py index 509ef6b2c1..5f22089a6b 100644 --- a/synapse/api/ratelimiting.py +++ b/synapse/api/ratelimiting.py @@ -435,3 +435,78 @@ class RequestRatelimiter: update=update, n_actions=n_actions, ) + + async def can_do_action( + self, + requester: Optional[Requester], + burst_count: Optional[int] = None, + update: bool = True, + is_admin_redaction: bool = False, + n_actions: int = 1, + ) -> Tuple[bool, float]: + """Can the entity (e.g. user or IP address) perform the action? + + Checks if the user has ratelimiting disabled in the database by looking + for null/zero values in the `ratelimit_override` table. (Non-zero + values aren't honoured, as they're specific to the event sending + ratelimiter, rather than all ratelimiters) + + Args: + requester: The requester that is doing the action, if any. Used to check + if the user has ratelimits disabled in the database. + key: An arbitrary key used to classify an action. Defaults to the + requester's user ID. + 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. If the action + cannot be performed, the user's action count is not incremented at all. + n_actions: The number of times the user wants to do this action. If the user + cannot do all of the actions, the user's action count is not incremented + at all. + _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 + """ + user_id = requester.user.to_string() + + # The AS user itself is never rate limited. + app_service = self.store.get_app_service_by_user_id(user_id) + if app_service is not None: + return True, 0 # do not ratelimit app service senders + + messages_per_second = self._rc_message.per_second + burst_count = self._rc_message.burst_count + + # Check if there is a per user override in the DB. + override = await self.store.get_ratelimit_for_user(user_id) + if override: + # If overridden with a null Hz then ratelimiting has been entirely + # disabled for the user + if not override.messages_per_second: + return True, 0 + + messages_per_second = override.messages_per_second + burst_count = override.burst_count + + if is_admin_redaction and self.admin_redaction_ratelimiter: + # If we have separate config for admin redactions, use a separate + # ratelimiter as to not have user_ids clash + return await self.admin_redaction_ratelimiter.can_do_action( + requester, update=update, n_actions=n_actions + ) + else: + # Override rate and burst count per-user + return await self.request_ratelimiter.can_do_action( + requester, + rate_hz=messages_per_second, + burst_count=burst_count, + update=update, + n_actions=n_actions, + ) -- 2.49.0