Skip to content

Commit

Permalink
Refactor it a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
psrok1 committed Feb 23, 2024
1 parent ae24872 commit 4dbf36d
Showing 1 changed file with 25 additions and 26 deletions.
51 changes: 25 additions & 26 deletions mwdb/core/rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@
}


def is_rate_limit_disabled():
return g.auth_user is not None and g.auth_user.has_rights(
Capabilities.unlimited_requests
)


if app_config.mwdb.enable_rate_limit:
limit_storage = RedisStorage(app_config.mwdb.redis_uri)
limiter = FixedWindowRateLimiter(limit_storage)
Expand All @@ -47,9 +41,13 @@ def apply_rate_limit_for_request() -> bool:
Limits are login-based for authenticated users.
If user is not authenticated then limit is IP-based.
"""
# If limiter is not available: rate limiting is turned off
if not limiter:
return False
if is_rate_limit_disabled():
# If rate limiting is disabled for current user: turned off
if g.auth_user is not None and g.auth_user.has_rights(
Capabilities.unlimited_requests
):
return False
# Split blueprint name and resource name from endpoint
_, resource_name = request.endpoint.split(".", 2)
Expand All @@ -60,22 +58,23 @@ def apply_rate_limit_for_request() -> bool:
for limit_key in limit_keys:
# Get limit values for key
limit_values = get_limit_from_config("_".join(limit_key))
if limit_values:
# limits has parse_many, but we're separating values using space
for limit_value in limit_values.split(" "):
limit_item = parse(limit_value)
identifiers = [user, *limit_key]
if not limiter.hit(limit_item, *identifiers):
reset_time = limiter.get_window_stats(
limit_item, *identifiers
).reset_time
# Limits' reset_time uses Redis TTL internally and
# adds time.time to it, so we can safely subtract
# the same value. There still should be a cutoff to
# make clients wait at least few seconds.
retry_after = max(5, reset_time - int(time.time()))
raise TooManyRequests(
retry_after=retry_after,
description=f"Request limit: {limit_value} for "
f"{method} method was exceeded!",
)
if not limit_values:
continue
# limits has parse_many, but we're separating values using space
for limit_value in limit_values.split(" "):
limit_item = parse(limit_value)
identifiers = [user, *limit_key]
if not limiter.hit(limit_item, *identifiers):
reset_time = limiter.get_window_stats(
limit_item, *identifiers
).reset_time
# Limits' reset_time uses Redis TTL internally and
# adds time.time to it, so we can safely subtract
# the same value. There still should be a cutoff to
# make clients wait at least few seconds.
retry_after = max(5, reset_time - int(time.time()))
raise TooManyRequests(
retry_after=retry_after,
description=f"Request limit: {limit_value} for "
f"{method} method was exceeded!",
)

0 comments on commit 4dbf36d

Please sign in to comment.