diff --git a/README.rst b/README.rst index 724c16f..8c37f06 100644 --- a/README.rst +++ b/README.rst @@ -144,7 +144,7 @@ By default, django-axes will lock out repeated attempts from the same IP address. You can allow this IP to attempt again by deleting the relevant ``AccessAttempt`` records in the admin. -You can also use the ``axes_reset`` management command (since 1.2.5-rc1). Using Django's +You can also use the ``axes_reset`` management command using Django's ``manage.py``. * ``manage.py axes_reset`` will reset all lockouts and access records. @@ -153,7 +153,5 @@ You can also use the ``axes_reset`` management command (since 1.2.5-rc1). Using In your code, you can use ``from axes.utils import reset``. * ``reset()`` will reset all lockouts and access records. -* ``reset(ip)`` will clear lockout/records for ip - -``reset`` will print a message to std out if there is nothing to reset, -unless called with ``silent = True`` +* ``reset(ip=ip)`` will clear lockout/records for ip +* ``reset(username=username)`` will clear lockout/records for username diff --git a/axes/management/commands/axes_reset.py b/axes/management/commands/axes_reset.py index 27fe9ca..246394c 100644 --- a/axes/management/commands/axes_reset.py +++ b/axes/management/commands/axes_reset.py @@ -1,4 +1,6 @@ -from django.core.management.base import BaseCommand, CommandError +from django.core.management.base import BaseCommand +from django.core.management.base import CommandError + from axes.utils import reset @@ -8,8 +10,14 @@ class Command(BaseCommand): "IP, resets only for that IP") def handle(self, *args, **kwargs): + count = 0 if args: for ip in args: - reset(ip) + count += reset(ip=ip) else: - reset() + count = reset() + + if count: + print '{0} attempts removed.'.format(count) + else: + print 'No attempts found.' diff --git a/axes/tests.py b/axes/tests.py index fe5aade..fcd7f90 100644 --- a/axes/tests.py +++ b/axes/tests.py @@ -9,6 +9,7 @@ from django.test.utils import override_settings from axes.decorators import FAILURE_LIMIT from axes.decorators import LOGIN_FORM_KEY from axes.models import AccessLog +from axes.utils import reset class AccessAttemptTest(TestCase): @@ -151,3 +152,27 @@ class AccessAttemptTest(TestCase): response = self._login() self.assertIn(self.LOCKED_MESSAGE, response.content) + + def test_reset_ip(self): + """Tests if can reset an ip address + """ + # Make a lockout + self.test_with_real_username_max() + + # Reset the ip so we can try again + reset(ip='127.0.0.1') + + # Make a login attempt again + self.test_with_real_username_max() + + def test_reset_all(self): + """Tests if can reset all attempts + """ + # Make a lockout + self.test_with_real_username_max() + + # Reset all attempts so we can try again + reset() + + # Make a login attempt again + self.test_with_real_username_max() diff --git a/axes/utils.py b/axes/utils.py index be39a1c..0ea87ea 100644 --- a/axes/utils.py +++ b/axes/utils.py @@ -1,25 +1,20 @@ from axes.models import AccessAttempt -def reset(ip=None, username=None, silent=False): - # no need to reset trusted records. If they fail, they go to untrusted - params = { - 'trusted': False, - } +def reset(ip=None, username=None): + """Reset records that match ip or username, and + return the count of removed attempts. + """ + count = 0 + attempts = AccessAttempt.objects.all() if ip: - params['ip_address'] = ip - - attempts = AccessAttempt.objects.filter(**params) + attempts = attempts.filter(ip_address=ip) if username: - if 'ip_address' in params: - del params['ip_address'] - - params['username'] = username - attempts |= AccessAttempt.objects.filter(**params) + attempts = attempts.filter(username=username) if attempts: + count = attempts.count() attempts.delete() - else: - if not silent: - print 'No attempts found.' + + return count