From 6e64c62fbfe99adfdcf39059bd1a1762afca01fd Mon Sep 17 00:00:00 2001 From: Pierre Lalet Date: Wed, 15 Nov 2023 21:26:33 +0100 Subject: [PATCH] Add new management command axes_reset_ip_username --- .../commands/axes_reset_ip_username.py | 19 +++++++++++++++++++ docs/3_usage.rst | 4 +++- tests/test_management.py | 15 +++++++++++++-- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 axes/management/commands/axes_reset_ip_username.py diff --git a/axes/management/commands/axes_reset_ip_username.py b/axes/management/commands/axes_reset_ip_username.py new file mode 100644 index 0000000..71f3f43 --- /dev/null +++ b/axes/management/commands/axes_reset_ip_username.py @@ -0,0 +1,19 @@ +from django.core.management.base import BaseCommand + +from axes.utils import reset + + +class Command(BaseCommand): + help = "Reset all access attempts and lockouts for a given IP address and username" + + def add_arguments(self, parser): + parser.add_argument("ip", type=str) + parser.add_argument("username", type=str) + + def handle(self, *args, **options): + count = reset(ip=options["ip"], username=options["username"]) + + if count: + self.stdout.write(f"{count} attempts removed.") + else: + self.stdout.write("No attempts found.") diff --git a/docs/3_usage.rst b/docs/3_usage.rst index cfd5a09..f7d0438 100644 --- a/docs/3_usage.rst +++ b/docs/3_usage.rst @@ -80,7 +80,7 @@ Resetting attempts from command line ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Axes offers a command line interface with -``axes_reset``, ``axes_reset_ip``, and ``axes_reset_username`` +``axes_reset``, ``axes_reset_ip``, ``axes_reset_username``, and ``axes_reset_ip_username`` management commands with the Django ``manage.py`` or ``django-admin`` command helpers: - ``python manage.py axes_reset`` @@ -89,6 +89,8 @@ management commands with the Django ``manage.py`` or ``django-admin`` command he will clear lockouts and records for the given IP addresses. - ``python manage.py axes_reset_username [username ...]`` will clear lockouts and records for the given usernames. +- ``python manage.py axes_reset_ip_username [ip] [username]`` + will clear lockouts and records for the given IP address and username. - ``python manage.py axes_reset_logs (age)`` will reset (i.e. delete) AccessLog records that are older than the given age where the default is 30 days. diff --git a/tests/test_management.py b/tests/test_management.py index f48aeae..4cd07d8 100644 --- a/tests/test_management.py +++ b/tests/test_management.py @@ -56,18 +56,22 @@ class ManagementCommandTestCase(AxesTestCase): username="john.doe", ip_address="10.0.0.2", failures_since_start="15" ) + AccessAttempt.objects.create( + username="richard.doe", ip_address="10.0.0.4", failures_since_start="12" + ) + def test_axes_list_attempts(self): out = StringIO() call_command("axes_list_attempts", stdout=out) - expected = "10.0.0.1\tjane.doe\t4\n10.0.0.2\tjohn.doe\t15\n" + expected = "10.0.0.1\tjane.doe\t4\n10.0.0.2\tjohn.doe\t15\n10.0.0.4\trichard.doe\t12\n" self.assertEqual(expected, out.getvalue()) def test_axes_reset(self): out = StringIO() call_command("axes_reset", stdout=out) - expected = "2 attempts removed.\n" + expected = "3 attempts removed.\n" self.assertEqual(expected, out.getvalue()) def test_axes_reset_not_found(self): @@ -87,6 +91,13 @@ class ManagementCommandTestCase(AxesTestCase): expected = "1 attempts removed.\n" self.assertEqual(expected, out.getvalue()) + def test_axes_reset_ip_username(self): + out = StringIO() + call_command("axes_reset_ip_username", "10.0.0.4", "richard.doe", stdout=out) + + expected = "1 attempts removed.\n" + self.assertEqual(expected, out.getvalue()) + def test_axes_reset_ip_not_found(self): out = StringIO() call_command("axes_reset_ip", "10.0.0.3", stdout=out)