Harden auth and security controls with session auth and docs
This commit is contained in:
42
backend/app/services/rate_limiter.py
Normal file
42
backend/app/services/rate_limiter.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""Redis-backed fixed-window rate limiter helpers for sensitive API operations."""
|
||||
|
||||
import time
|
||||
|
||||
from redis.exceptions import RedisError
|
||||
|
||||
from app.worker.queue import get_redis
|
||||
|
||||
|
||||
def _rate_limit_key(*, scope: str, subject: str, window_id: int) -> str:
|
||||
"""Builds a stable Redis key for one scope, subject, and fixed time window."""
|
||||
|
||||
return f"dcm:rate-limit:{scope}:{subject}:{window_id}"
|
||||
|
||||
|
||||
def increment_rate_limit(
|
||||
*,
|
||||
scope: str,
|
||||
subject: str,
|
||||
limit: int,
|
||||
window_seconds: int = 60,
|
||||
) -> tuple[int, int]:
|
||||
"""Increments one rate bucket and returns current count with configured limit."""
|
||||
|
||||
bounded_limit = max(0, int(limit))
|
||||
if bounded_limit == 0:
|
||||
return (0, 0)
|
||||
|
||||
bounded_window = max(1, int(window_seconds))
|
||||
current_window = int(time.time() // bounded_window)
|
||||
key = _rate_limit_key(scope=scope, subject=subject, window_id=current_window)
|
||||
|
||||
redis_client = get_redis()
|
||||
try:
|
||||
pipeline = redis_client.pipeline(transaction=True)
|
||||
pipeline.incr(key, 1)
|
||||
pipeline.expire(key, bounded_window + 5)
|
||||
count_value, _ = pipeline.execute()
|
||||
except RedisError as error:
|
||||
raise RuntimeError("Rate limiter backend unavailable") from error
|
||||
|
||||
return (int(count_value), bounded_limit)
|
||||
Reference in New Issue
Block a user