Skip to content

Rate limiting#968

Merged
evgenyfadeev merged 17 commits into
masterfrom
rate-limiting
May 22, 2026
Merged

Rate limiting#968
evgenyfadeev merged 17 commits into
masterfrom
rate-limiting

Conversation

@evgenyfadeev

Copy link
Copy Markdown
Member

No description provided.

sdeibel and others added 17 commits April 8, 2026 22:35
…ocity limiting

Sliding-window per-IP rate limiter with livesettings configuration:
- Configurable requests/window, cache size, optional ban command
- Uses X-Forwarded-For for correct IP detection behind reverse proxies
- ASKBOT_INTERNAL_IPS whitelist bypasses rate limiting
- Per-IP registration rate limiting on signup/register endpoints
- Content velocity limiting for watched users (opt-in, default off)

Rate-limited requests get a _ratelimited attribute for integration
with the request logging middleware.
…ponses

* Move per-IP rate-limit state from in-process dict to the Django cache
  so multi-process deployments share buckets; warn when the cache
  backend does not support shared use.
* Add registration rate limiting via view-level decorators on
  register and signup_with_password.
* Render 429 responses as HTML for browsers and JSON for AJAX/API
  clients, with a Retry-After header and a client-side banner that
  shows a humanized retry-after time.
* Add new livesettings RATE_LIMIT_IP_ALLOWLIST (runtime allowlist
  unioned with deploy-time ASKBOT_INTERNAL_IPS) and
  RATE_LIMIT_SUBNET_GRANULARITY (host/subnet/region keying).
* Honor CIDR ranges and IPv6 (incl. IPv4-mapped) in the closed-forum
  ASKBOT_INTERNAL_IPS bypass.
* Emit rate-limit events as structured WARNING log lines on the
  askbot.utils.ratelimit logger for tailer integrations.
* Add manage.py check entries that validate rate-limit settings.
* Require django-ratelimit>=4.0,<5.0 and Django 4.2 LTS minimum.

Includes documentation updates, a 429 template, helper utilities in
askbot/utils/ratelimit.py, new tests, and refreshed translation
catalogs.
Replace direct askbot_settings.update() / try-finally pairs with
livesettings_override context managers so prior values are captured
and restored automatically. The hardcoded restores in the old code
disagreed with the actual livesetting defaults
(REQUEST_RATE_LIMIT_ENABLED, REQUEST_RATE_LIMIT_MAX_REQUESTS),
leaking corrupted values into later tests.

Drop hardcoded restores from SubnetGranularityTests.tearDown and
AllowlistTests.tearDown; with_settings / livesettings_override now
handle restoration.

Guard the MISCONFIG_CHECK_DONE module flag in
RateLimitMisconfigWarningTests and RateLimitIntegrationTests with
addCleanup so it resets to False even if a test raises, preventing
leakage across class boundaries.

Drop stale "Test N" marker comments and the now-misleading
"retired test 7" / "Test 11" references in test_utils_ratelimit.py.

Refs: askbot-master-9pu
…tests

Replaces 11 ``with override_settings(...)`` / ``with livesettings_override(...)``
blocks that wrapped entire test bodies with method decorators (7
``@override_settings`` and 4 ``@with_settings``). Pure refactor — no
behavior change. Context-manager guards remain where the override
references per-test locals (e.g. tmpdir) or only covers a partial block.
…messageId]); SystemMessage also receives optional message id
…ate limit

Adds a `ratelimit_exempt` decorator and refactors
`RateLimitMiddleware.__call__` into `process_view`, so the per-request
policy short-circuits BEFORE consuming a bucket slot when the resolved
view carries the exempt mark.

Applied to:
- `read_message` (POST /messages/markread/) — dismissing a rate-limit
  banner no longer spawns a new one.
- `JavaScriptCatalog` (/jsi18n/) — a 429 there used to break i18n for
  the rest of the session.

Tests: new `askbot/tests/test_ratelimit_middleware.py` covers the
decorator contract, exempt bypass under a saturated bucket, the
exempt-doesn't-consume-slots invariant, the canonical
`@ratelimit_exempt` / `@csrf.csrf_protect` stack, non-`@wraps` inner
wrappers shadowing the attribute, registration-policy orthogonality,
and the `view_func=None` defensive case. End-to-end integration tests
for both endpoints added to `test_rate_limiting.py`.

Refs askbot-master-2c8.
…it middleware, when it is needed + test cases
… feature update; in askbot/jinja2/429.html removed link to the home page
Two new livesettings let trusted contributors skip the watched-user
post rate limit while still being watched for every other purpose:
RATE_LIMIT_BYPASS_HIGH_REP_USERS (master switch) and
MIN_REP_TO_BYPASS_RATE_LIMIT (reputation threshold). The bypass
applies only to the watched-user-post limit; the per-IP request and
registration limits are unaffected.
Add ratelimit_exempt_resolver, a URLconf-level opt-out that stamps the
exempt mark on every callback reachable through an included URLconf.
Wire it onto the livesettings include in askbot/urls.py so the admin
settings pages are never rate limited.
Move the registration rate-limit check from the view decorator into
explicit per-call-site checks inside the registration views. The
form-based signup paths now re-render with a feedback message, while
the formless one-click path still returns the standalone 429 page.
@evgenyfadeev evgenyfadeev merged commit 285eb58 into master May 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants