Skip to content

Feat: Progressive/tiered lockouts based on failure count#1402

Open
rodrigobnogueira wants to merge 4 commits intojazzband:masterfrom
rodrigobnogueira:feature/tiered-lockouts
Open

Feat: Progressive/tiered lockouts based on failure count#1402
rodrigobnogueira wants to merge 4 commits intojazzband:masterfrom
rodrigobnogueira:feature/tiered-lockouts

Conversation

@rodrigobnogueira
Copy link

What does this PR do?

This PR introduces the ability to configure progressive (tiered) lockouts in django-axes. Instead of a single fixed cool-off time, the system now supports escalating lockout durations based on the number of failed attempts.

Changes

  • Added LockoutTier dataclass and the AXES_LOCKOUT_TIERS setting to configure the tiers in axes.conf.
  • Modified core helpers (get_cool_off, get_failure_limit, get_lockout_message, get_lockout_response) in axes.helpers to resolve and apply the appropriate tier based on a request's axes_failures_since_start count.
  • Added system checks (W007, W008) in axes.checks to warn users if AXES_LOCKOUT_TIERS is misconfigured or used alongside AXES_COOLOFF_TIME.
  • Wrote comprehensive unit tests and check tests for the tiered lockout logic.
  • Added documentation for AXES_LOCKOUT_TIERS to docs/4_configuration.rst.

How it works

Users can configure AXES_LOCKOUT_TIERS as a list of LockoutTier instances. For example:

from datetime import timedelta
from axes.conf import LockoutTier

AXES_LOCKOUT_TIERS = [
    LockoutTier(failures=3,  cooloff=timedelta(minutes=15)),
    LockoutTier(failures=6,  cooloff=timedelta(hours=2)),
    LockoutTier(failures=10, cooloff=timedelta(days=1)),
]

When AXES_LOCKOUT_TIERS is defined, it overrides both AXES_FAILURE_LIMIT (which becomes the threshold of the lowest tier) and AXES_COOLOFF_TIME. Lockouts are subsequently applied dynamically according to the rules defined in the tiers.

All existing behavior remains completely unchanged if AXES_LOCKOUT_TIERS is not set.

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

@codecov
Copy link

codecov bot commented Feb 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.90%. Comparing base (c3dcd1b) to head (546cf4d).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1402      +/-   ##
==========================================
+ Coverage   91.56%   91.90%   +0.34%     
==========================================
  Files          37       37              
  Lines        1268     1322      +54     
  Branches      172      183      +11     
==========================================
+ Hits         1161     1215      +54     
  Misses         83       83              
  Partials       24       24              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aleksihakli
Copy link
Member

Hey, looks good, thanks, but this is a handful to review and a brand-new feature at that. I'll try to set some time aside later on for checking this out.

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