From 9581bffd108e04f08e5ebd6f41c082f642526b15 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 10 Nov 2021 22:14:26 +0200 Subject: [PATCH 001/372] CI: Replace deprecated pypy3 with pypy-3.8 pypy3 is deprecated and is not available in newer images: https://github.com/actions/setup-python/issues/244#issuecomment-925966022 Instead explicitly specify the version: https://github.com/actions/setup-python#specifying-a-pypy-version Committed via https://github.com/asottile/all-repos --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c39a9e..aa7063d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.6', '3.7', '3.8', '3.9', 'pypy3'] + python-version: ['3.6', '3.7', '3.8', '3.9', 'pypy-3.8'] django-version: ['2.2', '3.1', '3.2'] # Tox configuration for QA environment include: @@ -27,7 +27,7 @@ jobs: - python-version: '3.10' django-version: 'main' experimental: true - - python-version: 'pypy3' + - python-version: 'pypy-3.8' django-version: 'main' experimental: true From e08cb22bd782e14bfc1fde630d4755bda411f27f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:07:47 +0000 Subject: [PATCH 002/372] Bump coverage from 6.1.1 to 6.1.2 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.1.1 to 6.1.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.1.1...6.1.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 04d8b91..93f3a0b 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.1.1 +coverage==6.1.2 pytest==6.2.5 pytest-cov==3.0.0 pytest-django==4.4.0 From 474d424c5fb7ca988a861675545af954362e4256 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Nov 2021 12:08:14 +0000 Subject: [PATCH 003/372] Bump black from 21.10b0 to 21.11b0 Bumps [black](https://github.com/psf/black) from 21.10b0 to 21.11b0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index bbb90c2..8e6b574 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==21.10b0 +black==21.11b0 mypy==0.910 prospector==1.3.1 types-pkg_resources # Type stub From 634012209269546fcff0a172047b0a879ac37ef3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 12:06:36 +0000 Subject: [PATCH 004/372] Bump black from 21.11b0 to 21.11b1 Bumps [black](https://github.com/psf/black) from 21.11b0 to 21.11b1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 8e6b574..1d03dbd 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==21.11b0 +black==21.11b1 mypy==0.910 prospector==1.3.1 types-pkg_resources # Type stub From 6e10592fc1556dcf52749742c32c30bd942377a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 12:09:38 +0000 Subject: [PATCH 005/372] Bump coverage from 6.1.2 to 6.2 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.1.2 to 6.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.1.2...6.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 93f3a0b..bfc2759 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.1.2 +coverage==6.2 pytest==6.2.5 pytest-cov==3.0.0 pytest-django==4.4.0 From 1e7ff06e2963e53da7e7f101f3ac60dea864a872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:05:51 +0000 Subject: [PATCH 006/372] Bump pytest-django from 4.4.0 to 4.5.1 Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.4.0 to 4.5.1. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.4.0...v4.5.1) --- updated-dependencies: - dependency-name: pytest-django dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index bfc2759..dee5c75 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,5 +2,5 @@ coverage==6.2 pytest==6.2.5 pytest-cov==3.0.0 -pytest-django==4.4.0 +pytest-django==4.5.1 pytest-subtests==0.5.0 From ff3773a51842c147ce9f024e665d51e8fba73795 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Dec 2021 12:09:48 +0000 Subject: [PATCH 007/372] Bump black from 21.11b1 to 21.12b0 Bumps [black](https://github.com/psf/black) from 21.11b1 to 21.12b0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 1d03dbd..ea298a0 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==21.11b1 +black==21.12b0 mypy==0.910 prospector==1.3.1 types-pkg_resources # Type stub From ae64946a460e8846e4cae1b78e1a8ede09335b8b Mon Sep 17 00:00:00 2001 From: Gilad Even-Tov Date: Tue, 7 Dec 2021 12:44:27 +0200 Subject: [PATCH 008/372] Fix small typo --- docs/4_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index ef7cfa3..bf01a0f 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -82,7 +82,7 @@ The following ``settings.py`` options are available for customizing Axes behavio * ``AXES_WHITELIST_CALLABLE``: A callable or a string path to callable that takes two arguments for whitelisting determination and returns True, if user should be whitelisted: - ``def is_whilisted(request: HttpRequest, credentials: dict) -> bool: ...``. + ``def is_whitelisted(request: HttpRequest, credentials: dict) -> bool: ...``. This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. Default: ``None`` * ``AXES_LOCKOUT_CALLABLE``: A callable or a string path to callable that takes From d674fa6296b43dee86a8cb356e79991cf32d32b4 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 30 Nov 2021 13:48:53 +0100 Subject: [PATCH 009/372] Bump prospector from 1.3.1 to 1.5.3 --- axes/handlers/base.py | 2 +- axes/handlers/cache.py | 8 ++--- axes/handlers/database.py | 12 ++------ axes/helpers.py | 30 ++++++++++++------- .../management/commands/axes_list_attempts.py | 2 +- axes/management/commands/axes_reset.py | 2 +- requirements-qa.txt | 2 +- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/axes/handlers/base.py b/axes/handlers/base.py index b115013..fe3408f 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -142,7 +142,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument admin_url = reverse("admin:index") except NoReverseMatch: return True - return not re.match("^%s" % admin_url, request.path) + return not re.match(f"^{admin_url}", request.path) return False diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index cfaa56a..ea0671f 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -64,9 +64,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): ) return failure_count - def user_login_failed( - self, sender, credentials: dict, request=None, **kwargs - ): # pylint: disable=too-many-locals + def user_login_failed(self, sender, credentials: dict, request=None, **kwargs): """ When user login fails, save attempt record in cache and lock user out if necessary. @@ -136,9 +134,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): ip_address=request.axes_ip_address, ) - def user_logged_in( - self, sender, request, user, **kwargs - ): # pylint: disable=unused-argument + def user_logged_in(self, sender, request, user, **kwargs): """ When user logs in, update the AccessLog related to the user. """ diff --git a/axes/handlers/database.py b/axes/handlers/database.py index 2995c06..fcb9711 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -83,9 +83,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): ) return attempt_count - def user_login_failed( - self, sender, credentials: dict, request=None, **kwargs - ): # pylint: disable=too-many-locals + def user_login_failed(self, sender, credentials: dict, request=None, **kwargs): """ When user login fails, save AccessAttempt record in database, mark request with lockout attribute and emit lockout signal. """ @@ -194,9 +192,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): ip_address=request.axes_ip_address, ) - def user_logged_in( - self, sender, request, user, **kwargs - ): # pylint: disable=unused-argument + def user_logged_in(self, sender, request, user, **kwargs): """ When user logs in, update the AccessLog related to the user. """ @@ -236,9 +232,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): client_str, ) - def user_logged_out( - self, sender, request, user, **kwargs - ): # pylint: disable=unused-argument + def user_logged_out(self, sender, request, user, **kwargs): """ When user logs out, update the AccessLog related to the user. """ diff --git a/axes/helpers.py b/axes/helpers.py index 9c31310..3061614 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -62,7 +62,7 @@ def get_cool_off() -> Optional[timedelta]: if isinstance(cool_off, str): return import_string(cool_off)() if callable(cool_off): - return cool_off() + return cool_off() # pylint: disable=not-callable return cool_off @@ -121,7 +121,9 @@ def get_client_username(request, credentials: dict = None) -> str: log.debug("Using settings.AXES_USERNAME_CALLABLE to get username") if callable(settings.AXES_USERNAME_CALLABLE): - return settings.AXES_USERNAME_CALLABLE(request, credentials) + return settings.AXES_USERNAME_CALLABLE( # pylint: disable=not-callable + request, credentials + ) if isinstance(settings.AXES_USERNAME_CALLABLE, str): return import_string(settings.AXES_USERNAME_CALLABLE)(request, credentials) raise TypeError( @@ -258,7 +260,7 @@ def get_client_str( log.debug("Using settings.AXES_CLIENT_STR_CALLABLE to get client string.") if callable(settings.AXES_CLIENT_STR_CALLABLE): - return settings.AXES_CLIENT_STR_CALLABLE( + return settings.AXES_CLIENT_STR_CALLABLE( # pylint: disable=not-callable username, ip_address, user_agent, path_info, request ) if isinstance(settings.AXES_CLIENT_STR_CALLABLE, str): @@ -269,7 +271,7 @@ def get_client_str( "settings.AXES_CLIENT_STR_CALLABLE needs to be a string, callable or None." ) - client_dict = dict() + client_dict = {} if settings.AXES_VERBOSE: # Verbose mode logs every attribute that is available @@ -342,7 +344,9 @@ def get_query_str(query: Type[QueryDict], max_length: int = 1024) -> str: def get_failure_limit(request, credentials) -> int: if callable(settings.AXES_FAILURE_LIMIT): - return settings.AXES_FAILURE_LIMIT(request, credentials) + return settings.AXES_FAILURE_LIMIT( # pylint: disable=not-callable + request, credentials + ) if isinstance(settings.AXES_FAILURE_LIMIT, str): return import_string(settings.AXES_FAILURE_LIMIT)(request, credentials) if isinstance(settings.AXES_FAILURE_LIMIT, int): @@ -359,7 +363,9 @@ def get_lockout_message() -> str: def get_lockout_response(request, credentials: dict = None) -> HttpResponse: if settings.AXES_LOCKOUT_CALLABLE: if callable(settings.AXES_LOCKOUT_CALLABLE): - return settings.AXES_LOCKOUT_CALLABLE(request, credentials) + return settings.AXES_LOCKOUT_CALLABLE( # pylint: disable=not-callable + request, credentials + ) if isinstance(settings.AXES_LOCKOUT_CALLABLE, str): return import_string(settings.AXES_LOCKOUT_CALLABLE)(request, credentials) raise TypeError( @@ -400,7 +406,7 @@ def get_lockout_response(request, credentials: dict = None) -> HttpResponse: if settings.AXES_LOCKOUT_URL: lockout_url = settings.AXES_LOCKOUT_URL query_string = urlencode({"username": context["username"]}) - url = "{}?{}".format(lockout_url, query_string) + url = f"{lockout_url}?{query_string}" return redirect(url) return HttpResponse(get_lockout_message(), status=status) @@ -410,14 +416,18 @@ def is_ip_address_in_whitelist(ip_address: str) -> bool: if not settings.AXES_IP_WHITELIST: return False - return ip_address in settings.AXES_IP_WHITELIST + return ( # pylint: disable=unsupported-membership-test + ip_address in settings.AXES_IP_WHITELIST + ) def is_ip_address_in_blacklist(ip_address: str) -> bool: if not settings.AXES_IP_BLACKLIST: return False - return ip_address in settings.AXES_IP_BLACKLIST + return ( # pylint: disable=unsupported-membership-test + ip_address in settings.AXES_IP_BLACKLIST + ) def is_client_ip_address_whitelisted(request): @@ -494,7 +504,7 @@ def is_user_attempt_whitelisted(request, credentials: dict = None) -> bool: if whitelist_callable is None: return False if callable(whitelist_callable): - return whitelist_callable(request, credentials) + return whitelist_callable(request, credentials) # pylint: disable=not-callable if isinstance(whitelist_callable, str): return import_string(whitelist_callable)(request, credentials) diff --git a/axes/management/commands/axes_list_attempts.py b/axes/management/commands/axes_list_attempts.py index ad4da8d..190682b 100644 --- a/axes/management/commands/axes_list_attempts.py +++ b/axes/management/commands/axes_list_attempts.py @@ -6,7 +6,7 @@ from axes.models import AccessAttempt class Command(BaseCommand): help = "List access attempts" - def handle(self, *args, **options): # pylint: disable=unused-argument + def handle(self, *args, **options): for obj in AccessAttempt.objects.all(): self.stdout.write( f"{obj.ip_address}\t{obj.username}\t{obj.failures_since_start}" diff --git a/axes/management/commands/axes_reset.py b/axes/management/commands/axes_reset.py index 70187e5..1cffccc 100644 --- a/axes/management/commands/axes_reset.py +++ b/axes/management/commands/axes_reset.py @@ -6,7 +6,7 @@ from axes.utils import reset class Command(BaseCommand): help = "Reset all access attempts and lockouts" - def handle(self, *args, **options): # pylint: disable=unused-argument + def handle(self, *args, **options): count = reset() if count: diff --git a/requirements-qa.txt b/requirements-qa.txt index ea298a0..9d692a0 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 mypy==0.910 -prospector==1.3.1 +prospector==1.5.3 types-pkg_resources # Type stub From d6778e40bea46f7e1899579c9575b18aa54c21d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Dec 2021 12:07:39 +0000 Subject: [PATCH 010/372] Bump pytest-django from 4.5.1 to 4.5.2 Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.5.1 to 4.5.2. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.5.1...v4.5.2) --- updated-dependencies: - dependency-name: pytest-django dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index dee5c75..2b50c9f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,5 +2,5 @@ coverage==6.2 pytest==6.2.5 pytest-cov==3.0.0 -pytest-django==4.5.1 +pytest-django==4.5.2 pytest-subtests==0.5.0 From 85f156b6616f3dd868b6afd44b2e468bdc179791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Dec 2021 12:07:44 +0000 Subject: [PATCH 011/372] Bump prospector from 1.5.3 to 1.5.3.1 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.5.3 to 1.5.3.1. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.5.3...1.5.3.1) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 9d692a0..9145eeb 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 mypy==0.910 -prospector==1.5.3 +prospector==1.5.3.1 types-pkg_resources # Type stub From 2c2fdbe0d9a0a55faaf1a8924342a42bb0d10fa2 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 10 Dec 2021 20:29:18 +0100 Subject: [PATCH 012/372] Drop Django < 3.2 support. --- .github/workflows/test.yml | 2 +- CHANGES.rst | 2 ++ axes/__init__.py | 4 ---- docs/6_integration.rst | 2 +- pyproject.toml | 5 ----- setup.py | 4 +--- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aa7063d..15dceb0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: python-version: ['3.6', '3.7', '3.8', '3.9', 'pypy-3.8'] - django-version: ['2.2', '3.1', '3.2'] + django-version: ['3.2'] # Tox configuration for QA environment include: - python-version: '3.8' diff --git a/CHANGES.rst b/CHANGES.rst index 9c7fb77..e8f73a9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,8 @@ Changes ======= +- Drop Django < 3.2 support. + [hramezani] 5.27.0 (2021-11-04) ------------------- diff --git a/axes/__init__.py b/axes/__init__.py index 521048d..37645c8 100644 --- a/axes/__init__.py +++ b/axes/__init__.py @@ -1,8 +1,4 @@ from pkg_resources import get_distribution -import django - -if django.VERSION < (3, 2): - default_app_config = "axes.apps.AppConfig" __version__ = get_distribution("django-axes").version diff --git a/docs/6_integration.rst b/docs/6_integration.rst index 1baa838..06220ff 100644 --- a/docs/6_integration.rst +++ b/docs/6_integration.rst @@ -126,7 +126,7 @@ And then configure your application to load it in ``examples/apps.py``:: Please check the Django signals documentation for more information: -https://docs.djangoproject.com/en/3.1/topics/signals/ +https://docs.djangoproject.com/en/3.2/topics/signals/ When a user login fails a signal is emitted and PermissionDenied raises a HTTP 403 reply which interrupts the login process. diff --git a/pyproject.toml b/pyproject.toml index 6390bbb..fadd81f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{36,37,38,39,py3}-dj{22,31} py{36,37,38,39,310}-dj32 py{38,39,310}-djmain py38-djqa @@ -26,8 +25,6 @@ python = [gh-actions:env] DJANGO = - 2.2: dj22 - 3.1: dj31 3.2: dj32 main: djmain qa: djqa @@ -36,8 +33,6 @@ DJANGO = [testenv] deps = -r requirements-test.txt - dj22: django>=2.2,<2.3 - dj31: django>=3.1,<3.2 dj32: django>=3.2,<3.3 djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = true diff --git a/setup.py b/setup.py index a1a0bc8..4155fa9 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ setup( use_scm_version=True, setup_requires=["setuptools_scm"], python_requires="~=3.6", - install_requires=["django>=2.2", "django-ipware>=3,<5", "setuptools"], + install_requires=["django>=3.2", "django-ipware>=3,<5", "setuptools"], include_package_data=True, packages=find_packages(exclude=["tests"]), classifiers=[ @@ -44,8 +44,6 @@ setup( "Environment :: Web Environment", "Environment :: Plugins", "Framework :: Django", - "Framework :: Django :: 2.2", - "Framework :: Django :: 3.1", "Framework :: Django :: 3.2", "Intended Audience :: Developers", "Intended Audience :: System Administrators", From 03c6798ebe3990cea0cc3b4eb5c271ee52f47c96 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 10 Dec 2021 20:42:17 +0100 Subject: [PATCH 013/372] Add Django 4.0 to test matrix. --- .github/workflows/test.yml | 16 ++++++++++------ CHANGES.rst | 2 ++ pyproject.toml | 4 +++- setup.py | 1 + tests/settings.py | 2 -- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15dceb0..3b29971 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,15 +9,13 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.6', '3.7', '3.8', '3.9', 'pypy-3.8'] - django-version: ['3.2'] - # Tox configuration for QA environment + python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] + django-version: ['3.2', '4.0'] include: + # Tox configuration for QA environment - python-version: '3.8' django-version: 'qa' - # Django >= 3.2 only supports >= Python 3.8 - - python-version: '3.10' - django-version: '3.2' + # Django main - python-version: '3.8' django-version: 'main' experimental: true @@ -30,6 +28,12 @@ jobs: - python-version: 'pypy-3.8' django-version: 'main' experimental: true + exclude: + # Exclude Django 4.0 for Python 3.6 and 3.7 + - python-version: '3.6' + django-version: '4.0' + - python-version: '3.7' + django-version: '4.0' steps: - uses: actions/checkout@v2 diff --git a/CHANGES.rst b/CHANGES.rst index e8f73a9..5b11b36 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changes - Drop Django < 3.2 support. [hramezani] +- Add Django 4.0 to test matrix. + [hramezani] 5.27.0 (2021-11-04) ------------------- diff --git a/pyproject.toml b/pyproject.toml index fadd81f..16e7a6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ legacy_tox_ini = """ [tox] envlist = py{36,37,38,39,310}-dj32 - py{38,39,310}-djmain + py{38,39,310}-dj{4.0, main} py38-djqa [gh-actions] @@ -26,6 +26,7 @@ python = [gh-actions:env] DJANGO = 3.2: dj32 + 4.0: dj40 main: djmain qa: djqa @@ -34,6 +35,7 @@ DJANGO = deps = -r requirements-test.txt dj32: django>=3.2,<3.3 + dj40: django>=4.0,<4.1 djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = true commands = pytest diff --git a/setup.py b/setup.py index 4155fa9..6d8ef3a 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ setup( "Environment :: Plugins", "Framework :: Django", "Framework :: Django :: 3.2", + "Framework :: Django :: 4.0", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", diff --git a/tests/settings.py b/tests/settings.py index 1f64a8d..43de0b4 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -65,8 +65,6 @@ SECRET_KEY = "too-secret-for-test" USE_I18N = False -USE_L10N = False - USE_TZ = False LOGIN_REDIRECT_URL = "/admin/" From 61489f9b0e037457b2765a5b6b066a1e7478615b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 14 Dec 2021 21:31:06 +0200 Subject: [PATCH 014/372] Version 5.28.0 --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5b11b36..e672545 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,11 +2,16 @@ Changes ======= + +5.28.0 (2021-12-14) +------------------- + - Drop Django < 3.2 support. [hramezani] - Add Django 4.0 to test matrix. [hramezani] + 5.27.0 (2021-11-04) ------------------- From 87a6a505771f90754d81b28ef116bfddf558abb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:06:29 +0000 Subject: [PATCH 015/372] Bump mypy from 0.910 to 0.920 Bumps [mypy](https://github.com/python/mypy) from 0.910 to 0.920. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.910...v0.920) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 9145eeb..4c5f116 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 -mypy==0.910 +mypy==0.920 prospector==1.5.3.1 types-pkg_resources # Type stub From a0cffc4c214387d39fc0aca954d427c7d25585ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Dec 2021 12:10:13 +0000 Subject: [PATCH 016/372] Bump mypy from 0.920 to 0.921 Bumps [mypy](https://github.com/python/mypy) from 0.920 to 0.921. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.920...v0.921) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 4c5f116..3e9e2d7 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 -mypy==0.920 +mypy==0.921 prospector==1.5.3.1 types-pkg_resources # Type stub From 7e84a235e3bcea7b024d0af4d7da96f7d3581141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 12:07:28 +0000 Subject: [PATCH 017/372] Bump mypy from 0.921 to 0.930 Bumps [mypy](https://github.com/python/mypy) from 0.921 to 0.930. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.921...v0.930) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 3e9e2d7..8132cd2 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 -mypy==0.921 +mypy==0.930 prospector==1.5.3.1 types-pkg_resources # Type stub From e7ce4ee3788a1ee4221b368673c4f2b7f5c4a371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Dec 2021 12:06:55 +0000 Subject: [PATCH 018/372] Bump tox from 3.24.4 to 3.24.5 Bumps [tox](https://github.com/tox-dev/tox) from 3.24.4 to 3.24.5. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.24.4...3.24.5) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b7e8013..1133462 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.0.0 -tox==3.24.4 +tox==3.24.5 From 69c7a02183102c4ada7d551a08f9a5835d3256fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 8 Jan 2022 19:51:59 +0200 Subject: [PATCH 019/372] Update Django and Python version support --- .github/workflows/test.yml | 12 +++++++----- axes/management/commands/axes_reset_user.py | 2 +- pyproject.toml | 20 ++++++++++---------- setup.py | 1 - 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b29971..5a054c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,9 +8,10 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: false + max-parallel: 5 matrix: - python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] - django-version: ['3.2', '4.0'] + python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] + django-version: ['3.2', '4.0', 'main'] include: # Tox configuration for QA environment - python-version: '3.8' @@ -29,11 +30,12 @@ jobs: django-version: 'main' experimental: true exclude: - # Exclude Django 4.0 for Python 3.6 and 3.7 - - python-version: '3.6' - django-version: '4.0' + # Exclude Django 4.0 for Python 3.7 - python-version: '3.7' django-version: '4.0' + # Exclude Django 2.2 for Python 3.10 + - python-version: '3.10' + django-version: '2.2' steps: - uses: actions/checkout@v2 diff --git a/axes/management/commands/axes_reset_user.py b/axes/management/commands/axes_reset_user.py index 5f24875..654525b 120000 --- a/axes/management/commands/axes_reset_user.py +++ b/axes/management/commands/axes_reset_user.py @@ -1 +1 @@ -axes_reset_username.py \ No newline at end of file +axes_reset_username.py diff --git a/pyproject.toml b/pyproject.toml index 16e7a6b..d8b8b66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,13 +10,13 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{36,37,38,39,310}-dj32 - py{38,39,310}-dj{4.0, main} - py38-djqa + py{37,38,39,310,py3}-dj32 + py{38,39,310,py3}-dj40 + py{38,39,310}-djmain + py310-djqa [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38 3.9: py39 @@ -41,17 +41,17 @@ usedevelop = true commands = pytest setenv = PYTHONDONTWRITEBYTECODE=1 - # Django development version is allowed to fail the test matrix -[testenv:py{38,39,py3}-djmain] -ignore_errors = true -ignore_outcome = true +ignore_outcome = + djmain: True +ignore_errors = + djmain: True # QA runs type checks, linting, and code formatting checks -[testenv:py38-djqa] +[testenv:py310-djqa] deps = -r requirements-qa.txt commands = mypy axes prospector - black -t py36 --check --diff axes + black -t py38 --check --diff axes """ diff --git a/setup.py b/setup.py index 6d8ef3a..317a3f9 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,6 @@ setup( "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", From 12f5c176fedbd6b9ee9595be70c70f02515687ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 8 Jan 2022 21:45:37 +0200 Subject: [PATCH 020/372] Version 5.29.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e672545..7d6bcf9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +5.29.0 (2022-01-08) +------------------- + +- Drop Python 3.6 support. + [aleksihakli] + + 5.28.0 (2021-12-14) ------------------- From d6581058eb395f6f27300ed0ec378879b4f17c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 8 Jan 2022 21:59:38 +0200 Subject: [PATCH 021/372] Fix symlink for builds --- axes/management/commands/axes_reset_user.py | 1 - 1 file changed, 1 deletion(-) delete mode 120000 axes/management/commands/axes_reset_user.py diff --git a/axes/management/commands/axes_reset_user.py b/axes/management/commands/axes_reset_user.py deleted file mode 120000 index 654525b..0000000 --- a/axes/management/commands/axes_reset_user.py +++ /dev/null @@ -1 +0,0 @@ -axes_reset_username.py From 983ef3da440db13afd84ab85a1fda66a8988278d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 8 Jan 2022 22:00:47 +0200 Subject: [PATCH 022/372] Version 5.30.0 --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7d6bcf9..508a47c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,7 @@ Changes ======= -5.29.0 (2022-01-08) +5.30.0 (2022-01-08) ------------------- - Drop Python 3.6 support. From 414c972738491fdd4338acd7208b16f91cdfb40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 9 Jan 2022 12:33:37 +0200 Subject: [PATCH 023/372] Adjust package version requirements Allow django-ipware to have a newer version in the future Update Python version requirement to match package specifiers --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 317a3f9..d05cc01 100644 --- a/setup.py +++ b/setup.py @@ -35,8 +35,8 @@ setup( package_dir={"axes": "axes"}, use_scm_version=True, setup_requires=["setuptools_scm"], - python_requires="~=3.6", - install_requires=["django>=3.2", "django-ipware>=3,<5", "setuptools"], + python_requires=">=3.7", + install_requires=["django>=3.2", "django-ipware>=3", "setuptools"], include_package_data=True, packages=find_packages(exclude=["tests"]), classifiers=[ From bafd8263f7b7e95678e483449d5c400ed13cd808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 9 Jan 2022 13:22:14 +0200 Subject: [PATCH 024/372] Version 5.31.0 --- CHANGES.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 508a47c..8016e6c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,9 +3,25 @@ Changes ======= +5.31.0 (2022-01-08) +------------------- + +- Adjust version specifiers for newer Python and other package versions. + Set package minimum Python version to 3.7. + Relax ``django-ipware`` version requirements to allow newer versions. + [aleksihakli] + + 5.30.0 (2022-01-08) ------------------- +- Fix package build error in 5.29.0 to allow publishing. + [aleksihakli] + + +5.29.0 (2022-01-08) +------------------- + - Drop Python 3.6 support. [aleksihakli] From 3fbf7c55ece0f5bf3a9e699cb939f6981acf1375 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jan 2022 12:09:23 +0000 Subject: [PATCH 025/372] Bump mypy from 0.930 to 0.931 Bumps [mypy](https://github.com/python/mypy) from 0.930 to 0.931. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.930...v0.931) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 8132cd2..1ffee10 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 -mypy==0.930 +mypy==0.931 prospector==1.5.3.1 types-pkg_resources # Type stub From 59ce9d99b8179b118bc8754348872d64eb22c899 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 12:07:15 +0000 Subject: [PATCH 026/372] Bump pytest-subtests from 0.5.0 to 0.6.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/pytest-dev/pytest-subtests/releases) - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.5.0...0.6.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 2b50c9f..1206445 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ coverage==6.2 pytest==6.2.5 pytest-cov==3.0.0 pytest-django==4.5.2 -pytest-subtests==0.5.0 +pytest-subtests==0.6.0 From 40ed1107b54268c2d8b416ff5f23770dc1b031b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jan 2022 12:07:25 +0000 Subject: [PATCH 027/372] Bump prospector from 1.5.3.1 to 1.6.0 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.5.3.1 to 1.6.0. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.5.3.1...1.6.0) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 1ffee10..5df963b 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==21.12b0 mypy==0.931 -prospector==1.5.3.1 +prospector==1.6.0 types-pkg_resources # Type stub From b81c29856181ce761a81cb422909854233559275 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Tue, 18 Jan 2022 11:11:27 +0100 Subject: [PATCH 028/372] warn about limited django rest framework integration ref #814 --- docs/6_integration.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/6_integration.rst b/docs/6_integration.rst index 06220ff..4844062 100644 --- a/docs/6_integration.rst +++ b/docs/6_integration.rst @@ -98,6 +98,11 @@ You also need to decorate ``dispatch()`` and ``form_invalid()`` methods of the A Integration with Django REST Framework -------------------------------------- +.. warning:: + The following guide only covers authentication schemes that rely on + Django's ``authenticate()`` function. Other schemes (e.g. + ``TokenAuthentication``) are currently not supported. + Django Axes requires REST Framework to be connected via lockout signals for correct functionality. From b60fecc3732146bba49ee23be0701798e4be8dd8 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 18 Jan 2022 23:09:03 +0100 Subject: [PATCH 029/372] Remove remaining items from Django 2.2 --- .github/workflows/test.yml | 3 --- docs/9_development.rst | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a054c3..4e54cc5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,9 +33,6 @@ jobs: # Exclude Django 4.0 for Python 3.7 - python-version: '3.7' django-version: '4.0' - # Exclude Django 2.2 for Python 3.10 - - python-version: '3.10' - django-version: '2.2' steps: - uses: actions/checkout@v2 diff --git a/docs/9_development.rst b/docs/9_development.rst index 65dbb85..560d58a 100644 --- a/docs/9_development.rst +++ b/docs/9_development.rst @@ -41,6 +41,6 @@ Tox runs the same test set that is run by GitHub Actions, and your code should b If you wish to limit the testing to specific environment(s), you can parametrize the tox run:: - $ tox -e py39-django22 + $ tox -e py39-django32 After you have pushed your changes, open a pull request on GitHub for getting your code upstreamed. From e48e5aefd1a1d8807f896f5f3da1b7d084a5ce8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 12:05:37 +0000 Subject: [PATCH 030/372] Bump coverage from 6.2 to 6.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.2 to 6.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.2...6.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1206445..def50db 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.2 +coverage==6.3 pytest==6.2.5 pytest-cov==3.0.0 pytest-django==4.5.2 From fed6302dde1d41c968b1c19fe005d943ad9fde35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 12:08:49 +0000 Subject: [PATCH 031/372] Bump black from 21.12b0 to 22.1.0 Bumps [black](https://github.com/psf/black) from 21.12b0 to 22.1.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits/22.1.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5df963b..326de35 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==21.12b0 +black==22.1.0 mypy==0.931 prospector==1.6.0 types-pkg_resources # Type stub From 97ce5f9eb27e55f65c0ca3616cec8e9f4be0ac55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 12:07:37 +0000 Subject: [PATCH 032/372] Bump coverage from 6.3 to 6.3.1 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.3 to 6.3.1. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.3...6.3.1) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index def50db..f2d4b59 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3 +coverage==6.3.1 pytest==6.2.5 pytest-cov==3.0.0 pytest-django==4.5.2 From 374e9d05a81a2ffaecc20c101a9023ea35697af4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:07:11 +0000 Subject: [PATCH 033/372] Bump pytest from 6.2.5 to 7.0.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.5 to 7.0.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.5...7.0.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f2d4b59..1c69054 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.3.1 -pytest==6.2.5 +pytest==7.0.0 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.6.0 From ca4d326fa4f96e3203d8977707b21d0aa2fb1d69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:08:25 +0000 Subject: [PATCH 034/372] Bump pytest from 7.0.0 to 7.0.1 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.0.0...7.0.1) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1c69054..f907951 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.3.1 -pytest==7.0.0 +pytest==7.0.1 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.6.0 From 24f073e188f595677ca8d29785b2e1fef94761a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:08:28 +0000 Subject: [PATCH 035/372] Bump pytest-subtests from 0.6.0 to 0.7.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/pytest-dev/pytest-subtests/releases) - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.6.0...0.7.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f907951..0b36032 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ coverage==6.3.1 pytest==7.0.1 pytest-cov==3.0.0 pytest-django==4.5.2 -pytest-subtests==0.6.0 +pytest-subtests==0.7.0 From 9d391841792e817f38a6924320cb2d0439656153 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 21 Jan 2022 18:25:57 +0100 Subject: [PATCH 036/372] Exclude running test for Python3.7 and Django main. --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e54cc5..86d911a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,9 +30,11 @@ jobs: django-version: 'main' experimental: true exclude: - # Exclude Django 4.0 for Python 3.7 + # Exclude Python 3.7 for Django 4.0 and Django main - python-version: '3.7' django-version: '4.0' + - python-version: '3.7' + django-version: 'main' steps: - uses: actions/checkout@v2 From 769103a0901861b8eae19bae7de0d21d47aeabca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 12:06:27 +0000 Subject: [PATCH 037/372] Bump coverage from 6.3.1 to 6.3.2 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.3.1 to 6.3.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.3.1...6.3.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 0b36032..19c8fca 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3.1 +coverage==6.3.2 pytest==7.0.1 pytest-cov==3.0.0 pytest-django==4.5.2 From 8ed798c1555ac53308517b9ef9287c30aa10215e Mon Sep 17 00:00:00 2001 From: hashlash Date: Sun, 27 Feb 2022 19:32:51 +0700 Subject: [PATCH 038/372] Use 4 space indentation in config docs --- docs/4_configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index bf01a0f..7e2bb89 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -150,8 +150,8 @@ following settings to suit your set up to correctly resolve client IP addresses: # refer to the Django request and response objects documentation AXES_META_PRECEDENCE_ORDER = [ - 'HTTP_X_FORWARDED_FOR', - 'REMOTE_ADDR', + 'HTTP_X_FORWARDED_FOR', + 'REMOTE_ADDR', ] Please note that proxies have different behaviours with the HTTP headers. Make sure that your proxy either strips the incoming value or otherwise makes sure of the validity of the header that is used because **any header values used in application configuration must be secure and trusted**. Otherwise the client can spoof IP addresses by just setting the header in their request and circumvent the IP address monitoring. Normal proxy server behaviours include overriding and appending the header value depending on the platform. Different platforms and gateway services utilize different headers, please refer to your deployment target documentation for up-to-date information on correct configuration. From 1f95f2278fb55e012ee5f03583fd76202ed2d7bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:08:09 +0000 Subject: [PATCH 039/372] Bump prospector from 1.6.0 to 1.7.0 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.6.0...1.7.0) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 326de35..55f44ca 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 mypy==0.931 -prospector==1.6.0 +prospector==1.7.0 types-pkg_resources # Type stub From 9ffa8aa9baf84e2af91303a30701e40cb562c1db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:09:57 +0000 Subject: [PATCH 040/372] Bump prospector from 1.7.0 to 1.7.4 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.7.0 to 1.7.4. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.7.0...1.7.4) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 55f44ca..6dd84d5 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 mypy==0.931 -prospector==1.7.0 +prospector==1.7.4 types-pkg_resources # Type stub From adc26bf13a0a09d6364c05933828bdc3faf6c283 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 12:08:23 +0000 Subject: [PATCH 041/372] Bump prospector from 1.7.4 to 1.7.5 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.7.4 to 1.7.5. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.7.4...1.7.5) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 6dd84d5..f1003c7 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 mypy==0.931 -prospector==1.7.4 +prospector==1.7.5 types-pkg_resources # Type stub From e4e88e01a357bb0f4fc9254ea58f46d6f485c50a Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Mon, 21 Feb 2022 22:07:41 +0100 Subject: [PATCH 042/372] Fix #845 -- Rename test functions --- tests/test_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 05b4c0f..e17937c 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -248,7 +248,7 @@ class ClientStringTestCase(AxesTestCase): @override_settings( AXES_CLIENT_STR_CALLABLE="tests.test_helpers.get_dummy_client_str" ) - def test_get_client_str_callable(self): + def test_get_client_str_callable_return_str(self): self.assertEqual( get_client_str( "username", "ip_address", "user_agent", "path_info", self.request @@ -259,7 +259,7 @@ class ClientStringTestCase(AxesTestCase): @override_settings( AXES_CLIENT_STR_CALLABLE="tests.test_helpers.get_dummy_client_str_using_request" ) - def test_get_client_str_callable(self): + def test_get_client_str_callable_using_request(self): self.request.user = self.user self.assertEqual( get_client_str( @@ -269,7 +269,7 @@ class ClientStringTestCase(AxesTestCase): ) -def get_dummy_client_str(username, ip_address, user_agent, path_info): +def get_dummy_client_str(username, ip_address, user_agent, path_info, request): return "client string" From 60e4b5c6fc56022f9bbf52da1ab160a51f105e29 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 4 Mar 2022 13:23:52 +0100 Subject: [PATCH 043/372] Pin pypy-3.8 version to fix SQLite error in CI. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86d911a..e43c1f2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] + python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8-v7.3.7'] django-version: ['3.2', '4.0', 'main'] include: # Tox configuration for QA environment From 4f1a92b1df7be84229e374a1d147e511c86f67b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Mar 2022 12:07:57 +0000 Subject: [PATCH 044/372] Bump prospector from 1.7.5 to 1.7.6 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.7.5 to 1.7.6. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.7.5...1.7.6) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index f1003c7..8d4fa16 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 mypy==0.931 -prospector==1.7.5 +prospector==1.7.6 types-pkg_resources # Type stub From 65aafe0207f87e3f1669b49a4031be2b2794c598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Mar 2022 12:09:09 +0000 Subject: [PATCH 045/372] Bump prospector from 1.7.6 to 1.7.7 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.7.6 to 1.7.7. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.7.6...1.7.7) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 8d4fa16..ba4d046 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 mypy==0.931 -prospector==1.7.6 +prospector==1.7.7 types-pkg_resources # Type stub From 5b011fd6621ef9e4c28375d184ba23f3338eac5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:09:24 +0000 Subject: [PATCH 046/372] Bump mypy from 0.931 to 0.940 Bumps [mypy](https://github.com/python/mypy) from 0.931 to 0.940. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.931...v0.940) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index ba4d046..d4b7b0a 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 -mypy==0.931 +mypy==0.940 prospector==1.7.7 types-pkg_resources # Type stub From 32c825d98a9d5f9bf34a9794252ae16d6f5dac9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:09:29 +0000 Subject: [PATCH 047/372] Bump pytest from 7.0.1 to 7.1.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.0.1 to 7.1.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.0.1...7.1.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 19c8fca..1ec2daf 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.3.2 -pytest==7.0.1 +pytest==7.1.0 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.7.0 From 6df6f60ff8bf78cb7aff9cc9acb009536991c8fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:07:48 +0000 Subject: [PATCH 048/372] Bump mypy from 0.940 to 0.941 Bumps [mypy](https://github.com/python/mypy) from 0.940 to 0.941. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.940...v0.941) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index d4b7b0a..450cdd9 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 -mypy==0.940 +mypy==0.941 prospector==1.7.7 types-pkg_resources # Type stub From bedf8a7efa1f8e1c229f3fc6081aa2c0cffe15a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:06:51 +0000 Subject: [PATCH 049/372] Bump mypy from 0.941 to 0.942 Bumps [mypy](https://github.com/python/mypy) from 0.941 to 0.942. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.941...v0.942) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 450cdd9..4f8d5c5 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.1.0 -mypy==0.941 +mypy==0.942 prospector==1.7.7 types-pkg_resources # Type stub From 81f8c918581a438ee8169de22b70c0d0fce3f802 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Mar 2022 12:08:30 +0000 Subject: [PATCH 050/372] Bump pytest from 7.1.0 to 7.1.1 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.0 to 7.1.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.1.0...7.1.1) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1ec2daf..6657e36 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.3.2 -pytest==7.1.0 +pytest==7.1.1 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.7.0 From 110fe905eb0bc4266440f083d753d6c9a4b0c260 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 12:10:14 +0000 Subject: [PATCH 051/372] Bump black from 22.1.0 to 22.3.0 Bumps [black](https://github.com/psf/black) from 22.1.0 to 22.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.1.0...22.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 4f8d5c5..a920369 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.1.0 +black==22.3.0 mypy==0.942 prospector==1.7.7 types-pkg_resources # Type stub From da7a9b25ed0d4861c6174d3656ff91d1efc35397 Mon Sep 17 00:00:00 2001 From: Gregory DAVID Date: Tue, 15 Mar 2022 09:40:02 +0000 Subject: [PATCH 052/372] Add AccessFailureLogclass to models to keep trace of all failed logins --- axes/migrations/0008_accessfailurelog.py | 30 ++++++++++++++++++++++++ axes/models.py | 12 ++++++++++ 2 files changed, 42 insertions(+) create mode 100644 axes/migrations/0008_accessfailurelog.py diff --git a/axes/migrations/0008_accessfailurelog.py b/axes/migrations/0008_accessfailurelog.py new file mode 100644 index 0000000..c7956f4 --- /dev/null +++ b/axes/migrations/0008_accessfailurelog.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2.12 on 2022-03-15 03:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('axes', '0007_alter_accessattempt_unique_together'), + ] + + operations = [ + migrations.CreateModel( + name='AccessFailureLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_agent', models.CharField(db_index=True, max_length=255, verbose_name='User Agent')), + ('ip_address', models.GenericIPAddressField(db_index=True, null=True, verbose_name='IP Address')), + ('username', models.CharField(db_index=True, max_length=255, null=True, verbose_name='Username')), + ('http_accept', models.CharField(max_length=1025, verbose_name='HTTP Accept')), + ('path_info', models.CharField(max_length=255, verbose_name='Path')), + ('attempt_time', models.DateTimeField(auto_now_add=True, verbose_name='Attempt Time')), + ('locked_out', models.BooleanField(blank=True, default=False, verbose_name='Access lock out')), + ], + options={ + 'verbose_name': 'access failure', + 'verbose_name_plural': 'access failures', + }, + ), + ] diff --git a/axes/models.py b/axes/models.py index 14f13a2..58e5dbd 100644 --- a/axes/models.py +++ b/axes/models.py @@ -21,6 +21,18 @@ class AccessBase(models.Model): ordering = ["-attempt_time"] +class AccessFailureLog(AccessBase): + locked_out = models.BooleanField(_("Access lock out"), null=False, blank=True, default=False) + + def __str__(self): + locked_out_str = ' locked out' if self.locked_out else '' + return f"Failed access: user {self.username}{locked_out_str} on {self.attempt_time} from {self.ip_address}" + + class Meta: + verbose_name = _("access failure") + verbose_name_plural = _("access failures") + + class AccessAttempt(AccessBase): get_data = models.TextField(_("GET Data")) From 1849552f45be8e3d4742f74d329a20d43b525e13 Mon Sep 17 00:00:00 2001 From: Gregory DAVID Date: Tue, 15 Mar 2022 09:42:24 +0000 Subject: [PATCH 053/372] Implement AccessFailureLog recordings --- axes/admin.py | 38 +++++++++++++- axes/conf.py | 4 ++ axes/handlers/base.py | 23 ++++++++ axes/handlers/database.py | 52 +++++++++++++++++-- axes/handlers/proxy.py | 8 +++ .../commands/axes_reset_failure_logs.py | 22 ++++++++ docs/4_configuration.rst | 7 +++ 7 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 axes/management/commands/axes_reset_failure_logs.py diff --git a/axes/admin.py b/axes/admin.py index ee36b31..93f3301 100644 --- a/axes/admin.py +++ b/axes/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ from axes.conf import settings -from axes.models import AccessAttempt, AccessLog +from axes.models import AccessAttempt, AccessLog, AccessFailureLog class AccessAttemptAdmin(admin.ModelAdmin): @@ -78,6 +78,42 @@ class AccessLogAdmin(admin.ModelAdmin): return False +class AccessFailureLogAdmin(admin.ModelAdmin): + list_display = ( + "attempt_time", + "ip_address", + "username", + "user_agent", + "path_info", + "locked_out", + ) + + list_filter = ["attempt_time", "locked_out", "path_info"] + + search_fields = ["ip_address", "user_agent", "username", "path_info"] + + date_hierarchy = "attempt_time" + + fieldsets = ( + (None, {"fields": ("path_info",)}), + (_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}), + ) + + readonly_fields = [ + "user_agent", + "ip_address", + "username", + "http_accept", + "path_info", + "attempt_time", + "locked_out", + ] + + def has_add_permission(self, request): + return False + + if settings.AXES_ENABLE_ADMIN: admin.site.register(AccessAttempt, AccessAttemptAdmin) admin.site.register(AccessLog, AccessLogAdmin) + admin.site.register(AccessFailureLog, AccessFailureLogAdmin) diff --git a/axes/conf.py b/axes/conf.py index 67dc2d0..63d6ce8 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -57,6 +57,10 @@ settings.AXES_RESET_ON_SUCCESS = getattr(settings, "AXES_RESET_ON_SUCCESS", Fals settings.AXES_DISABLE_ACCESS_LOG = getattr(settings, "AXES_DISABLE_ACCESS_LOG", False) +settings.AXES_ENABLE_ACCESS_FAILURE_LOG = getattr(settings, "AXES_ENABLE_ACCESS_FAILURE_LOG", False) + +settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT = getattr(settings, "AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT", 1000) + settings.AXES_HANDLER = getattr( settings, "AXES_HANDLER", "axes.handlers.database.AxesDatabaseHandler" ) diff --git a/axes/handlers/base.py b/axes/handlers/base.py index fe3408f..59b715d 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -174,6 +174,29 @@ class AxesBaseHandler: # pylint: disable=unused-argument """ return 0 + def reset_failure_logs(self, *, age_days: int = None) -> int: + """ + Resets access failure logs that are older than given number of days. + + This method makes more sense for the DB backend, but as it is used by the ProxyHandler + (via inherent), it needs to be defined here so we get compliant with all proxy methods. + + Please overwrite it on each specialized handler as needed. + """ + return 0 + + + def remove_out_of_limit_failure_logs(self, *, username: str, limit: int = None) -> int: + """Remove access failure logs that are over + AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT for user username. + + This method makes more sense for the DB backend, but as it is used by the ProxyHandler + (via inherent), it needs to be defined here so we get compliant with all proxy methods. + + Please overwrite it on each specialized handler as needed. + + """ + return 0 class AxesHandler(AbstractAxesHandler, AxesBaseHandler): """ diff --git a/axes/handlers/database.py b/axes/handlers/database.py index fcb9711..d2fc1a7 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -19,7 +19,7 @@ from axes.helpers import ( get_failure_limit, get_query_str, ) -from axes.models import AccessLog, AccessAttempt +from axes.models import AccessLog, AccessAttempt, AccessFailureLog from axes.signals import user_locked_out log = getLogger(__name__) @@ -70,6 +70,35 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return count + def reset_failure_logs(self, *, age_days: int = None) -> int: + if age_days is None: + count, _ = AccessFailureLog.objects.all().delete() + log.info("AXES: Reset all %d access failure logs from database.", count) + else: + limit = timezone.now() - timezone.timedelta(days=age_days) + count, _ = AccessFailureLog.objects.filter(attempt_time__lte=limit).delete() + log.info( + "AXES: Reset %d access failure logs older than %d days from database.", + count, + age_days, + ) + + return count + + def remove_out_of_limit_failure_logs( + self, + *, + username: str, + limit: int = settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT) -> int: + count = 0 + failures = AccessFailureLog.objects.filter(username=username) + out_of_limit_failures_logs = failures.count() - limit + if out_of_limit_failures_logs > 0: + for failure in failures[:out_of_limit_failures_logs]: + failure.delete() + count += 1 + return count + def get_failures(self, request, credentials: dict = None) -> int: attempts_list = get_user_attempts(request, credentials) attempt_count = max( @@ -84,8 +113,10 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return attempt_count def user_login_failed(self, sender, credentials: dict, request=None, **kwargs): - """ - When user login fails, save AccessAttempt record in database, mark request with lockout attribute and emit lockout signal. + """When user login fails, save AccessFailureLog record in database, + save AccessAttempt record in database, mark request with + lockout attribute and emit lockout signal. + """ log.info("AXES: User login failed, running database handler for failure.") @@ -192,6 +223,21 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): ip_address=request.axes_ip_address, ) + # 5. database entry: Log for ever the attempt in the AccessFailureLog + if settings.AXES_ENABLE_ACCESS_FAILURE_LOG: + with transaction.atomic(): + AccessFailureLog.objects.create( + username=username, + ip_address=request.axes_ip_address, + user_agent=request.axes_user_agent, + http_accept=request.axes_http_accept, + path_info=request.axes_path_info, + attempt_time=request.axes_attempt_time, + locked_out=request.axes_locked_out, + ) + self.remove_out_of_limit_failure_logs(username=username) + + def user_logged_in(self, sender, request, user, **kwargs): """ When user logs in, update the AccessLog related to the user. diff --git a/axes/handlers/proxy.py b/axes/handlers/proxy.py index a539db3..960d820 100644 --- a/axes/handlers/proxy.py +++ b/axes/handlers/proxy.py @@ -58,6 +58,14 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): def reset_logs(cls, *, age_days: int = None) -> int: return cls.get_implementation().reset_logs(age_days=age_days) + @classmethod + def reset_failure_logs(cls, *, age_days: int = None) -> int: + return cls.get_implementation().reset_failure_logs(age_days=age_days) + + @classmethod + def remove_out_of_limit_failure_logs(cls, *, username: str, limit: int = None) -> int: + return cls.get_implementation().remove_out_of_limit_failure_logs(username=username) + @staticmethod def update_request(request): """ diff --git a/axes/management/commands/axes_reset_failure_logs.py b/axes/management/commands/axes_reset_failure_logs.py new file mode 100644 index 0000000..b109d2f --- /dev/null +++ b/axes/management/commands/axes_reset_failure_logs.py @@ -0,0 +1,22 @@ +from django.core.management.base import BaseCommand + +from axes.handlers.proxy import AxesProxyHandler + + +class Command(BaseCommand): + help = "Reset access failure log records older than given days." + + def add_arguments(self, parser): + parser.add_argument( + "--age", + type=int, + default=30, + help="Maximum age for records to keep in days", + ) + + def handle(self, *args, **options): + count = AxesProxyHandler.reset_failure_logs(age_days=options["age"]) + if count: + self.stdout.write(f"{count} logs removed.") + else: + self.stdout.write("No logs found.") diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index 7e2bb89..f4abac8 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -110,6 +110,13 @@ The following ``settings.py`` options are available for customizing Axes behavio * ``AXES_DISABLE_ACCESS_LOG``: If ``True``, disable writing login and logout access logs to database, so the admin interface will not have user login trail for successful user authentication. Default: ``False`` +* ``AXES_ENABLE_ACCESS_FAILURE_LOG``: If ``True``, enable writing + login failure logs to database, so you will have every user login + trail for unsuccessful user authentication. Default: ``False`` +* ``AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT``: Sets the number of + failures to trail for each user. When the access failure log reach + this number of records, an automatic removal is ran. Default: + ``1000`` * ``AXES_RESET_ON_SUCCESS``: If ``True``, a successful login will reset the number of failed logins. Default: ``False`` * ``AXES_ALLOWED_CORS_ORIGINS``: Configures lockout response CORS headers for XHR requests. From 246d884b84eff1978934f3d22329db00c7baa635 Mon Sep 17 00:00:00 2001 From: Gregory DAVID Date: Tue, 15 Mar 2022 09:41:54 +0000 Subject: [PATCH 054/372] Tests for AccessFailureLog --- tests/base.py | 6 +++++- tests/test_admin.py | 6 +++++- tests/test_failures.py | 16 ++++++++++++++++ tests/test_handlers.py | 25 ++++++++++++++++++++++++- tests/test_models.py | 6 +++++- 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/test_failures.py diff --git a/tests/base.py b/tests/base.py index 52f32b5..be5cd56 100644 --- a/tests/base.py +++ b/tests/base.py @@ -19,7 +19,7 @@ from axes.helpers import ( get_credentials, get_failure_limit, ) -from axes.models import AccessAttempt, AccessLog +from axes.models import AccessAttempt, AccessLog, AccessFailureLog from axes.utils import reset @@ -79,6 +79,7 @@ class AxesTestCase(TestCase): self.request.axes_path_info = get_client_path_info(self.request) self.request.axes_http_accept = get_client_http_accept(self.request) self.request.axes_failures_since_start = None + self.request.axes_locked_out = False self.credentials = get_credentials(self.username) @@ -103,6 +104,9 @@ class AxesTestCase(TestCase): def create_log(self, **kwargs): return AccessLog.objects.create(**self.get_kwargs_with_defaults(**kwargs)) + def create_failure_log(self, **kwargs): + return AccessFailureLog.objects.create(**self.get_kwargs_with_defaults(**kwargs)) + def reset(self, ip=None, username=None): return reset(ip, username) diff --git a/tests/test_admin.py b/tests/test_admin.py index be9c35c..d739095 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -5,7 +5,7 @@ from django.contrib import admin from django.test import override_settings import axes.admin -from axes.models import AccessAttempt, AccessLog +from axes.models import AccessAttempt, AccessLog, AccessFailureLog from tests.base import AxesTestCase @@ -15,14 +15,18 @@ class AxesEnableAdminFlag(AxesTestCase): admin.site.unregister(AccessAttempt) with suppress(admin.sites.NotRegistered): admin.site.unregister(AccessLog) + with suppress(admin.sites.NotRegistered): + admin.site.unregister(AccessFailureLog) @override_settings(AXES_ENABLE_ADMIN=False) def test_disable_admin(self): reload(axes.admin) self.assertFalse(admin.site.is_registered(AccessAttempt)) self.assertFalse(admin.site.is_registered(AccessLog)) + self.assertFalse(admin.site.is_registered(AccessFailureLog)) def test_enable_admin_by_default(self): reload(axes.admin) self.assertTrue(admin.site.is_registered(AccessAttempt)) self.assertTrue(admin.site.is_registered(AccessLog)) + self.assertTrue(admin.site.is_registered(AccessFailureLog)) diff --git a/tests/test_failures.py b/tests/test_failures.py new file mode 100644 index 0000000..88c5edc --- /dev/null +++ b/tests/test_failures.py @@ -0,0 +1,16 @@ +from axes.models import AccessFailureLog +from tests.base import AxesTestCase +from axes.helpers import get_failure_limit +from django.test import override_settings + +@override_settings(AXES_ENABLE_ACCESS_FAILURE_LOG=True) +class FailureLogTestCase(AxesTestCase): + def test_failure_log(self): + self.login(is_valid_username=True, is_valid_password=False) + self.assertEqual(AccessFailureLog.objects.count(), 1) + self.assertTrue(AccessFailureLog.objects.filter(username=self.VALID_USERNAME).exists()) + self.assertTrue(AccessFailureLog.objects.filter(ip_address=self.ip_address).exists()) + + def test_failure_locked_out(self): + self.check_lockout() + self.assertEqual(AccessFailureLog.objects.filter(locked_out=True).count(), 1) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index b7cc0c1..5ae36b0 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -12,7 +12,7 @@ from django.utils.timezone import timedelta from axes.conf import settings from axes.handlers.proxy import AxesProxyHandler from axes.helpers import get_client_str -from axes.models import AccessAttempt, AccessLog +from axes.models import AccessAttempt, AccessLog, AccessFailureLog from tests.base import AxesTestCase @@ -212,6 +212,7 @@ class ResetAttemptsTestCase(AxesHandlerBaseTestCase): AXES_HANDLER="axes.handlers.database.AxesDatabaseHandler", AXES_COOLOFF_TIME=timedelta(seconds=2), AXES_RESET_ON_SUCCESS=True, + AXES_ENABLE_ACCESS_FAILURE_LOG=True, ) @mark.xfail( python_implementation() == "PyPy", @@ -240,6 +241,28 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): self.assertEqual(1, AxesProxyHandler.reset_logs(age_days=42)) self.assertEqual(AccessLog.objects.count(), 1) + def test_handler_reset_failure_logs(self): + self.create_failure_log() + self.assertEqual(1, AxesProxyHandler.reset_failure_logs()) + self.assertFalse(AccessFailureLog.objects.count()) + + def test_handler_reset_failure_logs_older_than_42_days(self): + self.create_failure_log() + + then = timezone.now() - timezone.timedelta(days=90) + with patch("django.utils.timezone.now", return_value=then): + self.create_failure_log() + + self.assertEqual(AccessFailureLog.objects.count(), 2) + self.assertEqual(1, AxesProxyHandler.reset_failure_logs(age_days=42)) + self.assertEqual(AccessFailureLog.objects.count(), 1) + + def test_handler_remove_out_of_limit_failure_logs(self): + _more = 10 + for i in range(settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT + _more): + self.create_failure_log() + self.assertEqual(_more, AxesProxyHandler.remove_out_of_limit_failure_logs(username=self.username)) + @override_settings(AXES_RESET_ON_SUCCESS=True) def test_handler(self): self.check_handler() diff --git a/tests/test_models.py b/tests/test_models.py index 95aade1..6ba0f5b 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -4,7 +4,7 @@ from django.db.migrations.autodetector import MigrationAutodetector from django.db.migrations.executor import MigrationExecutor from django.db.migrations.state import ProjectState -from axes.models import AccessAttempt, AccessLog +from axes.models import AccessAttempt, AccessLog, AccessFailureLog from tests.base import AxesTestCase @@ -16,6 +16,7 @@ class ModelsTestCase(AxesTestCase): failures_since_start=self.failures_since_start ) self.access_log = AccessLog() + self.access_failure_log = AccessFailureLog() def test_access_attempt_str(self): self.assertIn("Access", str(self.access_attempt)) @@ -23,6 +24,9 @@ class ModelsTestCase(AxesTestCase): def test_access_log_str(self): self.assertIn("Access", str(self.access_log)) + def test_access_failure_log_str(self): + self.assertIn("Failed", str(self.access_failure_log)) + class MigrationsTestCase(AxesTestCase): def test_missing_migrations(self): From 1015bad45149e50d0806c7f575ddec7b9599d2b2 Mon Sep 17 00:00:00 2001 From: Antoine Dujardin Date: Tue, 22 Mar 2022 11:11:34 +0100 Subject: [PATCH 055/372] Don't reset cooloff time in case of login attempt during lockout --- axes/backends.py | 3 +++ axes/handlers/cache.py | 14 ++++++++++++-- axes/handlers/database.py | 11 +++++++++++ axes/handlers/proxy.py | 3 ++- tests/test_login.py | 20 ++++++++++++++++++-- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/axes/backends.py b/axes/backends.py index 5616979..926eef0 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -51,6 +51,9 @@ class AxesBackend(ModelBackend): response_context = kwargs.get("response_context", {}) response_context["error"] = error_msg + # This flag can be used later to check if it was Axes that denied the login attempt. + request.axes_locked_out = True + # Raise an error that stops the authentication flows at django.contrib.auth.authenticate. # This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors. # After this error is caught by authenticate it emits a signal indicating user login failed, diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index ea0671f..38dae3c 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -24,7 +24,6 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): def __init__(self): self.cache = get_cache() - self.cache_timeout = get_cache_timeout() def reset_attempts( self, @@ -84,6 +83,17 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): ) return + # If axes denied access, don't record the failed attempt as that would reset the lockout time. + if request.axes_locked_out: + request.axes_credentials = credentials + user_locked_out.send( + "axes", + request=request, + username=username, + ip_address=request.axes_ip_address, + ) + return + client_str = get_client_str( username, request.axes_ip_address, @@ -115,7 +125,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): cache_keys = get_client_cache_key(request, credentials) for cache_key in cache_keys: failures = self.cache.get(cache_key, default=0) - self.cache.set(cache_key, failures + 1, self.cache_timeout) + self.cache.set(cache_key, failures + 1, get_cache_timeout()) if ( settings.AXES_LOCK_OUT_AT_FAILURE diff --git a/axes/handlers/database.py b/axes/handlers/database.py index d2fc1a7..e69e06d 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -139,6 +139,17 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): request, ) + # If axes denied access, don't record the failed attempt as that would reset the lockout time. + if request.axes_locked_out: + request.axes_credentials = credentials + user_locked_out.send( + "axes", + request=request, + username=username, + ip_address=request.axes_ip_address, + ) + return + # This replaces null byte chars that crash saving failures. get_data = get_query_str(request.GET).replace("\0", "0x00") post_data = get_query_str(request.POST).replace("\0", "0x00") diff --git a/axes/handlers/proxy.py b/axes/handlers/proxy.py index 960d820..8c0a3f3 100644 --- a/axes/handlers/proxy.py +++ b/axes/handlers/proxy.py @@ -78,7 +78,8 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): ) return if not hasattr(request, "axes_updated"): - request.axes_locked_out = False + if not hasattr(request, "axes_locked_out"): + request.axes_locked_out = False request.axes_attempt_time = now() request.axes_ip_address = get_client_ip_address(request) request.axes_user_agent = get_client_user_agent(request) diff --git a/tests/test_login.py b/tests/test_login.py index bb0468a..9541825 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -3,8 +3,9 @@ Integration tests for the login handling. TODO: Clean up the tests in this module. """ - +from datetime import timedelta from importlib import import_module +from time import sleep from django.contrib.auth import get_user_model, login, logout from django.http import HttpRequest @@ -12,7 +13,7 @@ from django.test import override_settings, TestCase from django.urls import reverse from axes.conf import settings -from axes.helpers import get_cache, make_cache_key_list +from axes.helpers import get_cache, make_cache_key_list, get_cool_off from axes.models import AccessAttempt from tests.base import AxesTestCase @@ -631,6 +632,21 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) + @override_settings(AXES_COOLOFF_TIME=timedelta(seconds=1)) + def test_login_during_lockout_doesnt_reset_cool_off_time(self): + # Lockout + self.lockout() + + # Attempt during lockout + sleep_time = get_cool_off().total_seconds() / 2 + sleep(sleep_time) + self.login() + sleep(sleep_time) + + # New attempt after initial lockout period: should work + response = self.login(is_valid_username=True, is_valid_password=True) + self.assertNotContains(response, self.LOCKED_MESSAGE, status_code=302) + # Test the same logic with cache handler @override_settings(AXES_HANDLER="axes.handlers.cache.AxesCacheHandler") From 9c2ceb7eb7a025310e72ba0948025b5c928d0ae3 Mon Sep 17 00:00:00 2001 From: Antoine Dujardin Date: Tue, 22 Mar 2022 13:58:23 +0100 Subject: [PATCH 056/372] Add option to keep current behavior for cooloff reset --- axes/backends.py | 4 +++- axes/conf.py | 5 +++++ axes/handlers/cache.py | 5 ++++- axes/handlers/database.py | 5 ++++- docs/4_configuration.rst | 3 +++ tests/test_login.py | 35 ++++++++++++++++++++++++++++++----- 6 files changed, 49 insertions(+), 8 deletions(-) diff --git a/axes/backends.py b/axes/backends.py index 926eef0..c337f89 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib.auth.backends import ModelBackend from axes.exceptions import ( @@ -52,7 +53,8 @@ class AxesBackend(ModelBackend): response_context["error"] = error_msg # This flag can be used later to check if it was Axes that denied the login attempt. - request.axes_locked_out = True + if not settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT: + request.axes_locked_out = True # Raise an error that stops the authentication flows at django.contrib.auth.authenticate. # This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors. diff --git a/axes/conf.py b/axes/conf.py index 63d6ce8..ad7ccbe 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -136,3 +136,8 @@ settings.AXES_CLIENT_STR_CALLABLE = getattr(settings, "AXES_CLIENT_STR_CALLABLE" # set the HTTP response code given by too many requests settings.AXES_HTTP_RESPONSE_CODE = getattr(settings, "AXES_HTTP_RESPONSE_CODE", 403) + +# If True, a failed login attempt during lockout will reset the cool off period +settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT = getattr( + settings, "AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT", True +) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 38dae3c..8bddae1 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -84,7 +84,10 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return # If axes denied access, don't record the failed attempt as that would reset the lockout time. - if request.axes_locked_out: + if ( + not settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT + and request.axes_locked_out + ): request.axes_credentials = credentials user_locked_out.send( "axes", diff --git a/axes/handlers/database.py b/axes/handlers/database.py index e69e06d..176c898 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -140,7 +140,10 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): ) # If axes denied access, don't record the failed attempt as that would reset the lockout time. - if request.axes_locked_out: + if ( + not settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT + and request.axes_locked_out + ): request.axes_credentials = credentials user_locked_out.send( "axes", diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index f4abac8..8d2b552 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -125,6 +125,9 @@ The following ``settings.py`` options are available for customizing Axes behavio reached. For example: ``AXES_HTTP_RESPONSE_CODE = 429`` Default: ``403`` +* ``AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT``: If ``True``, a failed login attempt during lockout will + reset the cool off period. + Default: ``True`` The configuration option precedences for the access attempt monitoring are: diff --git a/tests/test_login.py b/tests/test_login.py index 9541825..07d5a45 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -13,7 +13,7 @@ from django.test import override_settings, TestCase from django.urls import reverse from axes.conf import settings -from axes.helpers import get_cache, make_cache_key_list, get_cool_off +from axes.helpers import get_cache, make_cache_key_list, get_cool_off, get_failure_limit from axes.models import AccessAttempt from tests.base import AxesTestCase @@ -632,20 +632,45 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) - @override_settings(AXES_COOLOFF_TIME=timedelta(seconds=1)) + @override_settings( + AXES_COOLOFF_TIME=timedelta(seconds=1), + AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT=False, + AXES_FAILURE_LIMIT=2, + ) def test_login_during_lockout_doesnt_reset_cool_off_time(self): # Lockout - self.lockout() + for _ in range(get_failure_limit(None, None)): + self.login(self.USER_1) # Attempt during lockout sleep_time = get_cool_off().total_seconds() / 2 sleep(sleep_time) - self.login() + self.login(self.USER_1) sleep(sleep_time) # New attempt after initial lockout period: should work response = self.login(is_valid_username=True, is_valid_password=True) - self.assertNotContains(response, self.LOCKED_MESSAGE, status_code=302) + self.assertNotContains(response, self.LOCKED_MESSAGE, status_code=self.ALLOWED) + + @override_settings( + AXES_COOLOFF_TIME=timedelta(seconds=1), + AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT=True, + AXES_FAILURE_LIMIT=2, + ) + def test_login_during_lockout_does_reset_cool_off_time(self): + # Lockout + for _ in range(get_failure_limit(None, None)): + self.login(self.USER_1) + + # Attempt during lockout + sleep_time = get_cool_off().total_seconds() / 2 + sleep(sleep_time) + self.login(self.USER_1) + sleep(sleep_time) + + # New attempt after initial lockout period: should not work + response = self.login(is_valid_username=True, is_valid_password=True) + self.assertContains(response, self.LOCKED_MESSAGE, status_code=self.BLOCKED) # Test the same logic with cache handler From 813a8518bc7306988492fdafdf38c0cd887f1129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 8 Apr 2022 22:21:17 +0300 Subject: [PATCH 057/372] Version 5.32.0 --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8016e6c..7266689 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,18 @@ Changes ======= +5.32.0 (2022-04-08) +------------------- + +- Add support for persistent failure logging + where failed login attempts are persisted in the database + until a specific threshold is reached. + [p1-gdd] +- Add support for not resetting login times when users + try to login during the lockout cooloff period. + [antoine-42] + + 5.31.0 (2022-01-08) ------------------- From eeafc569e934756ca39b9a8d6bdcb795dddbfe80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 12:27:59 +0000 Subject: [PATCH 058/372] Bump tox from 3.24.5 to 3.25.0 Bumps [tox](https://github.com/tox-dev/tox) from 3.24.5 to 3.25.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.24.5...3.25.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1133462..c2f5816 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.0.0 -tox==3.24.5 +tox==3.25.0 From bd82bf8e35e36187484f76a8a987b6afc717b86a Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Mon, 11 Apr 2022 14:03:49 +0200 Subject: [PATCH 059/372] Change tox QA env Python version to 3.8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d8b8b66..2ff2e5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ envlist = py{37,38,39,310,py3}-dj32 py{38,39,310,py3}-dj40 py{38,39,310}-djmain - py310-djqa + py38-djqa [gh-actions] python = From 4da7eb9fc13345f3ac3d9265ce6255477cad50ab Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Mon, 11 Apr 2022 14:05:09 +0200 Subject: [PATCH 060/372] Add Optional to type of params with default value equal to None --- axes/attempts.py | 12 ++++++------ axes/backends.py | 3 ++- axes/handlers/base.py | 23 ++++++++++++----------- axes/handlers/cache.py | 3 ++- axes/handlers/database.py | 13 +++++++------ axes/handlers/dummy.py | 5 +++-- axes/handlers/proxy.py | 17 +++++++++-------- axes/handlers/test.py | 11 ++++++----- axes/helpers.py | 10 +++++----- axes/utils.py | 2 +- 10 files changed, 53 insertions(+), 46 deletions(-) diff --git a/axes/attempts.py b/axes/attempts.py index a47eb60..9ab40be 100644 --- a/axes/attempts.py +++ b/axes/attempts.py @@ -1,5 +1,5 @@ from logging import getLogger -from typing import List +from typing import List, Optional from django.db.models import QuerySet from django.utils.timezone import datetime, now @@ -11,7 +11,7 @@ from axes.models import AccessAttempt log = getLogger(__name__) -def get_cool_off_threshold(attempt_time: datetime = None) -> datetime: +def get_cool_off_threshold(attempt_time: Optional[datetime] = None) -> datetime: """ Get threshold for fetching access attempts from the database. """ @@ -27,7 +27,7 @@ def get_cool_off_threshold(attempt_time: datetime = None) -> datetime: return attempt_time - cool_off -def filter_user_attempts(request, credentials: dict = None) -> List[QuerySet]: +def filter_user_attempts(request, credentials: Optional[dict] = None) -> List[QuerySet]: """ Return a list querysets of AccessAttempts that match the given request and credentials. """ @@ -44,7 +44,7 @@ def filter_user_attempts(request, credentials: dict = None) -> List[QuerySet]: return attempts_list -def get_user_attempts(request, credentials: dict = None) -> List[QuerySet]: +def get_user_attempts(request, credentials: Optional[dict] = None) -> List[QuerySet]: """ Get list of querysets with valid user attempts that match the given request and credentials. """ @@ -62,7 +62,7 @@ def get_user_attempts(request, credentials: dict = None) -> List[QuerySet]: return [attempts.filter(attempt_time__gte=threshold) for attempts in attempts_list] -def clean_expired_user_attempts(attempt_time: datetime = None) -> int: +def clean_expired_user_attempts(attempt_time: Optional[datetime] = None) -> int: """ Clean expired user attempts from the database. """ @@ -83,7 +83,7 @@ def clean_expired_user_attempts(attempt_time: datetime = None) -> int: return count -def reset_user_attempts(request, credentials: dict = None) -> int: +def reset_user_attempts(request, credentials: Optional[dict] = None) -> int: """ Reset all user attempts that match the given request and credentials. """ diff --git a/axes/backends.py b/axes/backends.py index c337f89..30535f3 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -1,3 +1,4 @@ +from typing import Optional from django.conf import settings from django.contrib.auth.backends import ModelBackend @@ -22,7 +23,7 @@ class AxesBackend(ModelBackend): @toggleable def authenticate( - self, request, username: str = None, password: str = None, **kwargs: dict + self, request, username: Optional[str] = None, password: Optional[str] = None, **kwargs: dict ): """ Checks user lockout status and raises an exception if user is not allowed to log in. diff --git a/axes/handlers/base.py b/axes/handlers/base.py index 59b715d..f2643c4 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -1,5 +1,6 @@ import re from abc import ABC, abstractmethod +from typing import Optional from django.urls import reverse from django.urls.exceptions import NoReverseMatch @@ -41,7 +42,7 @@ class AbstractAxesHandler(ABC): raise NotImplementedError("user_logged_out should be implemented") @abstractmethod - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: """ Checks the number of failures associated to the given request and credentials. @@ -65,7 +66,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument .. note:: This is a virtual class and **can not be used without specialization**. """ - def is_allowed(self, request, credentials: dict = None) -> bool: + def is_allowed(self, request, credentials: Optional[dict] = None) -> bool: """ Checks if the user is allowed to access or use given functionality such as a login view or authentication. @@ -94,7 +95,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument return True - def is_blacklisted(self, request, credentials: dict = None) -> bool: + def is_blacklisted(self, request, credentials: Optional[dict] = None) -> bool: """ Checks if the request or given credentials are blacklisted from access. """ @@ -104,7 +105,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument return False - def is_whitelisted(self, request, credentials: dict = None) -> bool: + def is_whitelisted(self, request, credentials: Optional[dict] = None) -> bool: """ Checks if the request or given credentials are whitelisted for access. """ @@ -120,7 +121,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument return False - def is_locked(self, request, credentials: dict = None) -> bool: + def is_locked(self, request, credentials: Optional[dict] = None) -> bool: """ Checks if the request or given credentials are locked. """ @@ -149,8 +150,8 @@ class AxesBaseHandler: # pylint: disable=unused-argument def reset_attempts( self, *, - ip_address: str = None, - username: str = None, + ip_address: Optional[str] = None, + username: Optional[str] = None, ip_or_username: bool = False, ) -> int: """ @@ -163,7 +164,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument """ return 0 - def reset_logs(self, *, age_days: int = None) -> int: + def reset_logs(self, *, age_days: Optional[int] = None) -> int: """ Resets access logs that are older than given number of days. @@ -174,7 +175,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument """ return 0 - def reset_failure_logs(self, *, age_days: int = None) -> int: + def reset_failure_logs(self, *, age_days: Optional[int] = None) -> int: """ Resets access failure logs that are older than given number of days. @@ -186,7 +187,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument return 0 - def remove_out_of_limit_failure_logs(self, *, username: str, limit: int = None) -> int: + def remove_out_of_limit_failure_logs(self, *, username: str, limit: Optional[int] = None) -> int: """Remove access failure logs that are over AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT for user username. @@ -212,5 +213,5 @@ class AxesHandler(AbstractAxesHandler, AxesBaseHandler): def user_logged_out(self, sender, request, user, **kwargs): pass - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: return 0 diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 8bddae1..423c592 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -1,4 +1,5 @@ from logging import getLogger +from typing import Optional from axes.conf import settings from axes.handlers.base import AxesBaseHandler, AbstractAxesHandler @@ -56,7 +57,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return count - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: cache_keys = get_client_cache_key(request, credentials) failure_count = max( self.cache.get(cache_key, default=0) for cache_key in cache_keys diff --git a/axes/handlers/database.py b/axes/handlers/database.py index 176c898..5ac0843 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -1,4 +1,5 @@ from logging import getLogger +from typing import Optional from django.db import transaction from django.db.models import F, Sum, Value, Q @@ -36,8 +37,8 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): def reset_attempts( self, *, - ip_address: str = None, - username: str = None, + ip_address: Optional[str] = None, + username: Optional[str] = None, ip_or_username: bool = False, ) -> int: attempts = AccessAttempt.objects.all() @@ -55,7 +56,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return count - def reset_logs(self, *, age_days: int = None) -> int: + def reset_logs(self, *, age_days: Optional[int] = None) -> int: if age_days is None: count, _ = AccessLog.objects.all().delete() log.info("AXES: Reset all %d access logs from database.", count) @@ -70,7 +71,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return count - def reset_failure_logs(self, *, age_days: int = None) -> int: + def reset_failure_logs(self, *, age_days: Optional[int] = None) -> int: if age_days is None: count, _ = AccessFailureLog.objects.all().delete() log.info("AXES: Reset all %d access failure logs from database.", count) @@ -89,7 +90,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): self, *, username: str, - limit: int = settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT) -> int: + limit: Optional[int] = settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT) -> int: count = 0 failures = AccessFailureLog.objects.filter(username=username) out_of_limit_failures_logs = failures.count() - limit @@ -99,7 +100,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): count += 1 return count - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: attempts_list = get_user_attempts(request, credentials) attempt_count = max( ( diff --git a/axes/handlers/dummy.py b/axes/handlers/dummy.py index 0e40ace..b8c3bbb 100644 --- a/axes/handlers/dummy.py +++ b/axes/handlers/dummy.py @@ -1,4 +1,5 @@ from axes.handlers.base import AxesBaseHandler, AbstractAxesHandler +from typing import Optional class AxesDummyHandler(AbstractAxesHandler, AxesBaseHandler): @@ -6,7 +7,7 @@ class AxesDummyHandler(AbstractAxesHandler, AxesBaseHandler): Signal handler implementation that does nothing and can be used to disable signal processing. """ - def is_allowed(self, request, credentials: dict = None) -> bool: + def is_allowed(self, request, credentials: Optional[dict] = None) -> bool: return True def user_login_failed(self, sender, credentials: dict, request=None, **kwargs): @@ -18,5 +19,5 @@ class AxesDummyHandler(AbstractAxesHandler, AxesBaseHandler): def user_logged_out(self, sender, request, user, **kwargs): pass - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: return 0 diff --git a/axes/handlers/proxy.py b/axes/handlers/proxy.py index 8c0a3f3..1adebe1 100644 --- a/axes/handlers/proxy.py +++ b/axes/handlers/proxy.py @@ -1,4 +1,5 @@ from logging import getLogger +from typing import Optional from django.utils.module_loading import import_string from django.utils.timezone import now @@ -46,8 +47,8 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): def reset_attempts( cls, *, - ip_address: str = None, - username: str = None, + ip_address: Optional[str] = None, + username: Optional[str] = None, ip_or_username: bool = False, ) -> int: return cls.get_implementation().reset_attempts( @@ -55,15 +56,15 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): ) @classmethod - def reset_logs(cls, *, age_days: int = None) -> int: + def reset_logs(cls, *, age_days: Optional[int] = None) -> int: return cls.get_implementation().reset_logs(age_days=age_days) @classmethod - def reset_failure_logs(cls, *, age_days: int = None) -> int: + def reset_failure_logs(cls, *, age_days: Optional[int] = None) -> int: return cls.get_implementation().reset_failure_logs(age_days=age_days) @classmethod - def remove_out_of_limit_failure_logs(cls, *, username: str, limit: int = None) -> int: + def remove_out_of_limit_failure_logs(cls, *, username: str, limit: Optional[int] = None) -> int: return cls.get_implementation().remove_out_of_limit_failure_logs(username=username) @staticmethod @@ -90,17 +91,17 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): request.axes_credentials = None @classmethod - def is_locked(cls, request, credentials: dict = None) -> bool: + def is_locked(cls, request, credentials: Optional[dict] = None) -> bool: cls.update_request(request) return cls.get_implementation().is_locked(request, credentials) @classmethod - def is_allowed(cls, request, credentials: dict = None) -> bool: + def is_allowed(cls, request, credentials: Optional[dict] = None) -> bool: cls.update_request(request) return cls.get_implementation().is_allowed(request, credentials) @classmethod - def get_failures(cls, request, credentials: dict = None) -> int: + def get_failures(cls, request, credentials: Optional[dict] = None) -> int: cls.update_request(request) return cls.get_implementation().get_failures(request, credentials) diff --git a/axes/handlers/test.py b/axes/handlers/test.py index c345104..bea66b3 100644 --- a/axes/handlers/test.py +++ b/axes/handlers/test.py @@ -1,4 +1,5 @@ from axes.handlers.base import AxesHandler +from typing import Optional class AxesTestHandler(AxesHandler): # pylint: disable=unused-argument @@ -9,17 +10,17 @@ class AxesTestHandler(AxesHandler): # pylint: disable=unused-argument def reset_attempts( self, *, - ip_address: str = None, - username: str = None, + ip_address: Optional[str] = None, + username: Optional[str] = None, ip_or_username: bool = False, ) -> int: return 0 - def reset_logs(self, *, age_days: int = None) -> int: + def reset_logs(self, *, age_days: Optional[int] = None) -> int: return 0 - def is_allowed(self, request, credentials: dict = None) -> bool: + def is_allowed(self, request, credentials: Optional[dict] = None) -> bool: return True - def get_failures(self, request, credentials: dict = None) -> int: + def get_failures(self, request, credentials: Optional[dict] = None) -> int: return 0 diff --git a/axes/helpers.py b/axes/helpers.py index 3061614..18a3aa8 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -90,7 +90,7 @@ def get_cool_off_iso8601(delta: timedelta) -> str: return f"P{days_str}" -def get_credentials(username: str = None, **kwargs) -> dict: +def get_credentials(username: Optional[str] = None, **kwargs) -> dict: """ Calculate credentials for Axes to use internally from given username and kwargs. @@ -103,7 +103,7 @@ def get_credentials(username: str = None, **kwargs) -> dict: return credentials -def get_client_username(request, credentials: dict = None) -> str: +def get_client_username(request, credentials: Optional[dict] = None) -> str: """ Resolve client username from the given request or credentials if supplied. @@ -218,7 +218,7 @@ def make_cache_key_list(filter_kwargs_list): def get_client_cache_key( - request_or_attempt: Union[HttpRequest, AccessBase], credentials: dict = None + request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None ) -> str: """ Build cache key name from request or AccessAttempt object. @@ -360,7 +360,7 @@ def get_lockout_message() -> str: return settings.AXES_PERMALOCK_MESSAGE -def get_lockout_response(request, credentials: dict = None) -> HttpResponse: +def get_lockout_response(request, credentials: Optional[dict] = None) -> HttpResponse: if settings.AXES_LOCKOUT_CALLABLE: if callable(settings.AXES_LOCKOUT_CALLABLE): return settings.AXES_LOCKOUT_CALLABLE( # pylint: disable=not-callable @@ -475,7 +475,7 @@ def is_client_method_whitelisted(request) -> bool: return False -def is_user_attempt_whitelisted(request, credentials: dict = None) -> bool: +def is_user_attempt_whitelisted(request, credentials: Optional[dict] = None) -> bool: """ Check if the given request or credentials refer to a whitelisted username. diff --git a/axes/utils.py b/axes/utils.py index 2cdd9d7..824fc9c 100644 --- a/axes/utils.py +++ b/axes/utils.py @@ -17,7 +17,7 @@ from axes.helpers import get_client_ip_address log = getLogger(__name__) -def reset(ip: str = None, username: str = None, ip_or_username=False) -> int: +def reset(ip: Optional[str] = None, username: Optional[str] = None, ip_or_username=False) -> int: """ Reset records that match IP or username, and return the count of removed attempts. From dc98a7b2e39080a9de05a7659dab289f86edb945 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Mon, 11 Apr 2022 11:52:37 +0200 Subject: [PATCH 061/372] Allow float values for AXES_COOLOFF_TIME(#868). --- axes/helpers.py | 2 ++ docs/4_configuration.rst | 7 +++++-- tests/test_helpers.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index 18a3aa8..a6466f7 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -59,6 +59,8 @@ def get_cool_off() -> Optional[timedelta]: if isinstance(cool_off, int): return timedelta(hours=cool_off) + if isinstance(cool_off, float): + return timedelta(minutes=cool_off * 60) if isinstance(cool_off, str): return import_string(cool_off)() if callable(cool_off): diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index 8d2b552..a021775 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -28,9 +28,12 @@ The following ``settings.py`` options are available for customizing Axes behavio Default: ``True`` * ``AXES_COOLOFF_TIME``: If set, defines a period of inactivity after which old failed login attempts will be cleared. - Can be set to a Python timedelta object, an integer, a callable, + Can be set to a Python timedelta object, an integer, a float, a callable, or a string path to a callable which takes no arguments. - If an integer, will be interpreted as a number of hours. + If an integer or float, will be interpreted as a number of hours: + ``AXES_COOLOFF_TIME = 2`` 2 hours + ``AXES_COOLOFF_TIME = 2.0`` 2 hours, 120 minutes + ``AXES_COOLOFF_TIME = 1.7`` 1.7 houts, 102 minutes, 6120 seconds Default: ``None`` * ``AXES_ONLY_ADMIN_SITE``: If ``True``, lock is only enabled for admin site. Admin site is determined by checking request path against the path of ``"admin:index"`` view. diff --git a/tests/test_helpers.py b/tests/test_helpers.py index e17937c..f17bee0 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -670,6 +670,18 @@ class AxesCoolOffTestCase(AxesTestCase): def test_get_cool_off_int(self): self.assertEqual(get_cool_off(), timedelta(hours=2)) + @override_settings(AXES_COOLOFF_TIME=2.0) + def test_get_cool_off_int(self): + self.assertEqual(get_cool_off(), timedelta(minutes=120)) + + @override_settings(AXES_COOLOFF_TIME=0.25) + def test_get_cool_off_int(self): + self.assertEqual(get_cool_off(), timedelta(minutes=15)) + + @override_settings(AXES_COOLOFF_TIME=1.7) + def test_get_cool_off_int(self): + self.assertEqual(get_cool_off(), timedelta(seconds=6120)) + @override_settings(AXES_COOLOFF_TIME=lambda: timedelta(seconds=30)) def test_get_cool_off_callable(self): self.assertEqual(get_cool_off(), timedelta(seconds=30)) From ada98869d8b8a6dfdc6ca210a3631ee7aed31d48 Mon Sep 17 00:00:00 2001 From: Tato Moaki Date: Wed, 20 Apr 2022 10:59:59 +0200 Subject: [PATCH 062/372] Fix typo in documentation --- docs/4_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index a021775..185837c 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -33,7 +33,7 @@ The following ``settings.py`` options are available for customizing Axes behavio If an integer or float, will be interpreted as a number of hours: ``AXES_COOLOFF_TIME = 2`` 2 hours ``AXES_COOLOFF_TIME = 2.0`` 2 hours, 120 minutes - ``AXES_COOLOFF_TIME = 1.7`` 1.7 houts, 102 minutes, 6120 seconds + ``AXES_COOLOFF_TIME = 1.7`` 1.7 hours, 102 minutes, 6120 seconds Default: ``None`` * ``AXES_ONLY_ADMIN_SITE``: If ``True``, lock is only enabled for admin site. Admin site is determined by checking request path against the path of ``"admin:index"`` view. From a0cdd9ca426093a2b8d470c02be2be581ffe7c19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 12:09:20 +0000 Subject: [PATCH 063/372] Bump pytest from 7.1.1 to 7.1.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.1 to 7.1.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.1.1...7.1.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 6657e36..da52f2f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.3.2 -pytest==7.1.1 +pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.7.0 From f6355028f58cef7626ecc4d50770475effa8e8e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 12:07:39 +0000 Subject: [PATCH 064/372] Bump mypy from 0.942 to 0.950 Bumps [mypy](https://github.com/python/mypy) from 0.942 to 0.950. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.942...v0.950) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index a920369..5688460 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.3.0 -mypy==0.942 +mypy==0.950 prospector==1.7.7 types-pkg_resources # Type stub From 382468cef4629e054bc2a6027a1b89930fd42bdb Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 26 Apr 2022 15:09:10 +0200 Subject: [PATCH 065/372] Add type hint for request variables --- axes/admin.py | 7 ++++--- axes/attempts.py | 7 ++++--- axes/backends.py | 3 ++- axes/helpers.py | 22 +++++++++++----------- axes/middleware.py | 5 +++-- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/axes/admin.py b/axes/admin.py index 93f3301..5f8a9c4 100644 --- a/axes/admin.py +++ b/axes/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django.http import HttpRequest from django.utils.translation import gettext_lazy as _ from axes.conf import settings @@ -39,7 +40,7 @@ class AccessAttemptAdmin(admin.ModelAdmin): "failures_since_start", ] - def has_add_permission(self, request): + def has_add_permission(self, request: HttpRequest) -> bool: return False @@ -74,7 +75,7 @@ class AccessLogAdmin(admin.ModelAdmin): "logout_time", ] - def has_add_permission(self, request): + def has_add_permission(self, request: HttpRequest) -> bool: return False @@ -109,7 +110,7 @@ class AccessFailureLogAdmin(admin.ModelAdmin): "locked_out", ] - def has_add_permission(self, request): + def has_add_permission(self, request: HttpRequest) -> bool: return False diff --git a/axes/attempts.py b/axes/attempts.py index 9ab40be..ee057fa 100644 --- a/axes/attempts.py +++ b/axes/attempts.py @@ -2,6 +2,7 @@ from logging import getLogger from typing import List, Optional from django.db.models import QuerySet +from django.http import HttpRequest from django.utils.timezone import datetime, now from axes.conf import settings @@ -27,7 +28,7 @@ def get_cool_off_threshold(attempt_time: Optional[datetime] = None) -> datetime: return attempt_time - cool_off -def filter_user_attempts(request, credentials: Optional[dict] = None) -> List[QuerySet]: +def filter_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> List[QuerySet]: """ Return a list querysets of AccessAttempts that match the given request and credentials. """ @@ -44,7 +45,7 @@ def filter_user_attempts(request, credentials: Optional[dict] = None) -> List[Qu return attempts_list -def get_user_attempts(request, credentials: Optional[dict] = None) -> List[QuerySet]: +def get_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> List[QuerySet]: """ Get list of querysets with valid user attempts that match the given request and credentials. """ @@ -83,7 +84,7 @@ def clean_expired_user_attempts(attempt_time: Optional[datetime] = None) -> int: return count -def reset_user_attempts(request, credentials: Optional[dict] = None) -> int: +def reset_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> int: """ Reset all user attempts that match the given request and credentials. """ diff --git a/axes/backends.py b/axes/backends.py index 30535f3..b8186cb 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -1,6 +1,7 @@ from typing import Optional from django.conf import settings from django.contrib.auth.backends import ModelBackend +from django.http import HttpRequest from axes.exceptions import ( AxesBackendPermissionDenied, @@ -23,7 +24,7 @@ class AxesBackend(ModelBackend): @toggleable def authenticate( - self, request, username: Optional[str] = None, password: Optional[str] = None, **kwargs: dict + self, request: HttpRequest, username: Optional[str] = None, password: Optional[str] = None, **kwargs: dict ): """ Checks user lockout status and raises an exception if user is not allowed to log in. diff --git a/axes/helpers.py b/axes/helpers.py index a6466f7..e85c8d7 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -105,7 +105,7 @@ def get_credentials(username: Optional[str] = None, **kwargs) -> dict: return credentials -def get_client_username(request, credentials: Optional[dict] = None) -> str: +def get_client_username(request: HttpRequest, credentials: Optional[dict] = None) -> str: """ Resolve client username from the given request or credentials if supplied. @@ -146,7 +146,7 @@ def get_client_username(request, credentials: Optional[dict] = None) -> str: return request_data.get(settings.AXES_USERNAME_FORM_FIELD, None) -def get_client_ip_address(request) -> str: +def get_client_ip_address(request: HttpRequest) -> str: """ Get client IP address as configured by the user. @@ -165,15 +165,15 @@ def get_client_ip_address(request) -> str: return client_ip_address -def get_client_user_agent(request) -> str: +def get_client_user_agent(request: HttpRequest) -> str: return request.META.get("HTTP_USER_AGENT", "")[:255] -def get_client_path_info(request) -> str: +def get_client_path_info(request: HttpRequest) -> str: return request.META.get("PATH_INFO", "")[:255] -def get_client_http_accept(request) -> str: +def get_client_http_accept(request: HttpRequest) -> str: return request.META.get("HTTP_ACCEPT", "")[:1025] @@ -344,7 +344,7 @@ def get_query_str(query: Type[QueryDict], max_length: int = 1024) -> str: return query_str[:max_length] -def get_failure_limit(request, credentials) -> int: +def get_failure_limit(request: HttpRequest, credentials) -> int: if callable(settings.AXES_FAILURE_LIMIT): return settings.AXES_FAILURE_LIMIT( # pylint: disable=not-callable request, credentials @@ -362,7 +362,7 @@ def get_lockout_message() -> str: return settings.AXES_PERMALOCK_MESSAGE -def get_lockout_response(request, credentials: Optional[dict] = None) -> HttpResponse: +def get_lockout_response(request: HttpRequest, credentials: Optional[dict] = None) -> HttpResponse: if settings.AXES_LOCKOUT_CALLABLE: if callable(settings.AXES_LOCKOUT_CALLABLE): return settings.AXES_LOCKOUT_CALLABLE( # pylint: disable=not-callable @@ -432,7 +432,7 @@ def is_ip_address_in_blacklist(ip_address: str) -> bool: ) -def is_client_ip_address_whitelisted(request): +def is_client_ip_address_whitelisted(request: HttpRequest): """ Check if the given request refers to a whitelisted IP. """ @@ -450,7 +450,7 @@ def is_client_ip_address_whitelisted(request): return False -def is_client_ip_address_blacklisted(request) -> bool: +def is_client_ip_address_blacklisted(request: HttpRequest) -> bool: """ Check if the given request refers to a blacklisted IP. """ @@ -466,7 +466,7 @@ def is_client_ip_address_blacklisted(request) -> bool: return False -def is_client_method_whitelisted(request) -> bool: +def is_client_method_whitelisted(request: HttpRequest) -> bool: """ Check if the given request uses a whitelisted method. """ @@ -477,7 +477,7 @@ def is_client_method_whitelisted(request) -> bool: return False -def is_user_attempt_whitelisted(request, credentials: Optional[dict] = None) -> bool: +def is_user_attempt_whitelisted(request: HttpRequest, credentials: Optional[dict] = None) -> bool: """ Check if the given request or credentials refer to a whitelisted username. diff --git a/axes/middleware.py b/axes/middleware.py index 45fea61..1f70ce9 100644 --- a/axes/middleware.py +++ b/axes/middleware.py @@ -1,6 +1,7 @@ from typing import Callable from django.conf import settings +from django.http import HttpRequest, HttpResponse from axes.helpers import get_lockout_response @@ -29,10 +30,10 @@ class AxesMiddleware: - ``AXES_PERMALOCK_MESSAGE``. """ - def __init__(self, get_response: Callable): + def __init__(self, get_response: Callable) -> None: self.get_response = get_response - def __call__(self, request): + def __call__(self, request: HttpRequest) -> HttpResponse: response = self.get_response(request) if settings.AXES_ENABLED: From 1dfcb233b4a90d2445a7d700ae35f827f9f4f718 Mon Sep 17 00:00:00 2001 From: neil <42328488+neilnaveen@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:23:50 -0500 Subject: [PATCH 066/372] chore: Included githubactions in the dependabot config This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: neil <42328488+neilnaveen@users.noreply.github.com> --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2ba83e6..54d834a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,9 @@ updates: interval: "daily" time: "12:00" open-pull-requests-limit: 10 +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + time: "12:00" + open-pull-requests-limit: 10 From c5af43bd1d2ca53fb8b8a7c213b2fdae9cfe28f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 15:23:07 +0000 Subject: [PATCH 067/372] Bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e43c1f2..b782d60 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: echo "::set-output name=dir::$(pip cache dir)" - name: Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: From 214b1969c6021303ab334ba6e2f93697cba9d91b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 15:23:09 +0000 Subject: [PATCH 068/372] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f87cedd..599e5fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b782d60..bae90df 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: django-version: 'main' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 From 7bedbaf1aaab0570a030162d9277795f53cfcda4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 15:23:13 +0000 Subject: [PATCH 069/372] Bump codecov/codecov-action from 1 to 3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bae90df..e64b974 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,6 @@ jobs: DJANGO: ${{ matrix.django-version }} - name: Upload coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: name: Python ${{ matrix.python-version }} From 6ccff1ff34ec97e31b821f8d1393be8d551a9222 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 15:23:17 +0000 Subject: [PATCH 070/372] Bump actions/setup-python from 2 to 3 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 599e5fd..a023e15 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: 3.8 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e64b974..6b4b2d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} From 038dc7cd97d14f3b935457890bb5976516354189 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Fri, 8 Apr 2022 21:47:40 +0000 Subject: [PATCH 071/372] Set permissions for GitHub actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/release.yml | 3 +++ .github/workflows/test.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a023e15..619ad49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: tags: - '*' +permissions: + contents: read + jobs: build: if: github.repository == 'jazzband/django-axes' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b4b2d5..4f38195 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,9 @@ name: Test on: [push, pull_request] +permissions: + contents: read + jobs: build: name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) From 012fde1caf35b17ac1eb1fe3f61cc5ea2b19cdbd Mon Sep 17 00:00:00 2001 From: Shaheed Haque Date: Sat, 7 May 2022 19:12:32 +0100 Subject: [PATCH 072/372] Refine and streamline startup log. The new output is on one line and looks like this: AXES: BEGIN version 5.32.1.dev14+g038dc7c.d20220507, blocking by IP only Resolves #884. --- axes/apps.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/axes/apps.py b/axes/apps.py index 52737ba..0f8413a 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -30,20 +30,15 @@ class AppConfig(apps.AppConfig): # Skip startup log messages if Axes is not set to verbose if settings.AXES_VERBOSE: - log.info("AXES: BEGIN LOG") - log.info( - "AXES: Using django-axes version %s", - get_distribution("django-axes").version, - ) - if settings.AXES_ONLY_USER_FAILURES: - log.info("AXES: blocking by username only.") + mode = "blocking by username only" elif settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP: - log.info("AXES: blocking by combination of username and IP.") + mode = "blocking by combination of username and IP" elif settings.AXES_LOCK_OUT_BY_USER_OR_IP: - log.info("AXES: blocking by username or IP.") + mode = "blocking by username or IP" else: - log.info("AXES: blocking by IP only.") + mode = "blocking by IP only" + log.info("AXES: BEGIN version %s, %s", get_distribution("django-axes").version, mode) def ready(self): self.initialize() From 99e10cb7149c985e772fa88b54da0f83088023f7 Mon Sep 17 00:00:00 2001 From: Shaheed Haque Date: Mon, 9 May 2022 15:23:37 +0100 Subject: [PATCH 073/372] Update expected test results. --- tests/test_logging.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_logging.py b/tests/test_logging.py index 5240f95..35ae52e 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -2,11 +2,15 @@ from unittest.mock import patch from django.test import override_settings from django.urls import reverse +from pkg_resources import get_distribution from axes.apps import AppConfig from axes.models import AccessAttempt, AccessLog from tests.base import AxesTestCase +_BEGIN = "AXES: BEGIN version %s, %s" +_VERSION = get_distribution("django-axes").version + @patch("axes.apps.AppConfig.initialized", False) @patch("axes.apps.log") @@ -33,22 +37,22 @@ class AppsTestCase(AxesTestCase): @override_settings(AXES_ONLY_USER_FAILURES=True) def test_axes_config_log_user_only(self, log): AppConfig.initialize() - log.info.assert_called_with("AXES: blocking by username only.") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username only") @override_settings(AXES_ONLY_USER_FAILURES=False) def test_axes_config_log_ip_only(self, log): AppConfig.initialize() - log.info.assert_called_with("AXES: blocking by IP only.") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by IP only") @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) def test_axes_config_log_user_ip(self, log): AppConfig.initialize() - log.info.assert_called_with("AXES: blocking by combination of username and IP.") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by combination of username and IP") @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) def test_axes_config_log_user_or_ip(self, log): AppConfig.initialize() - log.info.assert_called_with("AXES: blocking by username or IP.") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username or IP") class AccessLogTestCase(AxesTestCase): From 2784b79475ba7769530cee82debc3ea5f1a2e6ce Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 13 May 2022 19:21:14 +0200 Subject: [PATCH 074/372] Replace old-style PyPy config key. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2ff2e5b..fd32ec4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ python = 3.8: py38 3.9: py39 3.10: py310 - pypy3: pypy3 + pypy-3: pypy3 [gh-actions:env] DJANGO = From b7a93b42343bc6ddd3f18b71e0f94b1f49b427f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 May 2022 12:07:56 +0000 Subject: [PATCH 075/372] Bump coverage from 6.3.2 to 6.3.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.3.2 to 6.3.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.3.2...6.3.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index da52f2f..1440549 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3.2 +coverage==6.3.3 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 68a4827870ff778b0c54beef1aad8a5d69dcdfd8 Mon Sep 17 00:00:00 2001 From: naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Fri, 13 May 2022 21:28:48 +0000 Subject: [PATCH 076/372] chore: Enable codeql action This action runs GitHub's industry-leading semantic code analysis engine, CodeQL, against a repository's source code to find security vulnerabilities. https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql https://github.com/ossf/scorecard/blob/main/docs/checks.md#sast Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/codeql.yml | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..8958a0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,43 @@ +name: "Code Scanning - Action" + +on: + pull_request: + +jobs: + CodeQL-Build: + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest + runs-on: ubuntu-latest + + permissions: + # required for all workflows + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below). + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # ✏️ If the Autobuild fails above, remove it and uncomment the following + # three lines and modify them (or add more) to build your code if your + # project uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From 44900131113aff331a8a79609f093dc5e19a9ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 15 May 2022 15:07:18 +0300 Subject: [PATCH 077/372] Migrate MD5 hashing to SHA256 Continue using MD5 hashing in Axes test settings as it offers better performance for test runs without compromising security for users --- axes/helpers.py | 4 ++-- tests/settings.py | 3 +++ tests/test_helpers.py | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index e85c8d7..577112f 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -1,5 +1,5 @@ from datetime import timedelta -from hashlib import md5 +from hashlib import sha256 from logging import getLogger from string import Template from typing import Callable, Optional, Type, Union @@ -214,7 +214,7 @@ def make_cache_key_list(filter_kwargs_list): cache_key_components = "".join( value for value in filter_kwargs.values() if value ) - cache_key_digest = md5(cache_key_components.encode()).hexdigest() + cache_key_digest = sha256(cache_key_components.encode()).hexdigest() cache_keys.append(f"axes-{cache_key_digest}") return cache_keys diff --git a/tests/settings.py b/tests/settings.py index 43de0b4..949aad8 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -24,6 +24,9 @@ AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", ] +# Use MD5 for tests as it is considerably faster than other options +# note that this should never be used in any online setting +# where users actually log in to the system due to easy exploitability PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] ROOT_URLCONF = "tests.urls" diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f17bee0..30a216d 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,5 +1,5 @@ from datetime import timedelta -from hashlib import md5 +from hashlib import sha256 from unittest.mock import patch from django.contrib.auth import get_user_model @@ -353,7 +353,7 @@ class ClientCacheKeyTestCase(AxesTestCase): Test the cache key format. """ - cache_hash_digest = md5(self.ip_address.encode()).hexdigest() + cache_hash_digest = sha256(self.ip_address.encode()).hexdigest() cache_hash_key = f"axes-{cache_hash_digest}" # Getting cache key from request @@ -385,7 +385,7 @@ class ClientCacheKeyTestCase(AxesTestCase): empty_ip_address = "" - cache_hash_digest = md5(empty_ip_address.encode()).hexdigest() + cache_hash_digest = sha256(empty_ip_address.encode()).hexdigest() cache_hash_key = f"axes-{cache_hash_digest}" # Getting cache key from request @@ -418,7 +418,7 @@ class ClientCacheKeyTestCase(AxesTestCase): """ ip_address = self.ip_address - cache_hash_digest = md5(ip_address.encode()).hexdigest() + cache_hash_digest = sha256(ip_address.encode()).hexdigest() cache_hash_key = f"axes-{cache_hash_digest}" # Getting cache key from request From 67189a48c9b628e11dd0a72dfd96311fe5b7286a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 15 May 2022 15:30:26 +0300 Subject: [PATCH 078/372] Fix Python QA version 3.10 is used for QA runs in tox configuration --- .github/workflows/test.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f38195..52bdd55 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: django-version: ['3.2', '4.0', 'main'] include: # Tox configuration for QA environment - - python-version: '3.8' + - python-version: '3.10' django-version: 'qa' # Django main - python-version: '3.8' diff --git a/pyproject.toml b/pyproject.toml index fd32ec4..e2e9bd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ envlist = py{37,38,39,310,py3}-dj32 py{38,39,310,py3}-dj40 py{38,39,310}-djmain - py38-djqa + py310-djqa [gh-actions] python = From 54310f12bafe7f7db8025896a1e14feb4eb68223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 15 May 2022 15:24:47 +0300 Subject: [PATCH 079/372] Adjust version matrix for testing Release tight PyPy version pinning Drop Django upstrema main version from wide build matrix as it is already overspecified in the narrow inclusion matrix --- .github/workflows/test.yml | 4 ++-- pyproject.toml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52bdd55..82b5fdd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,8 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8-v7.3.7'] - django-version: ['3.2', '4.0', 'main'] + python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] + django-version: ['3.2', '4.0'] include: # Tox configuration for QA environment - python-version: '3.10' diff --git a/pyproject.toml b/pyproject.toml index e2e9bd5..1deebea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,9 +10,9 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{37,38,39,310,py3}-dj32 - py{38,39,310,py3}-dj40 - py{38,39,310}-djmain + py{37,38,39,310,py38}-dj32 + py{38,39,310,py38}-dj40 + py{38,39,310,py38}-djmain py310-djqa [gh-actions] @@ -21,7 +21,7 @@ python = 3.8: py38 3.9: py39 3.10: py310 - pypy-3: pypy3 + pypy-3.8: pypy38 [gh-actions:env] DJANGO = From 55a78313b9bbeb9fd23dc3c30b20fc4492484ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 15 May 2022 15:12:31 +0300 Subject: [PATCH 080/372] Use compatible coverage version GitHub PyPy version 7.3.7 needs 6.3.2 at the moment as 6.3.3 is only available for newer PyPy version 7.3.8 --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1440549..da52f2f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3.3 +coverage==6.3.2 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 21898c131c6aadc47e1f1827de690879e91aa3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 16 May 2022 10:12:05 +0300 Subject: [PATCH 081/372] Rename pep8 to pycodestyle --- .prospector.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.prospector.yaml b/.prospector.yaml index 9020238..219f6ac 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -2,6 +2,6 @@ ignore-paths: - docs - axes/migrations -pep8: +pycodestyle: options: max-line-length: 142 From fffb539c32f03794d131e3fb6e4a9364f0c0a1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 16 May 2022 10:12:31 +0300 Subject: [PATCH 082/372] Fix proxy handler false negative errors for pylint --- axes/handlers/proxy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/axes/handlers/proxy.py b/axes/handlers/proxy.py index 1adebe1..1582f83 100644 --- a/axes/handlers/proxy.py +++ b/axes/handlers/proxy.py @@ -1,3 +1,6 @@ +# pylint: disable=arguments-differ +# pylint generates false negatives from proxy class method overrides + from logging import getLogger from typing import Optional From 621dfa68822ac92c93db25d16aca6cbab4ca4596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 16 May 2022 10:31:46 +0300 Subject: [PATCH 083/372] Fix code formatting --- axes/apps.py | 6 ++- axes/attempts.py | 12 +++-- axes/backends.py | 6 ++- axes/conf.py | 8 ++- axes/handlers/base.py | 6 ++- axes/handlers/database.py | 10 ++-- axes/handlers/proxy.py | 8 ++- axes/helpers.py | 15 ++++-- axes/migrations/0008_accessfailurelog.py | 63 +++++++++++++++++++----- axes/models.py | 6 ++- axes/utils.py | 4 +- 11 files changed, 109 insertions(+), 35 deletions(-) diff --git a/axes/apps.py b/axes/apps.py index 0f8413a..5ef2612 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -38,7 +38,11 @@ class AppConfig(apps.AppConfig): mode = "blocking by username or IP" else: mode = "blocking by IP only" - log.info("AXES: BEGIN version %s, %s", get_distribution("django-axes").version, mode) + log.info( + "AXES: BEGIN version %s, %s", + get_distribution("django-axes").version, + mode, + ) def ready(self): self.initialize() diff --git a/axes/attempts.py b/axes/attempts.py index ee057fa..fd6d552 100644 --- a/axes/attempts.py +++ b/axes/attempts.py @@ -28,7 +28,9 @@ def get_cool_off_threshold(attempt_time: Optional[datetime] = None) -> datetime: return attempt_time - cool_off -def filter_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> List[QuerySet]: +def filter_user_attempts( + request: HttpRequest, credentials: Optional[dict] = None +) -> List[QuerySet]: """ Return a list querysets of AccessAttempts that match the given request and credentials. """ @@ -45,7 +47,9 @@ def filter_user_attempts(request: HttpRequest, credentials: Optional[dict] = Non return attempts_list -def get_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> List[QuerySet]: +def get_user_attempts( + request: HttpRequest, credentials: Optional[dict] = None +) -> List[QuerySet]: """ Get list of querysets with valid user attempts that match the given request and credentials. """ @@ -84,7 +88,9 @@ def clean_expired_user_attempts(attempt_time: Optional[datetime] = None) -> int: return count -def reset_user_attempts(request: HttpRequest, credentials: Optional[dict] = None) -> int: +def reset_user_attempts( + request: HttpRequest, credentials: Optional[dict] = None +) -> int: """ Reset all user attempts that match the given request and credentials. """ diff --git a/axes/backends.py b/axes/backends.py index b8186cb..e077e29 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -24,7 +24,11 @@ class AxesBackend(ModelBackend): @toggleable def authenticate( - self, request: HttpRequest, username: Optional[str] = None, password: Optional[str] = None, **kwargs: dict + self, + request: HttpRequest, + username: Optional[str] = None, + password: Optional[str] = None, + **kwargs: dict, ): """ Checks user lockout status and raises an exception if user is not allowed to log in. diff --git a/axes/conf.py b/axes/conf.py index ad7ccbe..745024f 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -57,9 +57,13 @@ settings.AXES_RESET_ON_SUCCESS = getattr(settings, "AXES_RESET_ON_SUCCESS", Fals settings.AXES_DISABLE_ACCESS_LOG = getattr(settings, "AXES_DISABLE_ACCESS_LOG", False) -settings.AXES_ENABLE_ACCESS_FAILURE_LOG = getattr(settings, "AXES_ENABLE_ACCESS_FAILURE_LOG", False) +settings.AXES_ENABLE_ACCESS_FAILURE_LOG = getattr( + settings, "AXES_ENABLE_ACCESS_FAILURE_LOG", False +) -settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT = getattr(settings, "AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT", 1000) +settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT = getattr( + settings, "AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT", 1000 +) settings.AXES_HANDLER = getattr( settings, "AXES_HANDLER", "axes.handlers.database.AxesDatabaseHandler" diff --git a/axes/handlers/base.py b/axes/handlers/base.py index f2643c4..92b50b1 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -186,8 +186,9 @@ class AxesBaseHandler: # pylint: disable=unused-argument """ return 0 - - def remove_out_of_limit_failure_logs(self, *, username: str, limit: Optional[int] = None) -> int: + def remove_out_of_limit_failure_logs( + self, *, username: str, limit: Optional[int] = None + ) -> int: """Remove access failure logs that are over AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT for user username. @@ -199,6 +200,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument """ return 0 + class AxesHandler(AbstractAxesHandler, AxesBaseHandler): """ Signal bare handler implementation without any storage backend. diff --git a/axes/handlers/database.py b/axes/handlers/database.py index 5ac0843..4e4dc67 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -87,10 +87,11 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return count def remove_out_of_limit_failure_logs( - self, - *, - username: str, - limit: Optional[int] = settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT) -> int: + self, + *, + username: str, + limit: Optional[int] = settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT, + ) -> int: count = 0 failures = AccessFailureLog.objects.filter(username=username) out_of_limit_failures_logs = failures.count() - limit @@ -252,7 +253,6 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): ) self.remove_out_of_limit_failure_logs(username=username) - def user_logged_in(self, sender, request, user, **kwargs): """ When user logs in, update the AccessLog related to the user. diff --git a/axes/handlers/proxy.py b/axes/handlers/proxy.py index 1582f83..3933013 100644 --- a/axes/handlers/proxy.py +++ b/axes/handlers/proxy.py @@ -67,8 +67,12 @@ class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler): return cls.get_implementation().reset_failure_logs(age_days=age_days) @classmethod - def remove_out_of_limit_failure_logs(cls, *, username: str, limit: Optional[int] = None) -> int: - return cls.get_implementation().remove_out_of_limit_failure_logs(username=username) + def remove_out_of_limit_failure_logs( + cls, *, username: str, limit: Optional[int] = None + ) -> int: + return cls.get_implementation().remove_out_of_limit_failure_logs( + username=username + ) @staticmethod def update_request(request): diff --git a/axes/helpers.py b/axes/helpers.py index 577112f..879a784 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -105,7 +105,9 @@ def get_credentials(username: Optional[str] = None, **kwargs) -> dict: return credentials -def get_client_username(request: HttpRequest, credentials: Optional[dict] = None) -> str: +def get_client_username( + request: HttpRequest, credentials: Optional[dict] = None +) -> str: """ Resolve client username from the given request or credentials if supplied. @@ -220,7 +222,8 @@ def make_cache_key_list(filter_kwargs_list): def get_client_cache_key( - request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None + request_or_attempt: Union[HttpRequest, AccessBase], + credentials: Optional[dict] = None, ) -> str: """ Build cache key name from request or AccessAttempt object. @@ -362,7 +365,9 @@ def get_lockout_message() -> str: return settings.AXES_PERMALOCK_MESSAGE -def get_lockout_response(request: HttpRequest, credentials: Optional[dict] = None) -> HttpResponse: +def get_lockout_response( + request: HttpRequest, credentials: Optional[dict] = None +) -> HttpResponse: if settings.AXES_LOCKOUT_CALLABLE: if callable(settings.AXES_LOCKOUT_CALLABLE): return settings.AXES_LOCKOUT_CALLABLE( # pylint: disable=not-callable @@ -477,7 +482,9 @@ def is_client_method_whitelisted(request: HttpRequest) -> bool: return False -def is_user_attempt_whitelisted(request: HttpRequest, credentials: Optional[dict] = None) -> bool: +def is_user_attempt_whitelisted( + request: HttpRequest, credentials: Optional[dict] = None +) -> bool: """ Check if the given request or credentials refer to a whitelisted username. diff --git a/axes/migrations/0008_accessfailurelog.py b/axes/migrations/0008_accessfailurelog.py index c7956f4..ac7c549 100644 --- a/axes/migrations/0008_accessfailurelog.py +++ b/axes/migrations/0008_accessfailurelog.py @@ -6,25 +6,64 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('axes', '0007_alter_accessattempt_unique_together'), + ("axes", "0007_alter_accessattempt_unique_together"), ] operations = [ migrations.CreateModel( - name='AccessFailureLog', + name="AccessFailureLog", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_agent', models.CharField(db_index=True, max_length=255, verbose_name='User Agent')), - ('ip_address', models.GenericIPAddressField(db_index=True, null=True, verbose_name='IP Address')), - ('username', models.CharField(db_index=True, max_length=255, null=True, verbose_name='Username')), - ('http_accept', models.CharField(max_length=1025, verbose_name='HTTP Accept')), - ('path_info', models.CharField(max_length=255, verbose_name='Path')), - ('attempt_time', models.DateTimeField(auto_now_add=True, verbose_name='Attempt Time')), - ('locked_out', models.BooleanField(blank=True, default=False, verbose_name='Access lock out')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "user_agent", + models.CharField( + db_index=True, max_length=255, verbose_name="User Agent" + ), + ), + ( + "ip_address", + models.GenericIPAddressField( + db_index=True, null=True, verbose_name="IP Address" + ), + ), + ( + "username", + models.CharField( + db_index=True, + max_length=255, + null=True, + verbose_name="Username", + ), + ), + ( + "http_accept", + models.CharField(max_length=1025, verbose_name="HTTP Accept"), + ), + ("path_info", models.CharField(max_length=255, verbose_name="Path")), + ( + "attempt_time", + models.DateTimeField( + auto_now_add=True, verbose_name="Attempt Time" + ), + ), + ( + "locked_out", + models.BooleanField( + blank=True, default=False, verbose_name="Access lock out" + ), + ), ], options={ - 'verbose_name': 'access failure', - 'verbose_name_plural': 'access failures', + "verbose_name": "access failure", + "verbose_name_plural": "access failures", }, ), ] diff --git a/axes/models.py b/axes/models.py index 58e5dbd..1b42141 100644 --- a/axes/models.py +++ b/axes/models.py @@ -22,10 +22,12 @@ class AccessBase(models.Model): class AccessFailureLog(AccessBase): - locked_out = models.BooleanField(_("Access lock out"), null=False, blank=True, default=False) + locked_out = models.BooleanField( + _("Access lock out"), null=False, blank=True, default=False + ) def __str__(self): - locked_out_str = ' locked out' if self.locked_out else '' + locked_out_str = " locked out" if self.locked_out else "" return f"Failed access: user {self.username}{locked_out_str} on {self.attempt_time} from {self.ip_address}" class Meta: diff --git a/axes/utils.py b/axes/utils.py index 824fc9c..55c82ef 100644 --- a/axes/utils.py +++ b/axes/utils.py @@ -17,7 +17,9 @@ from axes.helpers import get_client_ip_address log = getLogger(__name__) -def reset(ip: Optional[str] = None, username: Optional[str] = None, ip_or_username=False) -> int: +def reset( + ip: Optional[str] = None, username: Optional[str] = None, ip_or_username=False +) -> int: """ Reset records that match IP or username, and return the count of removed attempts. From 64aca421b46d9c81f839e2a5e251a53cabfb74c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 16 May 2022 10:40:55 +0300 Subject: [PATCH 084/372] Version 5.33.0 --- CHANGES.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7266689..91c021c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,19 @@ Changes ======= +5.33.0 (2022-05-16) +------------------- + +- Migrate MD5 cache key digests to SHA256. + [aleksihakli] +- Improve and streamline startup logging. + [ShaheedHaque] +- Improve module typing. + [hramezani] +- Add support for float or partial hours for ``AXES_COOLOFF_TIME``. + [hramezani] + + 5.32.0 (2022-04-08) ------------------- @@ -213,7 +226,7 @@ Changes - Add ``DEFAULT_AUTO_FIELD`` to test settings. [hramezani] - Fix documentation language. - [danielquinn] + [danielquinn] - Fix Python package version specifiers and remove redundant imports. [aleksihakli] From 77108939d3a93deecd9587977f510ed186bca31b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 12:07:14 +0000 Subject: [PATCH 085/372] Bump coverage from 6.3.2 to 6.3.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.3.2 to 6.3.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.3.2...6.3.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index da52f2f..1440549 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3.2 +coverage==6.3.3 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 26cf73585e11ad4778ed4883e70e34aa270718c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 May 2022 12:10:43 +0000 Subject: [PATCH 086/372] Bump coverage from 6.3.3 to 6.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.3.3 to 6.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.3.3...6.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1440549..f2eb202 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.3.3 +coverage==6.4 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 8eaffafb6eccd0b0de1533b9c5cd062957ff2ef7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 May 2022 12:07:23 +0000 Subject: [PATCH 087/372] Bump pytest-subtests from 0.7.0 to 0.8.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.7.0 to 0.8.0. - [Release notes](https://github.com/pytest-dev/pytest-subtests/releases) - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.7.0...0.8.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f2eb202..77ac4cd 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ coverage==6.4 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 -pytest-subtests==0.7.0 +pytest-subtests==0.8.0 From a21a8848e7531521ce98910424a920de98edc2c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 May 2022 12:07:17 +0000 Subject: [PATCH 088/372] Bump mypy from 0.950 to 0.960 Bumps [mypy](https://github.com/python/mypy) from 0.950 to 0.960. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.950...v0.960) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5688460..9fd3485 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.3.0 -mypy==0.950 +mypy==0.960 prospector==1.7.7 types-pkg_resources # Type stub From ec7c54f9c59be7348102753a3190a882bf4c097c Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Fri, 27 May 2022 11:55:04 +0200 Subject: [PATCH 089/372] #892: German translation update --- axes/handlers/base.py | 8 ++-- axes/locale/de/LC_MESSAGES/django.mo | Bin 1549 -> 1711 bytes axes/locale/de/LC_MESSAGES/django.po | 55 +++++++++++++++++---------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/axes/handlers/base.py b/axes/handlers/base.py index 92b50b1..8c192ed 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -158,7 +158,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument Resets access attempts that match the given IP address or username. This method makes more sense for the DB backend, but as it is used by the ProxyHandler - (via inherent), it needs to be defined here so we get compliant with all proxy methods. + (via inherent), it needs to be defined here, so we get compliant with all proxy methods. Please overwrite it on each specialized handler as needed. """ @@ -169,7 +169,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument Resets access logs that are older than given number of days. This method makes more sense for the DB backend, but as it is used by the ProxyHandler - (via inherent), it needs to be defined here so we get compliant with all proxy methods. + (via inherent), it needs to be defined here, so we get compliant with all proxy methods. Please overwrite it on each specialized handler as needed. """ @@ -180,7 +180,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument Resets access failure logs that are older than given number of days. This method makes more sense for the DB backend, but as it is used by the ProxyHandler - (via inherent), it needs to be defined here so we get compliant with all proxy methods. + (via inherent), it needs to be defined here, so we get compliant with all proxy methods. Please overwrite it on each specialized handler as needed. """ @@ -193,7 +193,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT for user username. This method makes more sense for the DB backend, but as it is used by the ProxyHandler - (via inherent), it needs to be defined here so we get compliant with all proxy methods. + (via inherent), it needs to be defined here, so we get compliant with all proxy methods. Please overwrite it on each specialized handler as needed. diff --git a/axes/locale/de/LC_MESSAGES/django.mo b/axes/locale/de/LC_MESSAGES/django.mo index 807c36d487dacadff1a696117d983d615a3fdce8..f9b4d3eb74c872dceafbcade37683accc2ebeb9d 100644 GIT binary patch delta 690 zcmZ9}u}d3a7{~GVV$zzJ)Hb%MLyOlgg@TqYE-spa2rdZ;4mut2atR(L+$EbOe?gbV zc5|>}mk61}%~>qC>Cma2+m1yLzrV``#223XyzjlwJ@0dOpR$8o?`J;w#V`uQ3*wA; zN$go%7=LjZ|6v)egfYul!7T1$4i7Pn1DwQToWfJg;CZ~hMCJV+*)xBbB)Rb)m3V_H zke)E+5#~`1yo%TFjw5_*VAL>-p2U8YvP2^Z+Um;p}7Sol4PI& delta 534 zcmYk(ze@sP9LMpmcUo5354WJm$t8|X#ViRLY>S{E5E=ppH8Ya*Y6@%&qQ#@l#i>Q; zheTUbG`1A9)zH||&>ztIYbW%D=XpJk_x$*rcNIPlHQpos3!x&kDO!g%O\n" "Language-Team: LANGUAGE \n" @@ -18,21 +18,21 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: axes/admin.py:38 +#: .\axes\admin.py:27 msgid "Form Data" msgstr "Form-Daten" -#: axes/admin.py:41 axes/admin.py:95 +#: .\axes\admin.py:28 .\axes\admin.py:65 .\axes\admin.py:100 msgid "Meta Data" msgstr "Meta-Daten" -#: axes/conf.py:58 +#: .\axes\conf.py:97 msgid "Account locked: too many login attempts. Please try again later." msgstr "" "Zugang gesperrt: zu viele fehlgeschlagene Anmeldeversuche. Bitte versuchen " "Sie es später erneut." -#: axes/conf.py:61 +#: .\axes\conf.py:105 msgid "" "Account locked: too many login attempts. Contact an admin to unlock your " "account." @@ -40,58 +40,73 @@ msgstr "" "Zugang gesperrt: zu viele fehlgeschlagene Anmeldeversuche. Kontaktieren Sie " "einen Administrator, um Ihren Zugang zu entsperren." -#: axes/models.py:9 +#: .\axes\models.py:6 msgid "User Agent" msgstr "Browserkennung" -#: axes/models.py:15 +#: .\axes\models.py:8 msgid "IP Address" msgstr "IP-Adresse" -#: axes/models.py:21 +#: .\axes\models.py:10 msgid "Username" msgstr "Benutzername" -#: axes/models.py:35 +#: .\axes\models.py:12 msgid "HTTP Accept" -msgstr "" +msgstr "HTTP-Accept" -#: axes/models.py:40 +#: .\axes\models.py:14 msgid "Path" msgstr "Pfad" -#: axes/models.py:45 +#: .\axes\models.py:16 msgid "Attempt Time" msgstr "Zugriffszeitpunkt" -#: axes/models.py:57 +#: .\axes\models.py:26 +#| msgid "access log" +msgid "Access lock out" +msgstr "Zugriff gesperrt" + +#: .\axes\models.py:34 +#| msgid "access log" +msgid "access failure" +msgstr "Fehlgeschlagener Zugriff" + +#: .\axes\models.py:35 +#| msgid "access logs" +msgid "access failures" +msgstr "Fehlgeschlagene Zugriffe" + +#: .\axes\models.py:39 msgid "GET Data" msgstr "GET-Daten" -#: axes/models.py:61 +#: .\axes\models.py:41 msgid "POST Data" msgstr "POST-Daten" -#: axes/models.py:65 +#: .\axes\models.py:43 msgid "Failed Logins" msgstr "Fehlgeschlagene Anmeldeversuche" -#: axes/models.py:76 +#: .\axes\models.py:49 msgid "access attempt" msgstr "Zugriffsversuch" -#: axes/models.py:77 +#: .\axes\models.py:50 msgid "access attempts" msgstr "Zugriffsversuche" -#: axes/models.py:81 +#: .\axes\models.py:55 msgid "Logout Time" msgstr "Abmeldezeitpunkt" -#: axes/models.py:90 +#: .\axes\models.py:61 msgid "access log" msgstr "Zugriffslog" -#: axes/models.py:91 +#: .\axes\models.py:62 msgid "access logs" msgstr "Zugriffslogs" From 5d4d626c78cee79c3f8e2bdb7fba4f9674f23a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 28 May 2022 18:04:30 +0300 Subject: [PATCH 090/372] Version 5.34.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 91c021c..8ed1c6d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +5.34.0 (2022-05-28) +------------------- + +- Improve German translations. + [GitRon] + + 5.33.0 (2022-05-16) ------------------- From 3448cba6b17db4ffd1e30b2b2bf52f50e000de38 Mon Sep 17 00:00:00 2001 From: Yousef Almutairi Date: Mon, 30 May 2022 15:30:29 +0300 Subject: [PATCH 091/372] Added Arabic translations --- axes/locale/ar/LC_MESSAGES/django.mo | Bin 0 -> 2018 bytes axes/locale/ar/LC_MESSAGES/django.po | 106 +++++++++++++++++++++++++++ axes/locale/ru/LC_MESSAGES/django.mo | Bin 1950 -> 1910 bytes axes/locale/tr/LC_MESSAGES/django.mo | Bin 1450 -> 1412 bytes 4 files changed, 106 insertions(+) create mode 100644 axes/locale/ar/LC_MESSAGES/django.mo create mode 100644 axes/locale/ar/LC_MESSAGES/django.po diff --git a/axes/locale/ar/LC_MESSAGES/django.mo b/axes/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..0e0f341663c41b1ae56dbccb21ad68117a46ba62 GIT binary patch literal 2018 zcmb`GO>7%Q6vwAfK5D+hHx&9nqL*q-+(OlE;u6z1sfE8toJzoHuqUp;-nDkeh)BI8 zaiS<3kbsJFB&2+h)I%=KS;=oAb-+S}kysugRG<5ui zz+!6G7*| zr|uHsWAGF3N$|zHg*XG+;IrT?_%OHtavz_@Tmzqn-UCm8x4=ihUqHSm?h)c?;3UX# z&x4PH#aOR_T*q~g<9rT23Vs{ge*k&JfI!LN;=Z|;A#BdoUpybKkr%%c&*L#{?jLQ1 z^TgP~#W{Fx{5HI}C$5VZN*5witt#KArdxfV+>U15?KnC-P&J#h>rzWPb2z9s9FkhA zR$Kc?y6ieyRy9c+)><%WmpV=uXU^?-B%?sd{~od2RMJPO^I^Jno=J$_>P6nl}&GD zGgDTfWQ}L&O35zd&b(bJW)peoYpdc($Hy35&!&l)e4dJ#LY79eg-k9#mKh(P%1%#T zf~!;S$hxvBO15muXNr?ET*+uYKh|s{U9W!Vg9-|{LiVuiRB|}M-vWkaRp#2LN2?F@ z%k3s!aD=MYq~|N0o~cY&7Y_ZntXbt*S*PmO8cyA&3-2~`qKp~Jrp2%7+vK!E)lZ)v zp-4GUwYM#BVaI-P=;io~-J&$9SZYIyik`W&4;9m%8V6yG>^XiKFAj2AD$_zH}# zjwD2|6f6X*rbiH#$*c!m(=#`Nm7ps^WykE9b+ZAp`7Y=N3pPtTFu?8+fpUX$Fs1E%O^s`C9e6UI=Yyq8x4ekUBNf961?k&tdrq)HL|2l|yn7&4!O@XfA z7c57, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-30 15:16+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: admin.py:27 +msgid "Form Data" +msgstr "بيانات النموذج" + +#: admin.py:28 admin.py:65 admin.py:100 +msgid "Meta Data" +msgstr "البيانات الوصفية" + +#: conf.py:97 +msgid "Account locked: too many login attempts. Please try again later." +msgstr "الحساب مغلق: محاولات تسجيل دخول كثيرة جدًا. الرجاء معاودة المحاولة في وقت لاحق." + +#: conf.py:105 +msgid "" +"Account locked: too many login attempts. Contact an admin to unlock your " +"account." +msgstr "الحساب مغلق: محاولات تسجيل دخول كثيرة جدًا. اتصل بمسؤول لفتح حسابك." + +#: models.py:6 +msgid "User Agent" +msgstr "وكيل المستخدم" + +#: models.py:8 +msgid "IP Address" +msgstr "عنوان IP" + +#: models.py:10 +msgid "Username" +msgstr "اسم المستخدم" + +#: models.py:12 +msgid "HTTP Accept" +msgstr "قبول HTTP" + +#: models.py:14 +msgid "Path" +msgstr "معلومات المسار" + +#: models.py:16 +msgid "Attempt Time" +msgstr "وقت المحاولة" + +#: models.py:26 +msgid "Access lock out" +msgstr "مقيد من الدخول" + +#: models.py:34 +msgid "access failure" +msgstr "سجل دخول فاشلة" + +#: models.py:35 +msgid "access failures" +msgstr "سجلات دخول فاشلة" + +#: models.py:39 +msgid "GET Data" +msgstr "GET بيانات" + +#: models.py:41 +msgid "POST Data" +msgstr "POST بيانات" + +#: models.py:43 +msgid "Failed Logins" +msgstr "عمليات تسجيل دخول فاشلة" + +#: models.py:49 +msgid "access attempt" +msgstr "محاولة دخول" + +#: models.py:50 +msgid "access attempts" +msgstr "محاولات دخول" + +#: models.py:55 +msgid "Logout Time" +msgstr "وقت تسجيل الخروج" + +#: models.py:61 +msgid "access log" +msgstr "سجل الدخول" + +#: models.py:62 +msgid "access logs" +msgstr "سجلات الدخول" diff --git a/axes/locale/ru/LC_MESSAGES/django.mo b/axes/locale/ru/LC_MESSAGES/django.mo index e617ee88e472168266332f21714b35a090b55b6a..36806fbaba5a9151844390f50c5b9f1aceb41e37 100644 GIT binary patch delta 347 zcmXZWJxhX76u|LwpU2no1>Z0bHK&F&YHm>6L|Vj65c~kq(9|S0IfZa>u%XStDbUHm z_Hb)#3;Y5aI63$~^4#Tj&nxF6zK@N{{hElR9FYjNF^WA5;}eGP1<&yn&+yHQC#Zc> z&rkFbFH!rxQ0KofhCdj`Kd-)Zq<>m`AW=6oyCNGJnrwN;TfayK*I3d)%%U3*DPj&2 zc#nGc18Tj4X?(^@9HJgF!z-NQ1^x!uQsMJYB9En@NCB&;i@LU9uA^Lax*H^A2PSL8 YPE0PG)CA9OS7vN(Y|Gg#9^E_N5#Yuo1^@s6 delta 384 zcmXZXze@u#6u|M9yVkR6TeM1rqMnNmXCOIU>>^l~&UJ9KND&b#Xa%=Y1UErA1b={& zvj{FO-CesnIrs;Nf{Tmal*{tT%X@iw`7JyYuU5JnB2w~1Ca{5d+{GNWaTL#R9D6v1 zmuY;1uI@4Q2{Xj6=<53D?!RFH2Ux_9H2>vEci8xkgF__}_#&SaoY3VCcd{Zg_=Ag> z8xbjEgbP^1N!&v>_-UFy$7$kw4DlJ=AOl>+cbr8ppo?Zx2}I_wj`O&W?xBmMuA=-D zJ2$sm*`%k+s+8QSiu?L-`AJ`0Ck^j=X#Vv2iz_jSH|j_%ss`axSno`y&oE2c;W>zKqQCa{Ak?4gUp=zbJAMVs>l>iZ>T zafLbDq1GQz>rdF_&#NhcOgQ*hA_E+g;^760ctb4+Q3GD6jlD6C-{{_R{_iSCl+;jj w{Kys-*!NKz8=>aUostNRNSvXCF@|<5`;PH28vC;OJXfum!E$2{m_z&i1B(nEX8-^I delta 350 zcmXZWy-EW?5Ww-dj~LCV=SLx!CSZ_&9@z`zB5C|=(gc(|fI)(&pc1aJ3W#81VOX%Y z4R#j#5NR q>*%PP_L`-diFYE?h~h>mt~cw+mZ=%@{_Ea4_Rp(Y>u=Z(|LY&sp(l|5 From 3edb33c5c88c1c856ee803918763df8ab5e2170a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 1 Jun 2022 21:14:17 +0300 Subject: [PATCH 092/372] Version 5.35.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8ed1c6d..8250abd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +5.35.0 (2022-06-01) +------------------- + +- Add Arabic translations. + [YDA93] + + 5.34.0 (2022-05-28) ------------------- From 92355b06b617c8afd7cb5280b7d807c573c1c3de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 12:08:23 +0000 Subject: [PATCH 093/372] Bump coverage from 6.4 to 6.4.1 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.4 to 6.4.1. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.4...6.4.1) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 77ac4cd..5841421 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.4 +coverage==6.4.1 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 5e204be6c010a3960f86b643aba2ccf5ea709162 Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Mon, 30 May 2022 15:10:17 +0200 Subject: [PATCH 094/372] #897: Settings variables as table instead of list in the docs --- docs/4_configuration.rst | 184 ++++++++++++++------------------------- 1 file changed, 67 insertions(+), 117 deletions(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index 185837c..ecbd31d 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -14,123 +14,73 @@ Configuring project settings The following ``settings.py`` options are available for customizing Axes behaviour. -* ``AXES_ENABLED``: Enable or disable Axes plugin functionality, - for example in test runner setup. Default: ``True`` -* ``AXES_FAILURE_LIMIT``: The integer number of login attempts allowed before a - record is created for the failed logins. This can also be a callable - or a dotted path to callable that returns an integer and all of the following are valid: - ``AXES_FAILURE_LIMIT = 42``, - ``AXES_FAILURE_LIMIT = lambda *args: 42``, and - ``AXES_FAILURE_LIMIT = 'project.app.get_login_failure_limit'``. - Default: ``3`` -* ``AXES_LOCK_OUT_AT_FAILURE``: After the number of allowed login attempts - are exceeded, should we lock out this IP (and optional user agent)? - Default: ``True`` -* ``AXES_COOLOFF_TIME``: If set, defines a period of inactivity after which - old failed login attempts will be cleared. - Can be set to a Python timedelta object, an integer, a float, a callable, - or a string path to a callable which takes no arguments. - If an integer or float, will be interpreted as a number of hours: - ``AXES_COOLOFF_TIME = 2`` 2 hours - ``AXES_COOLOFF_TIME = 2.0`` 2 hours, 120 minutes - ``AXES_COOLOFF_TIME = 1.7`` 1.7 hours, 102 minutes, 6120 seconds - Default: ``None`` -* ``AXES_ONLY_ADMIN_SITE``: If ``True``, lock is only enabled for admin site. - Admin site is determined by checking request path against the path of ``"admin:index"`` view. - If admin urls are not registered in current urlconf, all requests will not be locked. - Default: ``False`` -* ``AXES_ONLY_USER_FAILURES`` : If ``True``, only lock based on username, - and never lock based on IP if attempts exceed the limit. - Otherwise utilize the existing IP and user locking logic. - Default: ``False`` -* ``AXES_ENABLE_ADMIN``: If ``True``, admin views for access attempts and - logins are shown in Django admin interface. - Default: ``True`` -* ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``: If ``True``, prevent login - from IP under a particular username if the attempt limit has been exceeded, - otherwise lock out based on IP. - Default: ``False`` -* ``AXES_LOCK_OUT_BY_USER_OR_IP``: If ``True``, prevent login - from if the attempt limit has been exceeded for IP or username. - Default: ``False`` -* ``AXES_USE_USER_AGENT``: If ``True``, lock out and log based on the IP address - and the user agent. This means requests from different user agents but from - the same IP are treated differently. This settings has no effect if the - ``AXES_ONLY_USER_FAILURES`` setting is active. - Default: ``False`` -* ``AXES_HANDLER``: The path to the handler class to use. - If set, overrides the default signal handler backend. - Default: ``'axes.handlers.database.AxesDatabaseHandler'`` -* ``AXES_CACHE``: The name of the cache for Axes to use. - Default: ``'default'`` -* ``AXES_LOCKOUT_TEMPLATE``: If set, specifies a template to render when a - user is locked out. Template receives ``cooloff_timedelta``, ``cooloff_time``, ``username`` and ``failure_limit`` as - context variables. - Default: ``None`` -* ``AXES_LOCKOUT_URL``: If set, specifies a URL to redirect to on lockout. If both - ``AXES_LOCKOUT_TEMPLATE`` and ``AXES_LOCKOUT_URL`` are set, the template will be used. - Default: ``None`` -* ``AXES_VERBOSE``: If ``True``, you'll see slightly more logging for Axes. - Default: ``True`` -* ``AXES_USERNAME_FORM_FIELD``: the name of the form field that contains your users usernames. - Default: ``username`` -* ``AXES_USERNAME_CALLABLE``: A callable or a string path to callable that takes - two arguments for user lookups: ``def get_username(request: HttpRequest, credentials: dict) -> str: ...``. - This can be any callable such as ``AXES_USERNAME_CALLABLE = lambda request, credentials: 'username'`` - or a full Python module path to callable such as ``AXES_USERNAME_CALLABLE = 'example.get_username``. - The ``request`` is a HttpRequest like object and the ``credentials`` is a dictionary like object. - ``credentials`` are the ones that were passed to Django ``authenticate()`` in the login flow. - If no function is supplied, Axes fetches the username from the ``credentials`` or ``request.POST`` - dictionaries based on ``AXES_USERNAME_FORM_FIELD``. -* ``AXES_WHITELIST_CALLABLE``: A callable or a string path to callable that takes - two arguments for whitelisting determination and returns True, - if user should be whitelisted: - ``def is_whitelisted(request: HttpRequest, credentials: dict) -> bool: ...``. - This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. - Default: ``None`` -* ``AXES_LOCKOUT_CALLABLE``: A callable or a string path to callable that takes - two arguments returns a response. For example: - ``def generate_lockout_response(request: HttpRequest, credentials: dict) -> HttpResponse: ...``. - This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. - If not callable is defined, then the default implementation in ``axes.helpers.get_lockout_response`` - is used for determining the correct lockout response that is sent to the requesting client. - Default: ``None`` -* ``AXES_PASSWORD_FORM_FIELD``: the name of the form or credentials field that contains your users password. - Default: ``password`` -* ``AXES_SENSITIVE_PARAMETERS``: Configures POST and GET parameter values (in addition to the value of - ``AXES_PASSWORD_FORM_FIELD``) to mask in login attempt logging. - Default: ``[]`` -* ``AXES_NEVER_LOCKOUT_GET``: If ``True``, Axes will never lock out HTTP GET requests. - Default: ``False`` -* ``AXES_NEVER_LOCKOUT_WHITELIST``: If ``True``, users can always login from whitelisted IP addresses. - Default: ``False`` -* ``AXES_IP_BLACKLIST``: An iterable of IPs to be blacklisted. - Takes precedence over whitelists. For example: ``AXES_IP_BLACKLIST = ['0.0.0.0']``. - Default: ``None`` -* ``AXES_IP_WHITELIST``: An iterable of IPs to be whitelisted. - For example: ``AXES_IP_WHITELIST = ['0.0.0.0']``. - Default: ``None`` -* ``AXES_DISABLE_ACCESS_LOG``: If ``True``, disable writing login and logout access logs to database, - so the admin interface will not have user login trail for successful user authentication. - Default: ``False`` -* ``AXES_ENABLE_ACCESS_FAILURE_LOG``: If ``True``, enable writing - login failure logs to database, so you will have every user login - trail for unsuccessful user authentication. Default: ``False`` -* ``AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT``: Sets the number of - failures to trail for each user. When the access failure log reach - this number of records, an automatic removal is ran. Default: - ``1000`` -* ``AXES_RESET_ON_SUCCESS``: If ``True``, a successful login will reset the number of failed logins. - Default: ``False`` -* ``AXES_ALLOWED_CORS_ORIGINS``: Configures lockout response CORS headers for XHR requests. - Default: ``*`` -* ``AXES_HTTP_RESPONSE_CODE``: Sets the http response code returned when ``AXES_FAILURE_LIMIT`` is - reached. - For example: ``AXES_HTTP_RESPONSE_CODE = 429`` - Default: ``403`` -* ``AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT``: If ``True``, a failed login attempt during lockout will - reset the cool off period. - Default: ``True`` ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Variable | Default | Explanation | ++======================================================+==============================================+===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| AXES_ENABLED | True | Enable or disable Axes plugin functionality, for example in test runner setup | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_FAILURE_LIMIT | 3 | The integer number of login attempts allowed before a record is created for the failed logins. This can also be a callable or a dotted path to callable that returns an integer and all of the following are valid: ``AXES_FAILURE_LIMIT = 42``, ``AXES_FAILURE_LIMIT = lambda *args: 42``, and ``AXES_FAILURE_LIMIT = 'project.app.get_login_failure_limit'``. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCK_OUT_AT_FAILURE | True | After the number of allowed login attempts are exceeded, should we lock out this IP (and optional user agent)? | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_COOLOFF_TIME | None | If set, defines a period of inactivity after which old failed login attempts will be cleared. Can be set to a Python timedelta object, an integer, a float, a callable, or a string path to a callable which takes no arguments. If an integer or float, will be interpreted as a number of hours: ``AXES_COOLOFF_TIME = 2`` 2 hours, ``AXES_COOLOFF_TIME = 2.0`` 2 hours, 120 minutes, ``AXES_COOLOFF_TIME = 1.7`` 1.7 hours, 102 minutes, 6120 seconds | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ONLY_ADMIN_SITE | False | If ``True``, lock is only enabled for admin site. Admin site is determined by checking request path against the path of ``"admin:index"`` view. If admin urls are not registered in current urlconf, all requests will not be locked. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ONLY_USER_FAILURES | False | If ``True``, only lock based on username, and never lock based on IP if attempts exceed the limit. Otherwise utilize the existing IP and user locking logic. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ENABLE_ADMIN | True | If ``True``, admin views for access attempts and logins are shown in Django admin interface. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP | False | If ``True``, prevent login from IP under a particular username if the attempt limit has been exceeded, otherwise lock out based on IP. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCK_OUT_BY_USER_OR_IP | False | If ``True``, prevent login from if the attempt limit has been exceeded for IP or username. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_USE_USER_AGENT | False | If ``True``, lock out and log based on the IP address and the user agent. This means requests from different user agents but from the same IP are treated differently. This settings has no effect if the ``AXES_ONLY_USER_FAILURES`` setting is active. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_HANDLER | 'axes.handlers.database.AxesDatabaseHandler' | The path to the handler class to use. If set, overrides the default signal handler backend. Default: ``'axes.handlers.database.AxesDatabaseHandler'`` | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_CACHE | 'default' | The name of the cache for Axes to use. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCKOUT_TEMPLATE | None | If set, specifies a template to render when a user is locked out. Template receives ``cooloff_timedelta``, ``cooloff_time``, ``username`` and ``failure_limit`` as context variables. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCKOUT_URL | None | If set, specifies a URL to redirect to on lockout. If both ``AXES_LOCKOUT_TEMPLATE`` and ``AXES_LOCKOUT_URL`` are set, the template will be used. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_VERBOSE | True | If ``True``, you'll see slightly more logging for Axes. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_USERNAME_FORM_FIELD | 'username' | The name of the form field that contains your users usernames. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_USERNAME_CALLABLE | None | A callable or a string path to callable that takes two arguments for user lookups: ``def get_username(request: HttpRequest, credentials: dict) -> str: ...``. This can be any callable such as ``AXES_USERNAME_CALLABLE = lambda request, credentials: 'username'`` or a full Python module path to callable such as ``AXES_USERNAME_CALLABLE = 'example.get_username``. The ``request`` is a HttpRequest like object and the ``credentials`` is a dictionary like object. ``credentials`` are the ones that were passed to Django ``authenticate()`` in the login flow. If no function is supplied, Axes fetches the username from the ``credentials`` or ``request.POST`` dictionaries based on ``AXES_USERNAME_FORM_FIELD``. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_WHITELIST_CALLABLE | None | A callable or a string path to callable that takes two arguments for whitelisting determination and returns True, if user should be whitelisted: ``def is_whitelisted(request: HttpRequest, credentials: dict) -> bool: ...``. This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCKOUT_CALLABLE | None | A callable or a string path to callable that takes two arguments returns a response. For example: ``def generate_lockout_response(request: HttpRequest, credentials: dict) -> HttpResponse: ...``. This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. If not callable is defined, then the default implementation in ``axes.helpers.get_lockout_response`` is used for determining the correct lockout response that is sent to the requesting client. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_PASSWORD_FORM_FIELD | 'password' | The name of the form or credentials field that contains your users password. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_SENSITIVE_PARAMETERS | [] | Configures POST and GET parameter values (in addition to the value of ``AXES_PASSWORD_FORM_FIELD``) to mask in login attempt logging. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_NEVER_LOCKOUT_GET | False | If ``True``, Axes will never lock out HTTP GET requests. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_NEVER_LOCKOUT_WHITELIST | False | If ``True``, users can always login from whitelisted IP addresses. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_IP_BLACKLIST | None | An iterable of IPs to be blacklisted. Takes precedence over whitelists. For example: ``AXES_IP_BLACKLIST = ['0.0.0.0']``. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_IP_WHITELIST | None | An iterable of IPs to be whitelisted. For example: ``AXES_IP_WHITELIST = ['0.0.0.0']``. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_DISABLE_ACCESS_LOG | False | If ``True``, disable writing login and logout access logs to database, so the admin interface will not have user login trail for successful user authentication. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ENABLE_ACCESS_FAILURE_LOG | False | If ``True``, enable writing login failure logs to database, so you will have every user login trail for unsuccessful user authentication. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT | 1000 | Sets the number of failures to trail for each user. When the access failure log reach this number of records, an automatic removal is ran. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_RESET_ON_SUCCESS | False | If ``True``, a successful login will reset the number of failed logins. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_ALLOWED_CORS_ORIGINS | * | Configures lockout response CORS headers for XHR requests. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_HTTP_RESPONSE_CODE | 403 | Sets the http response code returned when ``AXES_FAILURE_LIMIT`` is reached. For example: ``AXES_HTTP_RESPONSE_CODE = 429`` | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT | True | If ``True``, a failed login attempt during lockout will reset the cool off period. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ The configuration option precedences for the access attempt monitoring are: From 57ca4460b0c7fa815fd65b88fa8425e42bdde249 Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Fri, 3 Jun 2022 15:15:04 +0200 Subject: [PATCH 095/372] #897: Bugfix with wrong display of default value in docs --- docs/4_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index ecbd31d..6228d9e 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -75,7 +75,7 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_RESET_ON_SUCCESS | False | If ``True``, a successful login will reset the number of failed logins. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_ALLOWED_CORS_ORIGINS | * | Configures lockout response CORS headers for XHR requests. | +| AXES_ALLOWED_CORS_ORIGINS | "*" | Configures lockout response CORS headers for XHR requests. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_HTTP_RESPONSE_CODE | 403 | Sets the http response code returned when ``AXES_FAILURE_LIMIT`` is reached. For example: ``AXES_HTTP_RESPONSE_CODE = 429`` | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ From 4e33d127cef2945b889e82cb1632b70603644376 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 12:09:42 +0000 Subject: [PATCH 096/372] Bump mypy from 0.960 to 0.961 Bumps [mypy](https://github.com/python/mypy) from 0.960 to 0.961. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.960...v0.961) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 9fd3485..d7ff397 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.3.0 -mypy==0.960 +mypy==0.961 prospector==1.7.7 types-pkg_resources # Type stub From 408f0e6acee143a7fa7caf6c25df293a345c4eac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 12:51:07 +0000 Subject: [PATCH 097/372] Bump actions/setup-python from 3 to 4 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 619ad49..ff22dfc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.8 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82b5fdd..2afdd30 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} From 8f305155bf402a4d79a4c844e9e037cde2b81755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 12:14:38 +0000 Subject: [PATCH 098/372] Bump black from 22.3.0 to 22.6.0 Bumps [black](https://github.com/psf/black) from 22.3.0 to 22.6.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.3.0...22.6.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index d7ff397..0f98e84 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.3.0 +black==22.6.0 mypy==0.961 prospector==1.7.7 types-pkg_resources # Type stub From 6013b1e9fb66c467c67616df53ce5757234c29a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:08:06 +0000 Subject: [PATCH 099/372] Bump tox from 3.25.0 to 3.25.1 Bumps [tox](https://github.com/tox-dev/tox) from 3.25.0 to 3.25.1. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.25.0...3.25.1) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c2f5816..ad223a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.0.0 -tox==3.25.0 +tox==3.25.1 From ca32979ae73df6b0b42cff74733753c18f0dafd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Jul 2022 12:10:52 +0000 Subject: [PATCH 100/372] Bump coverage from 6.4.1 to 6.4.2 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.4.1 to 6.4.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.4.1...6.4.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 5841421..52e4092 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.4.1 +coverage==6.4.2 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 5c4bca6cb69ad6d96bfec4df85e15dfe62a1689e Mon Sep 17 00:00:00 2001 From: Jonathan Giuffrida Date: Wed, 13 Jul 2022 15:56:03 -0500 Subject: [PATCH 101/372] Add AxesStandaloneBackend --- axes/backends.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/axes/backends.py b/axes/backends.py index e077e29..f39d9dd 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -1,6 +1,6 @@ from typing import Optional from django.conf import settings -from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.backends import BaseBackend, ModelBackend from django.http import HttpRequest from axes.exceptions import ( @@ -11,7 +11,7 @@ from axes.handlers.proxy import AxesProxyHandler from axes.helpers import get_credentials, get_lockout_message, toggleable -class AxesBackend(ModelBackend): +class AxesStandaloneBackend(BaseBackend): """ Authentication backend class that forbids login attempts for locked out users. @@ -19,6 +19,7 @@ class AxesBackend(ModelBackend): prevent locked out users from being logged in by the Django authentication flow. .. note:: This backend does not log your user in. It monitors login attempts. + It also does not run any permissions checks at all. Authentication is handled by the following backends that are configured in ``AUTHENTICATION_BACKENDS``. """ @@ -71,3 +72,16 @@ class AxesBackend(ModelBackend): raise AxesBackendPermissionDenied( "AxesBackend detected that the given user is locked out" ) + + +class AxesBackend(AxesStandaloneBackend, ModelBackend): + """ + Axes authentication backend that also inherits from ModelBackend, + and thus also performs other functions of ModelBackend such as permissions checks. + + Use this class as the first item of ``AUTHENTICATION_BACKENDS`` to + prevent locked out users from being logged in by the Django authentication flow. + + .. note:: This backend does not log your user in. It monitors login attempts. + Authentication is handled by the following backends that are configured in ``AUTHENTICATION_BACKENDS``. + """ From e8293df2ad67198cdb915e9877de400985014893 Mon Sep 17 00:00:00 2001 From: Jonathan Giuffrida Date: Wed, 13 Jul 2022 15:56:07 -0500 Subject: [PATCH 102/372] Update documentation --- docs/2_installation.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/2_installation.rst b/docs/2_installation.rst index 5d624fe..e001768 100644 --- a/docs/2_installation.rst +++ b/docs/2_installation.rst @@ -23,16 +23,21 @@ After installing the package, the project settings need to be configured. 'axes', ] -**2.** Add ``axes.backends.AxesBackend`` to the top of ``AUTHENTICATION_BACKENDS``:: +**2.** Add ``axes.backends.AxesStandaloneBackend`` to the top of ``AUTHENTICATION_BACKENDS``:: AUTHENTICATION_BACKENDS = [ - # AxesBackend should be the first backend in the AUTHENTICATION_BACKENDS list. - 'axes.backends.AxesBackend', + # AxesStandaloneBackend should be the first backend in the AUTHENTICATION_BACKENDS list. + 'axes.backends.AxesStandaloneBackend', # Django ModelBackend is the default authentication backend. 'django.contrib.auth.backends.ModelBackend', ] + For backwards compatibility, ``AxesBackend`` can be used in place of ``AxesStandaloneBackend``. + The only difference is that ``AxesBackend`` also provides the permissions-checking functionality + of Django's ``ModelBackend`` behind the scenes. We recommend using ``AxesStandaloneBackend`` + if you have any custom logic to override Django's standard permissions checks. + **3.** Add ``axes.middleware.AxesMiddleware`` to your list of ``MIDDLEWARE``:: MIDDLEWARE = [ From e168a9126be143d44ce04a5082c0a10dcba946e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 17 Jul 2022 17:19:05 +0300 Subject: [PATCH 103/372] Fix code formatting --- axes/backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axes/backends.py b/axes/backends.py index f39d9dd..6cec9b5 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -19,7 +19,7 @@ class AxesStandaloneBackend(BaseBackend): prevent locked out users from being logged in by the Django authentication flow. .. note:: This backend does not log your user in. It monitors login attempts. - It also does not run any permissions checks at all. + It also does not run any permissions checks at all. Authentication is handled by the following backends that are configured in ``AUTHENTICATION_BACKENDS``. """ @@ -76,7 +76,7 @@ class AxesStandaloneBackend(BaseBackend): class AxesBackend(AxesStandaloneBackend, ModelBackend): """ - Axes authentication backend that also inherits from ModelBackend, + Axes authentication backend that also inherits from ModelBackend, and thus also performs other functions of ModelBackend such as permissions checks. Use this class as the first item of ``AUTHENTICATION_BACKENDS`` to From 979699b5455d8f24e42c9ae3a76e3e4aa22fbbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 17 Jul 2022 17:22:13 +0300 Subject: [PATCH 104/372] Version 5.36.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8250abd..99219b6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +5.36.0 (2022-07-17) +------------------- + +- Add ``AxesStandaloneBackend`` without ``ModelBackend`` dependencies. + [jcgiuffrida] + + 5.35.0 (2022-06-01) ------------------- From 37bffab76cebeac0b95d62a2e7010b5da7fd8ec0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:10:57 +0000 Subject: [PATCH 105/372] Bump mypy from 0.961 to 0.971 Bumps [mypy](https://github.com/python/mypy) from 0.961 to 0.971. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.961...v0.971) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 0f98e84..4342b48 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.6.0 -mypy==0.961 +mypy==0.971 prospector==1.7.7 types-pkg_resources # Type stub From d9c4a43691b6b7dae12bfaa93202a623b235f567 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Aug 2022 12:09:54 +0000 Subject: [PATCH 106/372] Bump coverage from 6.4.2 to 6.4.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.4.2 to 6.4.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.4.2...6.4.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 52e4092..ea94e8f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.4.2 +coverage==6.4.3 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From ddb618c2762980dca2e3f55d4840e7a2c954b8b3 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 3 Aug 2022 15:47:06 +0200 Subject: [PATCH 107/372] Confirm Django 4.1 support --- .github/workflows/test.yml | 4 +++- pyproject.toml | 3 +++ setup.py | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2afdd30..2290033 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: max-parallel: 5 matrix: python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] - django-version: ['3.2', '4.0'] + django-version: ['3.2', '4.0', '4.1'] include: # Tox configuration for QA environment - python-version: '3.10' @@ -36,6 +36,8 @@ jobs: # Exclude Python 3.7 for Django 4.0 and Django main - python-version: '3.7' django-version: '4.0' + - python-version: '3.7' + django-version: '4.1' - python-version: '3.7' django-version: 'main' diff --git a/pyproject.toml b/pyproject.toml index 1deebea..2843f27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ legacy_tox_ini = """ envlist = py{37,38,39,310,py38}-dj32 py{38,39,310,py38}-dj40 + py{38,39,310,py38}-dj41 py{38,39,310,py38}-djmain py310-djqa @@ -27,6 +28,7 @@ python = DJANGO = 3.2: dj32 4.0: dj40 + 4.1: dj41 main: djmain qa: djqa @@ -36,6 +38,7 @@ deps = -r requirements-test.txt dj32: django>=3.2,<3.3 dj40: django>=4.0,<4.1 + dj41: django>=4.1,<4.2 djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = true commands = pytest diff --git a/setup.py b/setup.py index d05cc01..db2f245 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,7 @@ setup( "Framework :: Django", "Framework :: Django :: 3.2", "Framework :: Django :: 4.0", + "Framework :: Django :: 4.1", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", From b4c68154d089fd87d62f9538a74ad5fa01e468a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 16 Aug 2022 15:10:44 +0300 Subject: [PATCH 108/372] Allow PyPy 3.8 test to fail for Django 4.1 Further information on https://foss.heptapod.net/pypy/pypy/-/issues/3751 --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2290033..5972a39 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,6 +29,9 @@ jobs: - python-version: '3.10' django-version: 'main' experimental: true + - python-version: 'pypy-3.8' + django-version: '4.1' + experimental: true - python-version: 'pypy-3.8' django-version: 'main' experimental: true From a03e81328fd6131210ee7000699930626bb6465b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 16 Aug 2022 15:12:23 +0300 Subject: [PATCH 109/372] Allow PyPy 3.8 test to fail for Django 4.1 Further information on https://foss.heptapod.net/pypy/pypy/-/issues/3751 --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 2843f27..c711449 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,8 +47,10 @@ setenv = # Django development version is allowed to fail the test matrix ignore_outcome = djmain: True + pypy38-dj41: True ignore_errors = djmain: True + pypy38-dj41: True # QA runs type checks, linting, and code formatting checks [testenv:py310-djqa] From ea3a551b6456c5851254ce7d4e223ac6944d2726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 16 Aug 2022 15:24:42 +0300 Subject: [PATCH 110/372] Version 5.38.0 --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 99219b6..1da4158 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ Changes ======= +5.37.0 (2022-08-16) +------------------- + +- Add Django 4.1 support. PyPy 3.8 has a known issue with Django 4.1 and is exempted. + [hramezani] + 5.36.0 (2022-07-17) ------------------- From 7c0e53a95bcd6f7e3bfeb6daacaecdd30ed47df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 16 Aug 2022 15:25:26 +0300 Subject: [PATCH 111/372] Version 5.38.0 --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1da4158..accda35 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ Changes ======= + +5.38.0 (2022-08-16) +------------------- + +- Adjust changelog so release notes are correctly visible on PyPy and released package. + [aleksihakli] + + 5.37.0 (2022-08-16) ------------------- From d368bc364bf12b2e9bf3572cec98e45ab8d69520 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 12:10:00 +0000 Subject: [PATCH 112/372] Bump coverage from 6.4.3 to 6.4.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.4.3 to 6.4.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.4.3...6.4.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index ea94e8f..3c3f8d3 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.4.3 +coverage==6.4.4 pytest==7.1.2 pytest-cov==3.0.0 pytest-django==4.5.2 From 5590419f81e0edb392e05c713b0dbce03c9761d6 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Sat, 6 Aug 2022 10:22:50 +0200 Subject: [PATCH 113/372] Use new backend base class in checks In 5c4bca6cb69ad6d96bfec4df85e15dfe62a1689e a new backend base class was introduced. However the check and its corresponding tests still reference the old base class, thus triggering a warning on setups using the new backend base class. resolves #907 --- axes/checks.py | 10 ++++------ tests/test_checks.py | 8 +++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/axes/checks.py b/axes/checks.py index 249eb22..711bcf8 100644 --- a/axes/checks.py +++ b/axes/checks.py @@ -5,7 +5,7 @@ from django.core.checks import ( # pylint: disable=redefined-builtin ) from django.utils.module_loading import import_string -from axes.backends import AxesBackend +from axes.backends import AxesStandaloneBackend from axes.conf import settings @@ -19,16 +19,14 @@ class Messages: MIDDLEWARE_INVALID = ( "You do not have 'axes.middleware.AxesMiddleware' in your settings.MIDDLEWARE." ) - BACKEND_INVALID = "You do not have 'axes.backends.AxesBackend' or a subclass in your settings.AUTHENTICATION_BACKENDS." + BACKEND_INVALID = "You do not have 'axes.backends.AxesStandaloneBackend' or a subclass in your settings.AUTHENTICATION_BACKENDS." SETTING_DEPRECATED = "You have a deprecated setting {deprecated_setting} configured in your project settings" class Hints: CACHE_INVALID = None MIDDLEWARE_INVALID = None - BACKEND_INVALID = ( - "AxesModelBackend was renamed to AxesBackend in django-axes version 5.0." - ) + BACKEND_INVALID = "AxesModelBackend was renamed to AxesStandaloneBackend in django-axes version 5.0." SETTING_DEPRECATED = None @@ -101,7 +99,7 @@ def axes_backend_check(app_configs, **kwargs): # pylint: disable=unused-argumen "Can not import backend class defined in settings.AUTHENTICATION_BACKENDS" ) from e - if issubclass(backend, AxesBackend): + if issubclass(backend, AxesStandaloneBackend): found = True break diff --git a/tests/test_checks.py b/tests/test_checks.py index 594c2a7..1ccad7c 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -1,7 +1,7 @@ from django.core.checks import run_checks, Warning # pylint: disable=redefined-builtin from django.test import override_settings, modify_settings -from axes.backends import AxesBackend +from axes.backends import AxesStandaloneBackend from axes.checks import Messages, Hints, Codes from tests.base import AxesTestCase @@ -58,12 +58,14 @@ class MiddlewareCheckTestCase(AxesTestCase): self.assertEqual(warnings, [warning]) -class AxesSpecializedBackend(AxesBackend): +class AxesSpecializedBackend(AxesStandaloneBackend): pass class BackendCheckTestCase(AxesTestCase): - @modify_settings(AUTHENTICATION_BACKENDS={"remove": ["axes.backends.AxesBackend"]}) + @modify_settings( + AUTHENTICATION_BACKENDS={"remove": ["axes.backends.AxesStandaloneBackend"]} + ) def test_backend_missing(self): warnings = run_checks() warning = Warning( From 556b56a84befa713a65e3687693c11ab599a6e41 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Sat, 6 Aug 2022 18:09:21 +0200 Subject: [PATCH 114/372] update testconfig: use AxesStandaloneBackend --- tests/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/settings.py b/tests/settings.py index 949aad8..b378fea 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -20,7 +20,7 @@ MIDDLEWARE = [ ] AUTHENTICATION_BACKENDS = [ - "axes.backends.AxesBackend", + "axes.backends.AxesStandaloneBackend", "django.contrib.auth.backends.ModelBackend", ] From 3c352566abca0a01a9ba3cb8dc7e633c4379add5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 18 Aug 2022 16:33:17 +0300 Subject: [PATCH 115/372] Version 5.39.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index accda35..2b27ba6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +5.39.0 (2022-08-18) +------------------- + +- Utilize new backend class in tests to fix false negative system check warnings. + [simonkern] + + 5.38.0 (2022-08-16) ------------------- From ddc8cb7cb231fb72bcbd134cba6decd2a5421ac4 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Thu, 1 Sep 2022 20:39:26 +1000 Subject: [PATCH 116/372] docs: Fix a few typos There are small typos in: - docs/5_customization.rst - docs/7_architecture.rst Fixes: - Should read `necessary` rather than `nessary`. - Should read `exception` rather than `excepton`. Signed-off-by: Tim Gates --- docs/5_customization.rst | 2 +- docs/7_architecture.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/5_customization.rst b/docs/5_customization.rst index ae91cf6..44b3836 100644 --- a/docs/5_customization.rst +++ b/docs/5_customization.rst @@ -34,7 +34,7 @@ Here is a more detailed example of sending the necessary signals using and a custom auth backend at an endpoint that expects JSON requests. The custom authentication can be swapped out with ``authenticate`` and ``login`` from ``django.contrib.auth``, but beware that those methods take -care of sending the nessary signals for you, and there is no need to duplicate +care of sending the necessary signals for you, and there is no need to duplicate them as per the example. ``example/forms.py``:: diff --git a/docs/7_architecture.rst b/docs/7_architecture.rst index d670c27..f83c6b4 100644 --- a/docs/7_architecture.rst +++ b/docs/7_architecture.rst @@ -56,7 +56,7 @@ are not blocked, and allows the requests to go through if the check passes. If the authentication attempt matches a lockout rule, e.g. it is from a blacklisted IP or exceeds the maximum configured authentication attempts, -it is blocked by raising the ``PermissionDenied`` excepton in the backend. +it is blocked by raising the ``PermissionDenied`` exception in the backend. Axes monitors logins with the ``user_login_failed`` signal receiver and records authentication failures from both the ``AxesBackend`` and From ad4354cb97f5d7459379d7bed44d2bff4163ec3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 12:08:14 +0000 Subject: [PATCH 117/372] Bump black from 22.6.0 to 22.8.0 Bumps [black](https://github.com/psf/black) from 22.6.0 to 22.8.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.6.0...22.8.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 4342b48..7876d1f 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.6.0 +black==22.8.0 mypy==0.971 prospector==1.7.7 types-pkg_resources # Type stub From 293199b1d4cbd1857804f08bb7118bb6a565f022 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 12:08:27 +0000 Subject: [PATCH 118/372] Bump pytest from 7.1.2 to 7.1.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.2 to 7.1.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.1.2...7.1.3) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 3c3f8d3..32831f9 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.4.4 -pytest==7.1.2 +pytest==7.1.3 pytest-cov==3.0.0 pytest-django==4.5.2 pytest-subtests==0.8.0 From 6a16fb54193a34467c709cba2564af2014fd3c33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Sep 2022 12:10:18 +0000 Subject: [PATCH 119/372] Bump tox from 3.25.1 to 3.26.0 Bumps [tox](https://github.com/tox-dev/tox) from 3.25.1 to 3.26.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.25.1...3.26.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ad223a7..9e9b0aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.0.0 -tox==3.25.1 +tox==3.26.0 From ea93e5e6684da188f7deeb984874591b689cb337 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:09:26 +0000 Subject: [PATCH 120/372] Bump mypy from 0.971 to 0.981 Bumps [mypy](https://github.com/python/mypy) from 0.971 to 0.981. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.971...v0.981) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 7876d1f..76c0454 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.8.0 -mypy==0.971 +mypy==0.981 prospector==1.7.7 types-pkg_resources # Type stub From 3ccc5a284384c05f18059fdd4a66d32d83f1cddd Mon Sep 17 00:00:00 2001 From: Nishit Mohanan Date: Fri, 23 Sep 2022 16:07:37 +0530 Subject: [PATCH 121/372] Changed tox configuration link to pyproject.toml --- docs/1_requirements.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/1_requirements.rst b/docs/1_requirements.rst index 11b31f8..bbe2c99 100644 --- a/docs/1_requirements.rst +++ b/docs/1_requirements.rst @@ -7,7 +7,7 @@ Axes requires a supported Django version and runs on Python versions 3.6 and abo Refer to the project source code repository in `GitHub `_ and see the -`Tox configuration `_ and +`pyproject.toml file `_ and `Python package definition `_ to check if your Django and Python version are supported. From 2554a8cffd2586c117ab5debe2f9352d33ba1079 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 27 Sep 2022 21:29:37 +0200 Subject: [PATCH 122/372] Update remaining part of Python 3.6 to 3.7 --- docs/1_requirements.rst | 2 +- mypy.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/1_requirements.rst b/docs/1_requirements.rst index bbe2c99..75513f0 100644 --- a/docs/1_requirements.rst +++ b/docs/1_requirements.rst @@ -3,7 +3,7 @@ Requirements ============ -Axes requires a supported Django version and runs on Python versions 3.6 and above. +Axes requires a supported Django version and runs on Python versions 3.7 and above. Refer to the project source code repository in `GitHub `_ and see the diff --git a/mypy.ini b/mypy.ini index 1cbb0ff..dd2b3f3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,5 @@ [mypy] -python_version = 3.6 +python_version = 3.7 ignore_missing_imports = True [mypy-axes.migrations.*] From 1e474dc9cb00ef5850bbc989bc7ef6ece2d6efb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:11:10 +0000 Subject: [PATCH 123/372] Bump pytest-cov from 3.0.0 to 4.0.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 3.0.0 to 4.0.0. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v3.0.0...v4.0.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 32831f9..78764cf 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.4.4 pytest==7.1.3 -pytest-cov==3.0.0 +pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.8.0 From dea52c25bcba4608c360deefe529bb422d1b28a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Sep 2022 12:09:43 +0000 Subject: [PATCH 124/372] Bump coverage from 6.4.4 to 6.5.0 Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.4.4 to 6.5.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.4.4...6.5.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 78764cf..e2e9f09 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.4.4 +coverage==6.5.0 pytest==7.1.3 pytest-cov==4.0.0 pytest-django==4.5.2 From 113a45cef8da5750d68c58b6a7e17e5b2a25069d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 12:15:13 +0000 Subject: [PATCH 125/372] Bump mypy from 0.981 to 0.982 Bumps [mypy](https://github.com/python/mypy) from 0.981 to 0.982. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.981...v0.982) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 76c0454..6a5c916 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.8.0 -mypy==0.981 +mypy==0.982 prospector==1.7.7 types-pkg_resources # Type stub From 98ed2a28f3aeb85e0b3ae44a7a5d41e13fe8dc7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:07:16 +0000 Subject: [PATCH 126/372] Bump black from 22.8.0 to 22.10.0 Bumps [black](https://github.com/psf/black) from 22.8.0 to 22.10.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.8.0...22.10.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 6a5c916..7489a2f 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.8.0 +black==22.10.0 mypy==0.982 prospector==1.7.7 types-pkg_resources # Type stub From 2978e686968efb2a721da24af4c49889e44344fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:10:15 +0000 Subject: [PATCH 127/372] Bump tox from 3.26.0 to 3.27.0 Bumps [tox](https://github.com/tox-dev/tox) from 3.26.0 to 3.27.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.26.0...3.27.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9e9b0aa..b72d63c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.0.0 -tox==3.26.0 +tox==3.27.0 From 2cf8d0afae3e3341f0638ff409b006dcaaedeb0a Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 27 Oct 2022 13:24:52 -0500 Subject: [PATCH 128/372] add python 3.11 to CI, tox, and trove classifiers --- .github/workflows/test.yml | 5 ++++- pyproject.toml | 9 +++++---- setup.py | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5972a39..8c4edd0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.8'] django-version: ['3.2', '4.0', '4.1'] include: # Tox configuration for QA environment @@ -29,6 +29,9 @@ jobs: - python-version: '3.10' django-version: 'main' experimental: true + - python-version: '3.11' + django-version: 'main' + experimental: true - python-version: 'pypy-3.8' django-version: '4.1' experimental: true diff --git a/pyproject.toml b/pyproject.toml index c711449..7c62e79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,10 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{37,38,39,310,py38}-dj32 - py{38,39,310,py38}-dj40 - py{38,39,310,py38}-dj41 - py{38,39,310,py38}-djmain + py{37,38,39,310,311,py38}-dj32 + py{38,39,310,311,py38}-dj40 + py{38,39,310,311,py38}-dj41 + py{38,39,310,311,py38}-djmain py310-djqa [gh-actions] @@ -22,6 +22,7 @@ python = 3.8: py38 3.9: py39 3.10: py310 + 3.11: py311 pypy-3.8: pypy38 [gh-actions:env] diff --git a/setup.py b/setup.py index db2f245..c7a6d1f 100644 --- a/setup.py +++ b/setup.py @@ -57,6 +57,7 @@ setup( "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: Log Analysis", From c01bad0b91f22b32fead67d17be61551aa60667e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 12:10:48 +0000 Subject: [PATCH 129/372] Bump pytest-subtests from 0.8.0 to 0.9.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/pytest-dev/pytest-subtests/releases) - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.8.0...0.9.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index e2e9f09..aa8ade7 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ coverage==6.5.0 pytest==7.1.3 pytest-cov==4.0.0 pytest-django==4.5.2 -pytest-subtests==0.8.0 +pytest-subtests==0.9.0 From 6f359b930f44ff438329af9499b0b5e9d98b1937 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 12:09:06 +0000 Subject: [PATCH 130/372] Bump sphinx-rtd-theme from 1.0.0 to 1.1.0 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases) - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.0.0...1.1.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b72d63c..a5d10d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.0.0 +sphinx_rtd_theme==1.1.0 tox==3.27.0 From 8dd5823fae9094c2e96d91d5a68b1914f07e79cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 12:29:11 +0000 Subject: [PATCH 131/372] Bump pytest from 7.1.3 to 7.2.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.3 to 7.2.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.1.3...7.2.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index aa8ade7..71ab28a 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==6.5.0 -pytest==7.1.3 +pytest==7.2.0 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.9.0 From 670de1efeebe98fcddb72faaded69f2fc5b940f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 12:02:02 +0000 Subject: [PATCH 132/372] Bump sphinx-rtd-theme from 1.1.0 to 1.1.1 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases) - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.1.0...1.1.1) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a5d10d1..938d4fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.1.0 +sphinx_rtd_theme==1.1.1 tox==3.27.0 From 26a435e76ed1f975d1a92db0c4d09f7ce3e0daf0 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 8 Nov 2022 09:32:14 -0600 Subject: [PATCH 133/372] code review updates --- .github/workflows/test.yml | 6 ++++++ pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c4edd0..ec48e4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,6 +46,12 @@ jobs: django-version: '4.1' - python-version: '3.7' django-version: 'main' + # Exclude Python 3.11 for Django 3.2 and Django 4.0 + - python-version: '3.11' + django-version: '3.2' + - python-version: '3.11' + django-version: '4.0' + steps: - uses: actions/checkout@v3 diff --git a/pyproject.toml b/pyproject.toml index 7c62e79..d2086fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,8 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{37,38,39,310,311,py38}-dj32 - py{38,39,310,311,py38}-dj40 + py{37,38,39,310,py38}-dj32 + py{38,39,310,py38}-dj40 py{38,39,310,311,py38}-dj41 py{38,39,310,311,py38}-djmain py310-djqa From b565a70d5139185aeb6c6827baa7c0d8d37116b5 Mon Sep 17 00:00:00 2001 From: Ihor Sychevskyi Date: Wed, 9 Nov 2022 00:18:07 +0200 Subject: [PATCH 134/372] update links (#941) --- docs/Makefile | 2 +- docs/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 0fd0c1e..820fe11 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -9,7 +9,7 @@ BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/) endif # Internal variables. diff --git a/docs/conf.py b/docs/conf.py index eedb46c..8fbfe9e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ Sphinx documentation generator configuration. More information on the configuration options is available at: - http://www.sphinx-doc.org/en/master/usage/configuration.html + https://www.sphinx-doc.org/en/master/usage/configuration.html """ import sphinx_rtd_theme From c556675da442a381a89b7d2b73996e4385e6899a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:01:26 +0000 Subject: [PATCH 135/372] Bump tox from 3.27.0 to 3.27.1 Bumps [tox](https://github.com/tox-dev/tox) from 3.27.0 to 3.27.1. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.27.0...3.27.1) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 938d4fa..b050252 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==3.27.0 +tox==3.27.1 From 5c7bea2a425e920281ced5df0bd6f7e07419890e Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 9 Nov 2022 16:03:25 +0100 Subject: [PATCH 136/372] Update mypy to 0.990 --- axes/handlers/cache.py | 4 ++-- requirements-qa.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 423c592..b85f3f6 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -29,8 +29,8 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): def reset_attempts( self, *, - ip_address: str = None, - username: str = None, + ip_address: Optional[str] = None, + username: Optional[str] = None, ip_or_username: bool = False, ) -> int: cache_keys: list = [] diff --git a/requirements-qa.txt b/requirements-qa.txt index 7489a2f..34e30d6 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.10.0 -mypy==0.982 +mypy==0.990 prospector==1.7.7 types-pkg_resources # Type stub From ca42b0f7dcc3fc3403b7ba12750790943c5a0e77 Mon Sep 17 00:00:00 2001 From: JensDiemer Date: Thu, 15 Sep 2022 10:23:01 +0200 Subject: [PATCH 137/372] Bugfix #921 cannot import name 'get_distribution' Replace `pkg_resources` with `importlib` solution to fix https://github.com/jazzband/django-axes/issues/921 Because `importlib.metadata` is new in Python 3.8, fallback to old `setuptools` solution. --- axes/__init__.py | 10 +++++++--- axes/apps.py | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/axes/__init__.py b/axes/__init__.py index 37645c8..6eeef8e 100644 --- a/axes/__init__.py +++ b/axes/__init__.py @@ -1,4 +1,8 @@ -from pkg_resources import get_distribution +try: + from importlib.metadata import version # New in Python 3.8 +except ImportError: + from pkg_resources import get_distribution # from setuptools, deprecated - -__version__ = get_distribution("django-axes").version + __version__ = get_distribution('django-axes').version +else: + __version__ = version('django-axes') diff --git a/axes/apps.py b/axes/apps.py index 5ef2612..1fa3094 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -1,7 +1,8 @@ from logging import getLogger from django import apps -from pkg_resources import get_distribution + +from axes import __version__ log = getLogger(__name__) @@ -40,7 +41,7 @@ class AppConfig(apps.AppConfig): mode = "blocking by IP only" log.info( "AXES: BEGIN version %s, %s", - get_distribution("django-axes").version, + __version__, mode, ) From 1825f9be4faf1f0f39735421b455350122064df5 Mon Sep 17 00:00:00 2001 From: liampauling Date: Tue, 25 Oct 2022 09:34:12 +0100 Subject: [PATCH 138/372] add missing db alias to migration 0007 closes #932 --- .../0007_alter_accessattempt_unique_together.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/axes/migrations/0007_alter_accessattempt_unique_together.py b/axes/migrations/0007_alter_accessattempt_unique_together.py index d84e3fb..66927f9 100644 --- a/axes/migrations/0007_alter_accessattempt_unique_together.py +++ b/axes/migrations/0007_alter_accessattempt_unique_together.py @@ -6,21 +6,23 @@ from django.db.models import Count def deduplicate_attempts(apps, schema_editor): AccessAttempt = apps.get_model("axes", "AccessAttempt") + db_alias = schema_editor.connection.alias duplicated_attempts = ( - AccessAttempt.objects.values("username", "user_agent", "ip_address") + AccessAttempt.objects.using(db_alias) + .values("username", "user_agent", "ip_address") .annotate(Count("id")) .order_by() .filter(id__count__gt=1) ) for attempt in duplicated_attempts: - redundant_attempts = AccessAttempt.objects.filter( + redundant_attempts = AccessAttempt.objects.using(db_alias).filter( username=attempt["username"], user_agent=attempt["user_agent"], ip_address=attempt["ip_address"], )[1:] for redundant_attempt in redundant_attempts: - redundant_attempt.delete() + redundant_attempt.delete(using=db_alias) class Migration(migrations.Migration): From 2b24167decd7758b6f658206ad102e8f5e6a9ee2 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 15 Nov 2022 13:09:30 +0100 Subject: [PATCH 139/372] Fix black formatting --- axes/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axes/__init__.py b/axes/__init__.py index 6eeef8e..e3011df 100644 --- a/axes/__init__.py +++ b/axes/__init__.py @@ -3,6 +3,6 @@ try: except ImportError: from pkg_resources import get_distribution # from setuptools, deprecated - __version__ = get_distribution('django-axes').version + __version__ = get_distribution("django-axes").version else: - __version__ = version('django-axes') + __version__ = version("django-axes") From 00a418a659c7945239053b329bfc820638d88251 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 12:01:15 +0000 Subject: [PATCH 140/372] Bump mypy from 0.990 to 0.991 Bumps [mypy](https://github.com/python/mypy) from 0.990 to 0.991. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.990...v0.991) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 34e30d6..2eda95a 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.10.0 -mypy==0.990 +mypy==0.991 prospector==1.7.7 types-pkg_resources # Type stub From a39ba7474bda88cb1bc2438d1d124adb81e0d1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 19 Nov 2022 12:55:04 +0200 Subject: [PATCH 141/372] Version 5.40.0 --- CHANGES.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2b27ba6..a7f28a9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,23 @@ Changes ======= +5.40.0 (2022-11-19) +------------------- + +- Update packages and linters for new version support. + [hramezani] +- Update documentation links. + [Arhell] +- Use importlib instead of setuptools for Python 3.8+. + [jedie] +- Python 3.11 support. + [joshuadavidthomas] +- Documentation improvements. + [nsht] +- Documentation improvements. + [timgates42] + + 5.39.0 (2022-08-18) ------------------- From f2d300927989acf9bcff35e8b0b284d6e96caea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Thu, 24 Nov 2022 14:11:17 +0100 Subject: [PATCH 142/372] add user_agent test, fix get_client_parameters tests --- tests/test_helpers.py | 5 ++--- tests/test_login.py | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 30a216d..1da372d 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -329,7 +329,7 @@ class ClientParametersTestCase(AxesTestCase): def test_get_filter_kwargs_ip_and_agent(self): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent), - [{"ip_address": self.ip_address}, {"user_agent": self.user_agent}], + [{"ip_address": self.ip_address, "user_agent": self.user_agent}], ) @override_settings( @@ -341,8 +341,7 @@ class ClientParametersTestCase(AxesTestCase): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent), [ - {"username": self.username, "ip_address": self.ip_address}, - {"user_agent": self.user_agent}, + {"username": self.username, "ip_address": self.ip_address, "user_agent": self.user_agent}, ], ) diff --git a/tests/test_login.py b/tests/test_login.py index 07d5a45..29211e2 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -86,7 +86,7 @@ class DatabaseLoginTestCase(AxesTestCase): ALLOWED = 302 BLOCKED = 403 - def _login(self, username, password, ip_addr="127.0.0.1", **kwargs): + def _login(self, username, password, ip_addr="127.0.0.1", user_agent="test-browser", **kwargs): """ Login a user and get the response. @@ -101,13 +101,13 @@ class DatabaseLoginTestCase(AxesTestCase): reverse("admin:login"), post_data, REMOTE_ADDR=ip_addr, - HTTP_USER_AGENT="test-browser", + HTTP_USER_AGENT=user_agent, ) - def _lockout_user_from_ip(self, username, ip_addr): + def _lockout_user_from_ip(self, username, ip_addr, user_agent="test-browser"): for _ in range(settings.AXES_FAILURE_LIMIT): response = self._login( - username=username, password=self.WRONG_PASSWORD, ip_addr=ip_addr + username=username, password=self.WRONG_PASSWORD, ip_addr=ip_addr, user_agent=user_agent, ) return response @@ -368,6 +368,19 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) + @override_settings(AXES_USE_USER_AGENT=True) + def test_lockout_by_user_still_allows_login_with_differnet_user_agent(self): + # User with empty username is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test with another user agent: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser-2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + # Test for true and false positives when blocking by IP *OR* user (default) # With cache enabled. Default criteria. def test_lockout_by_ip_blocks_when_same_user_same_ip_using_cache(self): From ce3f2f2c3fce8f1832295436b86db356d6991ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Thu, 24 Nov 2022 14:33:45 +0100 Subject: [PATCH 143/372] fix get_client_parameters when AXES_USE_USER_AGENT=True --- axes/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axes/helpers.py b/axes/helpers.py index 879a784..e40a2b3 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -205,7 +205,7 @@ def get_client_parameters(username: str, ip_address: str, user_agent: str) -> li if settings.AXES_USE_USER_AGENT: # 4. The HTTP User-Agent can be used to track e.g. one browser - filter_query.append({"user_agent": user_agent}) + filter_query[0]["user_agent"] = user_agent return filter_query From b65482ec98d6b4fe85c4b11722895b885528cb2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 24 Nov 2022 22:01:20 +0200 Subject: [PATCH 144/372] Version 5.40.1 --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a7f28a9..47d01e2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,12 @@ Changes ======= +5.40.1 (2022-11-24) +------------------- + +- Fix bug in user agent request blocking. [PetrDlouhy] + + 5.40.0 (2022-11-19) ------------------- From c8f831bb62fa5f6d2297fdc23a7184f7f31ffd7c Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 2 Dec 2022 13:26:35 +0100 Subject: [PATCH 145/372] Update prospector to 1.8.2 --- .prospector.yaml | 8 ++++++++ axes/apps.py | 6 ++++-- axes/handlers/test.py | 2 +- requirements-qa.txt | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.prospector.yaml b/.prospector.yaml index 219f6ac..0753272 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -5,3 +5,11 @@ ignore-paths: pycodestyle: options: max-line-length: 142 + +pylint: + disable: + - django-not-configured + +pyflakes: + disable: + - F401 diff --git a/axes/apps.py b/axes/apps.py index 1fa3094..a2c0834 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -1,3 +1,5 @@ +# pylint: disable=import-outside-toplevel, unused-import + from logging import getLogger from django import apps @@ -26,8 +28,8 @@ class AppConfig(apps.AppConfig): cls.initialized = True # Only import settings, checks, and signals one time after Django has been initialized - from axes.conf import settings # noqa - from axes import checks, signals # noqa + from axes.conf import settings + from axes import checks, signals # Skip startup log messages if Axes is not set to verbose if settings.AXES_VERBOSE: diff --git a/axes/handlers/test.py b/axes/handlers/test.py index bea66b3..5213e2c 100644 --- a/axes/handlers/test.py +++ b/axes/handlers/test.py @@ -2,7 +2,7 @@ from axes.handlers.base import AxesHandler from typing import Optional -class AxesTestHandler(AxesHandler): # pylint: disable=unused-argument +class AxesTestHandler(AxesHandler): """ Signal handler implementation that does nothing, ideal for a test suite. """ diff --git a/requirements-qa.txt b/requirements-qa.txt index 2eda95a..f0ac01f 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.10.0 mypy==0.991 -prospector==1.7.7 +prospector==1.8.2 types-pkg_resources # Type stub From 9e3729e97ffe400baf020a79973323df3e8909e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 12:02:45 +0000 Subject: [PATCH 146/372] Bump prospector from 1.8.2 to 1.8.3 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/v1.8.2...v1.8.3) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index f0ac01f..1919456 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.10.0 mypy==0.991 -prospector==1.8.2 +prospector==1.8.3 types-pkg_resources # Type stub From 673124e5e068bc41830600d225510aa46f3bca9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 12:01:46 +0000 Subject: [PATCH 147/372] Bump tox from 3.27.1 to 4.0.3 Bumps [tox](https://github.com/tox-dev/tox) from 3.27.1 to 4.0.3. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.27.1...4.0.3) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b050252..8adc1dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==3.27.1 +tox==4.0.3 From a1a855815ee1de61cf742908c33bba8d1fcd07a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:02:29 +0000 Subject: [PATCH 148/372] Bump tox from 4.0.3 to 4.0.8 Bumps [tox](https://github.com/tox-dev/tox) from 4.0.3 to 4.0.8. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.3...4.0.8) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8adc1dc..d1fd28d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.3 +tox==4.0.8 From fce4642c1a0a53efd94854a785b9a9e7a8d0c172 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:02:23 +0000 Subject: [PATCH 149/372] Bump black from 22.10.0 to 22.12.0 Bumps [black](https://github.com/psf/black) from 22.10.0 to 22.12.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.10.0...22.12.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 1919456..75e4a9c 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.10.0 +black==22.12.0 mypy==0.991 prospector==1.8.3 types-pkg_resources # Type stub From e4553022440aebbfb15f2bb81141b4615a3e8bc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 12:01:57 +0000 Subject: [PATCH 150/372] Bump tox from 4.0.8 to 4.0.9 Bumps [tox](https://github.com/tox-dev/tox) from 4.0.8 to 4.0.9. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.8...4.0.9) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d1fd28d..6e29c84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.8 +tox==4.0.9 From e71d8ef20b7fe4fbb29d7c1359a963c52de3c3fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:02:00 +0000 Subject: [PATCH 151/372] Bump tox from 4.0.9 to 4.0.11 Bumps [tox](https://github.com/tox-dev/tox) from 4.0.9 to 4.0.11. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.9...4.0.11) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6e29c84..937bcf0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.9 +tox==4.0.11 From 7614f1595cf59ad30197dd2a79e1ffa4d8410559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 13:12:24 +0100 Subject: [PATCH 152/372] Bump tox from 4.0.11 to 4.0.14 (#960) Bumps [tox](https://github.com/tox-dev/tox) from 4.0.11 to 4.0.14. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.11...4.0.14) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 937bcf0..8830463 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.11 +tox==4.0.14 From 1c06408753f39ca3f5206528fc5278c5f3aee4ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 13:13:11 +0100 Subject: [PATCH 153/372] Bump coverage from 6.5.0 to 7.0.0 (#961) Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.5.0 to 7.0.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/6.5.0...7.0.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 71ab28a..fa06907 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==6.5.0 +coverage==7.0.0 pytest==7.2.0 pytest-cov==4.0.0 pytest-django==4.5.2 From 7dfa18d9d4c507299b839416225997edbb537e56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 13:29:34 +0100 Subject: [PATCH 154/372] Bump tox from 4.0.14 to 4.0.15 (#962) Bumps [tox](https://github.com/tox-dev/tox) from 4.0.14 to 4.0.15. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.14...4.0.15) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8830463..07f0c99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.14 +tox==4.0.15 From dfd85002e18d70dda6400b1c88ef38aa43877d7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 13:37:57 +0100 Subject: [PATCH 155/372] Bump tox from 4.0.15 to 4.0.16 (#963) Bumps [tox](https://github.com/tox-dev/tox) from 4.0.15 to 4.0.16. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.0.15...4.0.16) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 07f0c99..3cf1349 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.15 +tox==4.0.16 From 0c8180c42aac02c2988fa6914f47261f40140bd0 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 18 Jan 2023 14:32:23 +0100 Subject: [PATCH 156/372] Update dependancies --- requirements-qa.txt | 2 +- requirements-test.txt | 4 ++-- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 75e4a9c..5578c0a 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.12.0 mypy==0.991 -prospector==1.8.3 +prospector==1.8.4 types-pkg_resources # Type stub diff --git a/requirements-test.txt b/requirements-test.txt index fa06907..c97627a 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . -coverage==7.0.0 -pytest==7.2.0 +coverage==7.0.5 +pytest==7.2.1 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.9.0 diff --git a/requirements.txt b/requirements.txt index 3cf1349..b335b38 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.0.16 +tox==4.3.4 From 15f2717a4844ed48e13b3e6b323369681579ae07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jan 2023 13:18:41 +0100 Subject: [PATCH 157/372] Bump tox from 4.3.4 to 4.3.5 (#991) Bumps [tox](https://github.com/tox-dev/tox) from 4.3.4 to 4.3.5. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.3.4...4.3.5) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b335b38..6461bc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.3.4 +tox==4.3.5 From a2a55079635eaa42abd57eb260f2ec450d3efb33 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Thu, 19 Jan 2023 15:20:39 +0100 Subject: [PATCH 158/372] Run tests for Django main for Python >= 3.10 --- .github/workflows/test.yml | 9 --------- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec48e4a..164960f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,12 +20,6 @@ jobs: - python-version: '3.10' django-version: 'qa' # Django main - - python-version: '3.8' - django-version: 'main' - experimental: true - - python-version: '3.9' - django-version: 'main' - experimental: true - python-version: '3.10' django-version: 'main' experimental: true @@ -35,9 +29,6 @@ jobs: - python-version: 'pypy-3.8' django-version: '4.1' experimental: true - - python-version: 'pypy-3.8' - django-version: 'main' - experimental: true exclude: # Exclude Python 3.7 for Django 4.0 and Django main - python-version: '3.7' diff --git a/pyproject.toml b/pyproject.toml index d2086fb..346484b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ envlist = py{37,38,39,310,py38}-dj32 py{38,39,310,py38}-dj40 py{38,39,310,311,py38}-dj41 - py{38,39,310,311,py38}-djmain + py{310,311,py38}-djmain py310-djqa [gh-actions] From 62e01a8e4cd1908f38b09fc3d5f43dbb84aa17e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jan 2023 13:13:12 +0100 Subject: [PATCH 159/372] Bump coverage from 7.0.5 to 7.1.0 (#993) Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.0.5 to 7.1.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.0.5...7.1.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c97627a..9ad3e1e 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==7.0.5 +coverage==7.1.0 pytest==7.2.1 pytest-cov==4.0.0 pytest-django==4.5.2 From 6d9c2bf1daaf61c3d2adbc84fe99f765bbdde5e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:17:04 +0100 Subject: [PATCH 160/372] Bump tox from 4.3.5 to 4.4.3 (#995) Bumps [tox](https://github.com/tox-dev/tox) from 4.3.5 to 4.4.3. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.3.5...4.4.3) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6461bc6..9a41420 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.3.5 +tox==4.4.3 From 986eda365e8e08622b827a20fd8268e9f50aab04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 13:31:49 +0100 Subject: [PATCH 161/372] Bump tox from 4.4.3 to 4.4.4 (#996) Bumps [tox](https://github.com/tox-dev/tox) from 4.4.3 to 4.4.4. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.3...4.4.4) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9a41420..94a7b09 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.1.1 -tox==4.4.3 +tox==4.4.4 From 120df250235da9690e242b0a38a01a4bd6759b81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:49:04 +0300 Subject: [PATCH 162/372] Bump mypy from 0.991 to 1.0.0 (#999) Bumps [mypy](https://github.com/python/mypy) from 0.991 to 1.0.0. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.991...v1.0.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5578c0a..dc930a7 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.12.0 -mypy==0.991 +mypy==1.0.0 prospector==1.8.4 types-pkg_resources # Type stub From 7f534c0e3bb24589b0ddf9b9b55c5e5e101e3037 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 14:03:51 +0100 Subject: [PATCH 163/372] Bump sphinx-rtd-theme from 1.1.1 to 1.2.0 (#1000) Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases) - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.1.1...1.2.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 94a7b09..4484d52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.1.1 +sphinx_rtd_theme==1.2.0 tox==4.4.4 From 779fdf496bd45566d520877f15eeb6fa33194838 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 17 Feb 2023 14:42:49 +0100 Subject: [PATCH 164/372] Adopt test based on Django security release for CVE-2023-24580 --- tests/test_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_logging.py b/tests/test_logging.py index 35ae52e..ae2a7c6 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -69,6 +69,7 @@ class AccessLogTestCase(AxesTestCase): self.assertIsNotNone(AccessLog.objects.latest("id").logout_time) + @override_settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=1500) def test_log_data_truncated(self): """ Test that get_query_str properly truncates data to the max_length (default 1024). From 64be7f95b33bb68842abdc15e3a0f01d6b591dee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:57:24 +0000 Subject: [PATCH 165/372] Bump coverage from 7.1.0 to 7.2.0 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.1.0 to 7.2.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.1.0...7.2.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 9ad3e1e..cc495a5 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==7.1.0 +coverage==7.2.0 pytest==7.2.1 pytest-cov==4.0.0 pytest-django==4.5.2 From b00d1ffe21801a8a5e3b020883c379f8a36fd272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:57:18 +0000 Subject: [PATCH 166/372] Bump pytest-subtests from 0.9.0 to 0.10.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.9.0 to 0.10.0. - [Release notes](https://github.com/pytest-dev/pytest-subtests/releases) - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.9.0...0.10.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index cc495a5..cc1c58a 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ coverage==7.2.0 pytest==7.2.1 pytest-cov==4.0.0 pytest-django==4.5.2 -pytest-subtests==0.9.0 +pytest-subtests==0.10.0 From 4cc2558611193500b94c0f52050e4686d89e4156 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:57:11 +0000 Subject: [PATCH 167/372] Bump prospector from 1.8.4 to 1.9.0 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.8.4 to 1.9.0. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/v1.8.4...v1.9.0) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index dc930a7..d7ebab9 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==22.12.0 mypy==1.0.0 -prospector==1.8.4 +prospector==1.9.0 types-pkg_resources # Type stub From 42002b3f2c7ae168ecbb510a522892c9423a8d44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 05:49:49 +0000 Subject: [PATCH 168/372] Bump black from 22.12.0 to 23.1.0 Bumps [black](https://github.com/psf/black) from 22.12.0 to 23.1.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.12.0...23.1.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index d7ebab9..4adfb71 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==22.12.0 +black==23.1.0 mypy==1.0.0 prospector==1.9.0 types-pkg_resources # Type stub From 274c4f7010bc330c2735e3be06a4a857b90f030f Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Mon, 27 Feb 2023 14:11:51 +0100 Subject: [PATCH 169/372] Fix black formatting in migrations --- axes/migrations/0001_initial.py | 1 - axes/migrations/0002_auto_20151217_2044.py | 1 - axes/migrations/0003_auto_20160322_0929.py | 1 - axes/migrations/0004_auto_20181024_1538.py | 1 - axes/migrations/0005_remove_accessattempt_trusted.py | 1 - axes/migrations/0006_remove_accesslog_trusted.py | 1 - axes/migrations/0007_alter_accessattempt_unique_together.py | 1 - axes/migrations/0008_accessfailurelog.py | 1 - 8 files changed, 8 deletions(-) diff --git a/axes/migrations/0001_initial.py b/axes/migrations/0001_initial.py index 1535183..d361b72 100644 --- a/axes/migrations/0001_initial.py +++ b/axes/migrations/0001_initial.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [] operations = [ diff --git a/axes/migrations/0002_auto_20151217_2044.py b/axes/migrations/0002_auto_20151217_2044.py index 62bc371..0ed902a 100644 --- a/axes/migrations/0002_auto_20151217_2044.py +++ b/axes/migrations/0002_auto_20151217_2044.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [("axes", "0001_initial")] operations = [ diff --git a/axes/migrations/0003_auto_20160322_0929.py b/axes/migrations/0003_auto_20160322_0929.py index 18e3941..37106ab 100644 --- a/axes/migrations/0003_auto_20160322_0929.py +++ b/axes/migrations/0003_auto_20160322_0929.py @@ -2,7 +2,6 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [("axes", "0002_auto_20151217_2044")] operations = [ diff --git a/axes/migrations/0004_auto_20181024_1538.py b/axes/migrations/0004_auto_20181024_1538.py index fc88826..2d177d6 100644 --- a/axes/migrations/0004_auto_20181024_1538.py +++ b/axes/migrations/0004_auto_20181024_1538.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [("axes", "0003_auto_20160322_0929")] operations = [ diff --git a/axes/migrations/0005_remove_accessattempt_trusted.py b/axes/migrations/0005_remove_accessattempt_trusted.py index c7711fb..3716f25 100644 --- a/axes/migrations/0005_remove_accessattempt_trusted.py +++ b/axes/migrations/0005_remove_accessattempt_trusted.py @@ -2,7 +2,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [("axes", "0004_auto_20181024_1538")] operations = [migrations.RemoveField(model_name="accessattempt", name="trusted")] diff --git a/axes/migrations/0006_remove_accesslog_trusted.py b/axes/migrations/0006_remove_accesslog_trusted.py index 347e0f0..014abb1 100644 --- a/axes/migrations/0006_remove_accesslog_trusted.py +++ b/axes/migrations/0006_remove_accesslog_trusted.py @@ -4,7 +4,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [("axes", "0005_remove_accessattempt_trusted")] operations = [migrations.RemoveField(model_name="accesslog", name="trusted")] diff --git a/axes/migrations/0007_alter_accessattempt_unique_together.py b/axes/migrations/0007_alter_accessattempt_unique_together.py index 66927f9..508c4fd 100644 --- a/axes/migrations/0007_alter_accessattempt_unique_together.py +++ b/axes/migrations/0007_alter_accessattempt_unique_together.py @@ -26,7 +26,6 @@ def deduplicate_attempts(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ("axes", "0006_remove_accesslog_trusted"), ] diff --git a/axes/migrations/0008_accessfailurelog.py b/axes/migrations/0008_accessfailurelog.py index ac7c549..5d6d0e2 100644 --- a/axes/migrations/0008_accessfailurelog.py +++ b/axes/migrations/0008_accessfailurelog.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ("axes", "0007_alter_accessattempt_unique_together"), ] From c98d09f67afa58a80c798be1e43d4dfb19f8714d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:15:06 +0000 Subject: [PATCH 170/372] Bump tox from 4.4.4 to 4.4.6 Bumps [tox](https://github.com/tox-dev/tox) from 4.4.4 to 4.4.6. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.4...4.4.6) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4484d52..df59de9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.4 +tox==4.4.6 From d661c32d7726022380f215c62f22f104a10a7da8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:58:34 +0000 Subject: [PATCH 171/372] Bump coverage from 7.2.0 to 7.2.1 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.0 to 7.2.1. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.0...7.2.1) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index cc1c58a..545f060 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==7.2.0 +coverage==7.2.1 pytest==7.2.1 pytest-cov==4.0.0 pytest-django==4.5.2 From 068f5f8b9d382468373004c3e6a5f72dfbf14adb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 14:21:20 +0100 Subject: [PATCH 172/372] Bump pytest from 7.2.1 to 7.2.2 (#1013) Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.1 to 7.2.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.2.1...7.2.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 545f060..c2b878d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==7.2.1 -pytest==7.2.1 +pytest==7.2.2 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.10.0 From 21c468f8e0da439ab9ea5995fbde01822b7d8553 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:11:01 +0100 Subject: [PATCH 173/372] Bump mypy from 1.0.0 to 1.1.1 (#1014) Bumps [mypy](https://github.com/python/mypy) from 1.0.0 to 1.1.1. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v1.0.0...v1.1.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 4adfb71..ee7af78 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.1.0 -mypy==1.0.0 +mypy==1.1.1 prospector==1.9.0 types-pkg_resources # Type stub From 1d23bab33bb8b4dbb310a18b8bcc1bd61366a840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 14:14:19 +0100 Subject: [PATCH 174/372] Bump tox from 4.4.6 to 4.4.7 (#1017) Bumps [tox](https://github.com/tox-dev/tox) from 4.4.6 to 4.4.7. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.6...4.4.7) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index df59de9..a2ad5d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.6 +tox==4.4.7 From 55bf2956867f87dbed89a3cdbe83f3830fc8f9a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:45:18 +0100 Subject: [PATCH 175/372] Bump coverage from 7.2.1 to 7.2.2 (#1018) Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.1 to 7.2.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.1...7.2.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c2b878d..ce8e19d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==7.2.1 +coverage==7.2.2 pytest==7.2.2 pytest-cov==4.0.0 pytest-django==4.5.2 From dccbba7764d66837aa1c965dd515a92c3b609697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:41:44 +0000 Subject: [PATCH 176/372] Bump tox from 4.4.7 to 4.4.8 Bumps [tox](https://github.com/tox-dev/tox) from 4.4.7 to 4.4.8. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.7...4.4.8) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a2ad5d0..927851c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.7 +tox==4.4.8 From 7ae0af2a1e3573bcdf105c0b3a5a4201680cef74 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 29 Mar 2023 17:35:58 +0500 Subject: [PATCH 177/372] add AXES_CLIENT_IP_CALLABLE setting --- axes/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axes/conf.py b/axes/conf.py index 745024f..c3999e5 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -1,7 +1,6 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ - # disable plugin when set to False settings.AXES_ENABLED = getattr(settings, "AXES_ENABLED", True) @@ -52,6 +51,9 @@ settings.AXES_WHITELIST_CALLABLE = getattr(settings, "AXES_WHITELIST_CALLABLE", # return custom lockout response if configured settings.AXES_LOCKOUT_CALLABLE = getattr(settings, "AXES_LOCKOUT_CALLABLE", None) +# use a provided callable to get client ip address +settings.AXES_CLIENT_IP_CALLABLE = getattr(settings, "AXES_CLIENT_IP_CALLABLE", None) + # reset the number of failed attempts after one successful attempt settings.AXES_RESET_ON_SUCCESS = getattr(settings, "AXES_RESET_ON_SUCCESS", False) From c8bfbe603ac38fb9478ad3c4c5c525dabefa707e Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 29 Mar 2023 17:36:47 +0500 Subject: [PATCH 178/372] use `AXES_CLIENT_IP_CALLABLE` in get_client_ip_address --- axes/helpers.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index e40a2b3..457354d 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -6,9 +6,9 @@ from typing import Callable, Optional, Type, Union from urllib.parse import urlencode import ipware.ip -from django.core.cache import caches, BaseCache +from django.core.cache import BaseCache, caches from django.http import HttpRequest, HttpResponse, JsonResponse, QueryDict -from django.shortcuts import render, redirect +from django.shortcuts import redirect, render from django.utils.module_loading import import_string from axes.conf import settings @@ -152,10 +152,27 @@ def get_client_ip_address(request: HttpRequest) -> str: """ Get client IP address as configured by the user. - The django-ipware package is used for address resolution - and parameters can be configured in the Axes package. + The order of preference for address resolution is as follows: + + 1. If configured, use ``AXES_CLIENT_IP_CALLABLE``, and supply ``request`` as argument + 2. Use django-ipware package (parameters can be configured in the Axes package) + + :param request: incoming Django ``HttpRequest`` or similar object from authentication backend or other source """ + if settings.AXES_CLIENT_IP_CALLABLE: + log.debug("Using settings.AXES_CLIENT_IP_CALLABLE to get username") + + if callable(settings.AXES_CLIENT_IP_CALLABLE): + return settings.AXES_CLIENT_IP_CALLABLE( # pylint: disable=not-callable + request + ) + if isinstance(settings.AXES_CLIENT_IP_CALLABLE, str): + return import_string(settings.AXES_CLIENT_IP_CALLABLE)(request) + raise TypeError( + "settings.AXES_CLIENT_IP_CALLABLE needs to be a string, callable, or None." + ) + client_ip_address, _ = ipware.ip.get_client_ip( request, proxy_order=settings.AXES_PROXY_ORDER, From 91bc75f4a0844d73db96a4ccd869a3adc9e4ad1e Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 29 Mar 2023 17:38:09 +0500 Subject: [PATCH 179/372] add info to docs about `AXES_CLIENT_IP_CALLABLE` --- docs/4_configuration.rst | 2 ++ docs/5_customization.rst | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index 6228d9e..d9071a6 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -55,6 +55,8 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_LOCKOUT_CALLABLE | None | A callable or a string path to callable that takes two arguments returns a response. For example: ``def generate_lockout_response(request: HttpRequest, credentials: dict) -> HttpResponse: ...``. This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. If not callable is defined, then the default implementation in ``axes.helpers.get_lockout_response`` is used for determining the correct lockout response that is sent to the requesting client. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_CLIENT_IP_CALLABLE | None | A callable or a string path to callable that takes two arguments returns a response. For example: ``def get_ip(request: HttpRequest) -> str: ...``. This can be any callable similarly to ``AXES_USERNAME_CALLABLE``. If not callable is defined, then the default implementation in ``axes.helpers.get_client_ip_address`` is used. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_PASSWORD_FORM_FIELD | 'password' | The name of the form or credentials field that contains your users password. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_SENSITIVE_PARAMETERS | [] | Configures POST and GET parameter values (in addition to the value of ``AXES_PASSWORD_FORM_FIELD``) to mask in login attempt logging. | diff --git a/docs/5_customization.rst b/docs/5_customization.rst index 44b3836..47f2fd5 100644 --- a/docs/5_customization.rst +++ b/docs/5_customization.rst @@ -173,3 +173,18 @@ An example of usage could be e.g. a custom view for processing lockouts. ``settings.py``:: AXES_LOCKOUT_CALLABLE = "example.views.lockout" + + +Customizing client ip address lookups +----------------------------- + +Axes can be configured with ``AXES_CLIENT_IP_CALLABLE`` to use custom client ip address lookup logic. + +``example/utils.py``:: + + def get_client_ip(request): + return request.META.get("REMOTE_ADDR") + +``settings.py``:: + + AXES_LOCKOUT_CALLABLE = "example.utils.get_client_ip" From 791cce011dd1197019951ee76e51b2d3f7eb06f0 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 29 Mar 2023 17:39:06 +0500 Subject: [PATCH 180/372] add ClientIpAddressTestCase --- tests/test_helpers.py | 54 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 1da372d..fc4b5a3 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -3,16 +3,18 @@ from hashlib import sha256 from unittest.mock import patch from django.contrib.auth import get_user_model -from django.http import JsonResponse, HttpResponseRedirect, HttpResponse, HttpRequest -from django.test import override_settings, RequestFactory +from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse +from django.test import RequestFactory, override_settings from axes.apps import AppConfig from axes.helpers import ( + cleanse_parameters, get_cache_timeout, + get_client_cache_key, + get_client_ip_address, + get_client_parameters, get_client_str, get_client_username, - get_client_cache_key, - get_client_parameters, get_cool_off, get_cool_off_iso8601, get_lockout_response, @@ -23,7 +25,6 @@ from axes.helpers import ( is_ip_address_in_whitelist, is_user_attempt_whitelisted, toggleable, - cleanse_parameters, ) from axes.models import AccessAttempt from tests.base import AxesTestCase @@ -341,7 +342,11 @@ class ClientParametersTestCase(AxesTestCase): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent), [ - {"username": self.username, "ip_address": self.ip_address, "user_agent": self.user_agent}, + { + "username": self.username, + "ip_address": self.ip_address, + "user_agent": self.user_agent, + }, ], ) @@ -553,6 +558,43 @@ def get_username(request, credentials: dict) -> str: return "username" +def get_ip(request: HttpRequest) -> str: + return "127.0.0.1" + + +class ClientIpAddressTestCase(AxesTestCase): + @override_settings(AXES_CLIENT_IP_CALLABLE=get_ip) + def test_get_client_ip_address(self): + self.assertEqual(get_client_ip_address(HttpRequest()), "127.0.0.1") + + @override_settings(AXES_CLIENT_IP_CALLABLE="tests.test_helpers.get_ip") + def test_get_client_ip_address_str(self): + self.assertEqual(get_client_ip_address(HttpRequest()), "127.0.0.1") + + @override_settings( + AXES_CLIENT_IP_CALLABLE=lambda request: "127.0.0.1" + ) # pragma: no cover + def test_get_client_ip_address_lambda(self): + self.assertEqual(get_client_ip_address(HttpRequest()), "127.0.0.1") + + @override_settings(AXES_CLIENT_IP_CALLABLE=True) + def test_get_client_ip_address_not_callable(self): + with self.assertRaises(TypeError): + get_client_ip_address(HttpRequest()) + + @override_settings(AXES_CLIENT_IP_CALLABLE=lambda: None) # pragma: no cover + def test_get_client_ip_address_invalid_callable_too_few_arguments(self): + with self.assertRaises(TypeError): + get_client_ip_address(HttpRequest()) + + @override_settings( + AXES_CLIENT_IP_CALLABLE=lambda request, extra: None + ) # pragma: no cover + def test_get_client_ip_address_invalid_callable_too_many_arguments(self): + with self.assertRaises(TypeError): + get_client_ip_address(HttpRequest()) + + class IPWhitelistTestCase(AxesTestCase): def setUp(self): self.request = HttpRequest() From 6f629f64c99394c8d8857803c0c2a0ccfc4ba813 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:58:12 +0000 Subject: [PATCH 181/372] Bump black from 23.1.0 to 23.3.0 Bumps [black](https://github.com/psf/black) from 23.1.0 to 23.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.1.0...23.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index ee7af78..88ba230 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.1.0 +black==23.3.0 mypy==1.1.1 prospector==1.9.0 types-pkg_resources # Type stub From 25f690389925b2e545cbf2bbce0d30dab7dcd3d4 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 1 Apr 2023 16:28:07 +0500 Subject: [PATCH 182/372] fix logout tests --- tests/test_logging.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_logging.py b/tests/test_logging.py index ae2a7c6..68f2028 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -64,7 +64,7 @@ class AccessLogTestCase(AxesTestCase): self.login(is_valid_username=True, is_valid_password=True) self.assertIsNone(AccessLog.objects.latest("id").logout_time) - response = self.client.get(reverse("admin:logout")) + response = self.client.post(reverse("admin:logout")) self.assertContains(response, "Logged out") self.assertIsNotNone(AccessLog.objects.latest("id").logout_time) @@ -85,7 +85,7 @@ class AccessLogTestCase(AxesTestCase): AccessLog.objects.all().delete() response = self.login(is_valid_username=True, is_valid_password=True) - response = self.client.get(reverse("admin:logout")) + response = self.client.post(reverse("admin:logout")) self.assertEqual(AccessLog.objects.all().count(), 0) self.assertContains(response, "Logged out", html=True) @@ -108,7 +108,7 @@ class AccessLogTestCase(AxesTestCase): AccessLog.objects.all().delete() response = self.login(is_valid_username=True, is_valid_password=True) - response = self.client.get(reverse("admin:logout")) + response = self.client.post(reverse("admin:logout")) self.assertEqual(AccessLog.objects.count(), 0) self.assertContains(response, "Logged out", html=True) From e56f4c92716f79250ab526c800cd598cd51dcfff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 2 Apr 2023 20:58:58 +0300 Subject: [PATCH 183/372] Version 5.41.0 --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 47d01e2..ac112c0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,14 @@ Changes ======= +5.41.0 (2023-04-02) +------------------- + +- Fix tests. [hirotasoshu] +- Add ``AXES_CLIENT_CALLABLE`` setting. [hirotasoshu] +- Update Python, Django, and package versions. [hramezani] + + 5.40.1 (2022-11-24) ------------------- From 15dbdde903b312fd25cef332d4cb0bbfdd0e2381 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 12:57:12 +0000 Subject: [PATCH 184/372] Bump tox from 4.4.8 to 4.4.11 Bumps [tox](https://github.com/tox-dev/tox) from 4.4.8 to 4.4.11. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.8...4.4.11) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 927851c..e9a40db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.8 +tox==4.4.11 From bad728bee8459c92caaf164e1a70f697a38b159a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:57:17 +0000 Subject: [PATCH 185/372] Bump coverage from 7.2.2 to 7.2.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.2 to 7.2.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.2...7.2.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index ce8e19d..5607d98 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ -e . -coverage==7.2.2 +coverage==7.2.3 pytest==7.2.2 pytest-cov==4.0.0 pytest-django==4.5.2 From 632cd43222c06f1d671acffc38131606d68f4aa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:57:26 +0000 Subject: [PATCH 186/372] Bump mypy from 1.1.1 to 1.2.0 Bumps [mypy](https://github.com/python/mypy) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v1.1.1...v1.2.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 88ba230..7a7c2d3 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.1.1 +mypy==1.2.0 prospector==1.9.0 types-pkg_resources # Type stub From d3fc47b05b8d9cd552a744f051537e3fe68eb3f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 12:57:26 +0000 Subject: [PATCH 187/372] Bump pytest from 7.2.2 to 7.3.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.2 to 7.3.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.2.2...7.3.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 5607d98..f06ba4d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==7.2.3 -pytest==7.2.2 +pytest==7.3.0 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.10.0 From 0e76956a2e43c559ab2cbef5cc1423f6be8e0c83 Mon Sep 17 00:00:00 2001 From: ArtemDemidovAramMeem <114488361+ArtemDemidovAramMeem@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:39:11 +0400 Subject: [PATCH 188/372] Override log handler when using sensitive parameters. Closes #1010 --- axes/helpers.py | 2 +- tests/test_helpers.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/axes/helpers.py b/axes/helpers.py index 457354d..778d582 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -306,7 +306,7 @@ def get_client_str( client_dict = {} for client in client_list: client_dict.update(client) - + client_dict = cleanse_parameters(client_dict.copy()) # Path info is always included as last component in the client string for traceability purposes if path_info and isinstance(path_info, (tuple, list)): path_info = path_info[0] diff --git a/tests/test_helpers.py b/tests/test_helpers.py index fc4b5a3..0222d53 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -269,6 +269,26 @@ class ClientStringTestCase(AxesTestCase): self.email, ) + @override_settings(AXES_SENSITIVE_PARAMETERS=["username"]) + def test_get_client_str_with_sensitive_parameters(self): + username = "test@example.com" + ip_address = "127.0.0.1" + user_agent = "Googlebot/2.1 (+http://www.googlebot.com/bot.html)" + path_info = "/admin/" + + expected = self.get_expected_client_str( + "********************", + ip_address, + user_agent, + path_info, + self.request + ) + actual = get_client_str( + username, ip_address, user_agent, path_info, self.request + ) + + self.assertEqual(expected, actual) + def get_dummy_client_str(username, ip_address, user_agent, path_info, request): return "client string" From 8b9474079f089921909d0421b6c928fd3db2a232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 16 Apr 2023 19:53:06 +0300 Subject: [PATCH 189/372] Version 5.41.1 --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ac112c0..4b1f787 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,11 @@ Changes ======= +5.41.1 (2023-04-16) +------------------- + +- Fix sensitive parameter logging for database handler. [stereodamage] + 5.41.0 (2023-04-02) ------------------- From 498aeac86d86a4192089468bc6f9cdaf179d5a0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:57:12 +0000 Subject: [PATCH 190/372] Bump tox from 4.4.11 to 4.4.12 Bumps [tox](https://github.com/tox-dev/tox) from 4.4.11 to 4.4.12. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.11...4.4.12) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e9a40db..7fdd048 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.11 +tox==4.4.12 From c1175982e83d778fcc6bf940203bbab802f53b4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:38:55 +0330 Subject: [PATCH 191/372] Bump pytest from 7.3.0 to 7.3.1 (#1034) Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.0 to 7.3.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.3.0...7.3.1) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f06ba4d..7c28881 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . coverage==7.2.3 -pytest==7.3.0 +pytest==7.3.1 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.10.0 From 6aee1cb8e79d975c4410d1da1295c90e9895e98b Mon Sep 17 00:00:00 2001 From: Joe Wesch Date: Tue, 18 Apr 2023 14:02:15 +0000 Subject: [PATCH 192/372] Adds Django Auth LDAP as compatible --- docs/6_integration.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/6_integration.rst b/docs/6_integration.rst index 4844062..d7aa503 100644 --- a/docs/6_integration.rst +++ b/docs/6_integration.rst @@ -20,6 +20,7 @@ Django Allauth |check| Django Simple Captcha |check| Django OAuth Toolkit |check| Django Reversion |check| +Django Auth LDAP |check| ======================= ============= ============ ============ ============== .. |check| unicode:: U+2713 From c036149c6c246eb0ba2a9455610a02a858fd007b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:57:15 +0000 Subject: [PATCH 193/372] Bump tox from 4.4.12 to 4.5.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.4.12 to 4.5.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.4.12...4.5.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7fdd048..09826ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.4.12 +tox==4.5.0 From 8294fdf7562474f76795a743c71245f43ffd98b5 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Mon, 24 Apr 2023 11:40:00 +0500 Subject: [PATCH 194/372] feat!: set default response code to 429 --- axes/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axes/conf.py b/axes/conf.py index c3999e5..eea9b46 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -141,7 +141,7 @@ settings.AXES_SENSITIVE_PARAMETERS = getattr( settings.AXES_CLIENT_STR_CALLABLE = getattr(settings, "AXES_CLIENT_STR_CALLABLE", None) # set the HTTP response code given by too many requests -settings.AXES_HTTP_RESPONSE_CODE = getattr(settings, "AXES_HTTP_RESPONSE_CODE", 403) +settings.AXES_HTTP_RESPONSE_CODE = getattr(settings, "AXES_HTTP_RESPONSE_CODE", 429) # If True, a failed login attempt during lockout will reset the cool off period settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT = getattr( From a4806ba6b4f83a82a89f7418ebb2dda22f480bd1 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Mon, 24 Apr 2023 11:41:52 +0500 Subject: [PATCH 195/372] docs: update AXES_HTTP_RESPONSE_CODE entry in configuration --- docs/4_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index d9071a6..ead231e 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -79,7 +79,7 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_ALLOWED_CORS_ORIGINS | "*" | Configures lockout response CORS headers for XHR requests. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_HTTP_RESPONSE_CODE | 403 | Sets the http response code returned when ``AXES_FAILURE_LIMIT`` is reached. For example: ``AXES_HTTP_RESPONSE_CODE = 429`` | +| AXES_HTTP_RESPONSE_CODE | 429 | Sets the http response code returned when ``AXES_FAILURE_LIMIT`` is reached. For example: ``AXES_HTTP_RESPONSE_CODE = 403`` | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT | True | If ``True``, a failed login attempt during lockout will reset the cool off period. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ From 5fb675b58884ecf84522751894faf150b76a4b33 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Mon, 24 Apr 2023 11:44:21 +0500 Subject: [PATCH 196/372] test: change status code to 429 --- tests/base.py | 2 +- tests/test_decorators.py | 2 +- tests/test_helpers.py | 6 +++--- tests/test_login.py | 4 ++-- tests/test_middleware.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/base.py b/tests/base.py index be5cd56..3027fce 100644 --- a/tests/base.py +++ b/tests/base.py @@ -48,7 +48,7 @@ class AxesTestCase(TestCase): STATUS_SUCCESS = 200 ALLOWED = 302 - BLOCKED = 403 + BLOCKED = 429 def setUp(self): """ diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 7de9c26..f57bcbc 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -8,7 +8,7 @@ from tests.base import AxesTestCase class DecoratorTestCase(AxesTestCase): SUCCESS_RESPONSE = HttpResponse(status=200, content="Dispatched") - LOCKOUT_RESPONSE = HttpResponse(status=403, content="Locked out") + LOCKOUT_RESPONSE = HttpResponse(status=429, content="Locked out") def setUp(self): self.request = MagicMock() diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 0222d53..15e0746 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -791,12 +791,12 @@ class AxesLockoutTestCase(AxesTestCase): def test_get_lockout_response(self): response = get_lockout_response(self.request, self.credentials) - self.assertEqual(403, response.status_code) + self.assertEqual(429, response.status_code) - @override_settings(AXES_HTTP_RESPONSE_CODE=429) + @override_settings(AXES_HTTP_RESPONSE_CODE=403) def test_get_lockout_response_with_custom_http_response_code(self): response = get_lockout_response(self.request, self.credentials) - self.assertEqual(429, response.status_code) + self.assertEqual(403, response.status_code) @override_settings(AXES_LOCKOUT_CALLABLE=mock_get_lockout_response) def test_get_lockout_response_override_callable(self): diff --git a/tests/test_login.py b/tests/test_login.py index 29211e2..94d870f 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -84,7 +84,7 @@ class DatabaseLoginTestCase(AxesTestCase): LOGIN_FORM_KEY = '' ATTEMPT_NOT_BLOCKED = 200 ALLOWED = 302 - BLOCKED = 403 + BLOCKED = 429 def _login(self, username, password, ip_addr="127.0.0.1", user_agent="test-browser", **kwargs): """ @@ -197,7 +197,7 @@ class DatabaseLoginTestCase(AxesTestCase): # So, we shouldn't have gotten a lock-out yet. # But we should get one now response = self.login(is_valid_username=True, is_valid_password=False) - self.assertContains(response, self.LOCKED_MESSAGE, status_code=403) + self.assertContains(response, self.LOCKED_MESSAGE, status_code=429) @override_settings(AXES_ONLY_USER_FAILURES=True) def test_lockout_by_only_user_failures(self): diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 8b098d1..cf88927 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -12,7 +12,7 @@ def get_username(request, credentials: dict) -> str: class MiddlewareTestCase(AxesTestCase): STATUS_SUCCESS = 200 - STATUS_LOCKOUT = 403 + STATUS_LOCKOUT = 429 def setUp(self): self.request = HttpRequest() From f491f3dea86d0ef007dcc934a750db1819e82e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 25 Apr 2023 19:44:29 +0300 Subject: [PATCH 197/372] Version 6.0.0b1 --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4b1f787..9d15911 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,12 @@ Changes ======= +6.0.0b1 (2023-04-25) +-------------------- + +- Set 429 as the default lockout response code. [hirotasoshu] + + 5.41.1 (2023-04-16) ------------------- From 128b5c6e2578f42c8059270531d654d604928760 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 26 Apr 2023 00:34:40 +0500 Subject: [PATCH 198/372] chore: add venv to .gitignore Add all environment related things, copy from https://github.com/github/gitignore/blob/main/Python.gitignore --- .gitignore | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 78c025f..bdf9645 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,11 @@ docs/_build test.db .eggs pip-wheel-metadata -.vscode/ \ No newline at end of file +.vscode/ +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ From 833ef9669f7f6081ee19c760d26de00f4c49a168 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 26 Apr 2023 23:50:12 +0500 Subject: [PATCH 199/372] docs: add CONTRIBUTING.rst --- CONTRIBUTING.rst | 72 ++++++++++++++++++++++++++++++++++++++++++ README.rst | 27 ++-------------- docs/9_development.rst | 45 +------------------------- 3 files changed, 75 insertions(+), 69 deletions(-) create mode 100644 CONTRIBUTING.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..5744708 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,72 @@ +.. image:: https://jazzband.co/static/img/jazzband.svg + :target: https://jazzband.co/ + :alt: Jazzband + +This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. + + +Contributions +============= + +All contributions are welcome! + +It is best to separate proposed changes and PRs into small, distinct patches +by type so that they can be merged faster into upstream and released quicker. + +One way to organize contributions would be to separate PRs for e.g. + +* bugfixes, +* new features, +* code and design improvements, +* documentation improvements, or +* tooling and CI improvements. + +Merging contributions requires passing the checks configured +with the CI. This includes running tests and linters successfully +on the currently officially supported Python and Django versions. + + +Development +=========== + +You can contribute to this project forking it from GitHub and sending pull requests. + +First `fork `_ the +`repository `_ and then clone it:: + + $ git clone git@github.com:/django-axes.git + +Initialize a virtual environment for development purposes:: + + $ mkdir -p ~/.virtualenvs + $ python3 -m venv ~/.virtualenvs/django-axes + $ source ~/.virtualenvs/django-axes/bin/activate + +Then install the necessary requirements:: + + $ cd django-axes + $ pip install -r requirements.txt + +Unit tests are located in the ``axes/tests`` folder and can be easily run with the pytest tool:: + + $ pytest + +Prospector runs a number of source code style, safety, and complexity checks:: + + $ prospector + +Mypy runs static typing checks to verify the source code type annotations and correctness:: + + $ mypy . + +Before committing, you can run all the above tests against all supported Python and Django versions with tox:: + + $ tox + +Tox runs the same test set that is run by GitHub Actions, and your code should be good to go if it passes. + +If you wish to limit the testing to specific environment(s), you can parametrize the tox run:: + + $ tox -e py39-django32 + +After you have pushed your changes, open a pull request on GitHub for getting your code upstreamed. diff --git a/README.rst b/README.rst index 25f63a8..44a6834 100644 --- a/README.rst +++ b/README.rst @@ -77,30 +77,7 @@ If you have questions or have trouble using the app please file a bug report at: https://github.com/jazzband/django-axes/issues -Contributions +Contributing ------------- -All contributions are welcome! - -It is best to separate proposed changes and PRs into small, distinct patches -by type so that they can be merged faster into upstream and released quicker. - -One way to organize contributions would be to separate PRs for e.g. - -* bugfixes, -* new features, -* code and design improvements, -* documentation improvements, or -* tooling and CI improvements. - -Merging contributions requires passing the checks configured -with the CI. This includes running tests and linters successfully -on the currently officially supported Python and Django versions. - -The test automation is run automatically with GitHub Actions, but you can -run it locally with the ``tox`` command before pushing commits. - -Please note that this is a `Jazzband `_ project. -By contributing you agree to abide by the -`Contributor Code of Conduct `_ -and follow the `guidelines `_. +See `CONTRIBUTING ` diff --git a/docs/9_development.rst b/docs/9_development.rst index 560d58a..759113b 100644 --- a/docs/9_development.rst +++ b/docs/9_development.rst @@ -1,46 +1,3 @@ .. _development: -Development -=========== - -You can contribute to this project forking it from GitHub and sending pull requests. - -First `fork `_ the -`repository `_ and then clone it:: - - $ git clone git@github.com:/django-axes.git - -Initialize a virtual environment for development purposes:: - - $ mkdir -p ~/.virtualenvs - $ python3 -m venv ~/.virtualenvs/django-axes - $ source ~/.virtualenvs/django-axes/bin/activate - -Then install the necessary requirements:: - - $ cd django-axes - $ pip install -r requirements.txt - -Unit tests are located in the ``axes/tests`` folder and can be easily run with the pytest tool:: - - $ pytest - -Prospector runs a number of source code style, safety, and complexity checks:: - - $ prospector - -Mypy runs static typing checks to verify the source code type annotations and correctness:: - - $ mypy . - -Before committing, you can run all the above tests against all supported Python and Django versions with tox:: - - $ tox - -Tox runs the same test set that is run by GitHub Actions, and your code should be good to go if it passes. - -If you wish to limit the testing to specific environment(s), you can parametrize the tox run:: - - $ tox -e py39-django32 - -After you have pushed your changes, open a pull request on GitHub for getting your code upstreamed. +.. include:: ../CONTRIBUTING.rst From 3c1bb683ffd42c6e7288da3e82c74af9100899aa Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 26 Apr 2023 23:53:37 +0500 Subject: [PATCH 200/372] docs: fix link to CONTRIBUTING in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 44a6834..c7e966d 100644 --- a/README.rst +++ b/README.rst @@ -80,4 +80,4 @@ https://github.com/jazzband/django-axes/issues Contributing ------------- -See `CONTRIBUTING ` +See `CONTRIBUTING `__. From 8c9b75343098e20a3ef0126cb09dd5f7da660244 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 26 Apr 2023 23:59:23 +0500 Subject: [PATCH 201/372] docs: rename 9_development.rst to 9_contributing.rst --- docs/{9_development.rst => 9_contributing.rst} | 2 +- docs/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename docs/{9_development.rst => 9_contributing.rst} (65%) diff --git a/docs/9_development.rst b/docs/9_contributing.rst similarity index 65% rename from docs/9_development.rst rename to docs/9_contributing.rst index 759113b..4628182 100644 --- a/docs/9_development.rst +++ b/docs/9_contributing.rst @@ -1,3 +1,3 @@ -.. _development: +.. _contributing: .. include:: ../CONTRIBUTING.rst diff --git a/docs/index.rst b/docs/index.rst index 19972ee..72b3162 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,7 +18,7 @@ Contents 6_integration 7_architecture 8_reference - 9_development + 9_contributing 10_changelog From ced022adfd976d879d5e8c25453a03980635afbb Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 27 Apr 2023 18:47:38 +0500 Subject: [PATCH 202/372] docs: fix divider line after contributing section --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c7e966d..49d94b3 100644 --- a/README.rst +++ b/README.rst @@ -78,6 +78,6 @@ https://github.com/jazzband/django-axes/issues Contributing -------------- +------------ See `CONTRIBUTING `__. From f376ab6c7fdeb163b3c2f46d3d9099087fd8b055 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:58:26 +0000 Subject: [PATCH 203/372] Bump tox from 4.5.0 to 4.5.1 Bumps [tox](https://github.com/tox-dev/tox) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.5.0...4.5.1) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09826ba..7c4c941 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.0 -tox==4.5.0 +tox==4.5.1 From f13475803be088c09399ab36cf781d1eecc60254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 12:29:36 +0300 Subject: [PATCH 204/372] Add Django 4.2 support to test matrix --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 346484b..baaa23c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ envlist = py{37,38,39,310,py38}-dj32 py{38,39,310,py38}-dj40 py{38,39,310,311,py38}-dj41 + py{38,39,310,311,py38}-dj42 py{310,311,py38}-djmain py310-djqa @@ -30,6 +31,7 @@ DJANGO = 3.2: dj32 4.0: dj40 4.1: dj41 + 4.2: dj42 main: djmain qa: djqa @@ -40,6 +42,7 @@ deps = dj32: django>=3.2,<3.3 dj40: django>=4.0,<4.1 dj41: django>=4.1,<4.2 + dj42: django>=4.1,<4.2 djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = true commands = pytest From d12ee8270ce30a443a454f3df75399480bf5a791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 12:29:58 +0300 Subject: [PATCH 205/372] Drop Django 4.1 support from test matrix --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index baaa23c..796880f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ legacy_tox_ini = """ [tox] envlist = py{37,38,39,310,py38}-dj32 - py{38,39,310,py38}-dj40 py{38,39,310,311,py38}-dj41 py{38,39,310,311,py38}-dj42 py{310,311,py38}-djmain @@ -29,7 +28,6 @@ python = [gh-actions:env] DJANGO = 3.2: dj32 - 4.0: dj40 4.1: dj41 4.2: dj42 main: djmain @@ -40,7 +38,6 @@ DJANGO = deps = -r requirements-test.txt dj32: django>=3.2,<3.3 - dj40: django>=4.0,<4.1 dj41: django>=4.1,<4.2 dj42: django>=4.1,<4.2 djmain: https://github.com/django/django/archive/main.tar.gz From 1d13338cdb1562760f2808f137aaafd399c1f6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 12:30:22 +0300 Subject: [PATCH 206/372] Drop PyPy 3.8 outcome ignores from test matrix --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 796880f..a4201c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,13 +48,11 @@ setenv = # Django development version is allowed to fail the test matrix ignore_outcome = djmain: True - pypy38-dj41: True ignore_errors = djmain: True - pypy38-dj41: True # QA runs type checks, linting, and code formatting checks -[testenv:py310-djqa] +[testenv:py311-djqa] deps = -r requirements-qa.txt commands = mypy axes From 700ea46607928e3ade827511d25b220938a402fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 13:08:17 +0300 Subject: [PATCH 207/372] Adjust GitHub and tox matrix to match current version support state --- .github/workflows/test.yml | 24 +++++++++++++----------- pyproject.toml | 6 ++++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 164960f..9ad1ea9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,35 +13,37 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.8'] - django-version: ['3.2', '4.0', '4.1'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + django-version: ['3.2', '4.1', '4.2'] include: # Tox configuration for QA environment - - python-version: '3.10' + - python-version: '3.11' django-version: 'qa' # Django main - - python-version: '3.10' - django-version: 'main' - experimental: true - python-version: '3.11' django-version: 'main' experimental: true + # PyPy 3.8 + - python-version: 'pypy-3.8' + django-version: '3.2' + experimental: true - python-version: 'pypy-3.8' django-version: '4.1' experimental: true + - python-version: 'pypy-3.8' + django-version: '4.2' + experimental: true exclude: - # Exclude Python 3.7 for Django 4.0 and Django main - - python-version: '3.7' - django-version: '4.0' + # Exclude Python 3.7 for Django 4.x and Django main - python-version: '3.7' django-version: '4.1' + - python-version: '3.7' + django-version: '4.2' - python-version: '3.7' django-version: 'main' # Exclude Python 3.11 for Django 3.2 and Django 4.0 - python-version: '3.11' django-version: '3.2' - - python-version: '3.11' - django-version: '4.0' steps: diff --git a/pyproject.toml b/pyproject.toml index a4201c8..070e2c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,8 @@ envlist = py{37,38,39,310,py38}-dj32 py{38,39,310,311,py38}-dj41 py{38,39,310,311,py38}-dj42 - py{310,311,py38}-djmain - py310-djqa + py311-djmain + py311-djqa [gh-actions] python = @@ -48,8 +48,10 @@ setenv = # Django development version is allowed to fail the test matrix ignore_outcome = djmain: True + pypy38: True ignore_errors = djmain: True + pypy38: True # QA runs type checks, linting, and code formatting checks [testenv:py311-djqa] From cd950ddfef23bc5ff4cfe24de0904fc71509e565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 27 Apr 2023 22:53:22 +0300 Subject: [PATCH 208/372] Make ipware an optional dependency Relates to #1038 --- axes/helpers.py | 45 ++++++++++++++++++++++++++++++----------- docs/2_installation.rst | 3 ++- requirements-test.txt | 1 + setup.py | 5 ++++- tests/test_helpers.py | 10 +++++++++ 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index 778d582..8c58411 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -5,7 +5,6 @@ from string import Template from typing import Callable, Optional, Type, Union from urllib.parse import urlencode -import ipware.ip from django.core.cache import BaseCache, caches from django.http import HttpRequest, HttpResponse, JsonResponse, QueryDict from django.shortcuts import redirect, render @@ -16,6 +15,13 @@ from axes.models import AccessBase log = getLogger(__name__) +try: + import ipware.ip + + IPWARE_INSTALLED = True +except ImportError: + IPWARE_INSTALLED = False + def get_cache() -> BaseCache: """ @@ -148,20 +154,24 @@ def get_client_username( return request_data.get(settings.AXES_USERNAME_FORM_FIELD, None) -def get_client_ip_address(request: HttpRequest) -> str: +def get_client_ip_address( + request: HttpRequest, + use_ipware: Optional[bool] = None, +) -> Optional[str]: """ Get client IP address as configured by the user. The order of preference for address resolution is as follows: 1. If configured, use ``AXES_CLIENT_IP_CALLABLE``, and supply ``request`` as argument - 2. Use django-ipware package (parameters can be configured in the Axes package) + 2. If available, use django-ipware package (parameters can be configured in the Axes package) + 3. Use ``request.META.get('REMOTE_ADDR', None)`` as a fallback :param request: incoming Django ``HttpRequest`` or similar object from authentication backend or other source """ if settings.AXES_CLIENT_IP_CALLABLE: - log.debug("Using settings.AXES_CLIENT_IP_CALLABLE to get username") + log.debug("Using settings.AXES_CLIENT_IP_CALLABLE to get client IP address") if callable(settings.AXES_CLIENT_IP_CALLABLE): return settings.AXES_CLIENT_IP_CALLABLE( # pylint: disable=not-callable @@ -173,15 +183,26 @@ def get_client_ip_address(request: HttpRequest) -> str: "settings.AXES_CLIENT_IP_CALLABLE needs to be a string, callable, or None." ) - client_ip_address, _ = ipware.ip.get_client_ip( - request, - proxy_order=settings.AXES_PROXY_ORDER, - proxy_count=settings.AXES_PROXY_COUNT, - proxy_trusted_ips=settings.AXES_PROXY_TRUSTED_IPS, - request_header_order=settings.AXES_META_PRECEDENCE_ORDER, - ) + # Resolve using django-ipware from a configuration flag that can be set to False to explicitly disable + # this is added to both enable or disable the branch when ipware is installed in the test environment + if use_ipware is None: + use_ipware = IPWARE_INSTALLED + if use_ipware: + log.debug("Using django-ipware to get client IP address") - return client_ip_address + client_ip_address, _ = ipware.ip.get_client_ip( + request, + proxy_order=settings.AXES_PROXY_ORDER, + proxy_count=settings.AXES_PROXY_COUNT, + proxy_trusted_ips=settings.AXES_PROXY_TRUSTED_IPS, + request_header_order=settings.AXES_META_PRECEDENCE_ORDER, + ) + return client_ip_address + + log.debug( + "Using request.META.get('REMOTE_ADDR', None) fallback method to get client IP address" + ) + return request.META.get("REMOTE_ADDR", None) def get_client_user_agent(request: HttpRequest) -> str: diff --git a/docs/2_installation.rst b/docs/2_installation.rst index e001768..27d1f45 100644 --- a/docs/2_installation.rst +++ b/docs/2_installation.rst @@ -5,7 +5,8 @@ Installation Axes is easy to install from the PyPI package:: - $ pip install django-axes + $ pip install django-axes[ipware] # use django-ipware for resolving client IP addresses OR + $ pip install django-axes # implement and configure custom AXES_CLIENT_IP_CALLABLE After installing the package, the project settings need to be configured. diff --git a/requirements-test.txt b/requirements-test.txt index 7c28881..a7a440f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,4 +1,5 @@ -e . +django-ipware>=3 coverage==7.2.3 pytest==7.3.1 pytest-cov==4.0.0 diff --git a/setup.py b/setup.py index c7a6d1f..aca2304 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,10 @@ setup( use_scm_version=True, setup_requires=["setuptools_scm"], python_requires=">=3.7", - install_requires=["django>=3.2", "django-ipware>=3", "setuptools"], + install_requires=["django>=3.2", "setuptools"], + extras_require={ + "ipware": "django-ipware>=3", + }, include_package_data=True, packages=find_packages(exclude=["tests"]), classifiers=[ diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 15e0746..2677a17 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -614,6 +614,16 @@ class ClientIpAddressTestCase(AxesTestCase): with self.assertRaises(TypeError): get_client_ip_address(HttpRequest()) + def test_get_client_ip_address_with_ipware(self): + request = HttpRequest() + request.META["REMOTE_ADDR"] = "127.0.0.2" + self.assertEqual(get_client_ip_address(request, use_ipware=True), "127.0.0.2") + + def test_get_client_ip_address_without_ipware(self): + request = HttpRequest() + request.META["REMOTE_ADDR"] = "127.0.0.3" + self.assertEqual(get_client_ip_address(request, use_ipware=False), "127.0.0.3") + class IPWhitelistTestCase(AxesTestCase): def setUp(self): From 31249a594797dad957823d9be3f3a5476b011f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 27 Apr 2023 22:29:49 +0300 Subject: [PATCH 209/372] Update django-ipware configuration flags to new AXES_IPWARE_ prefixes Use explicit new AXES_IPWARE_ referencing configuration flag names in place of the old plain implicit AXES_ name prefixes --- axes/checks.py | 6 ++++ axes/conf.py | 59 ++++++++++++++++++++++++++++------------ axes/helpers.py | 8 +++--- docs/4_configuration.rst | 6 ++-- 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/axes/checks.py b/axes/checks.py index 711bcf8..8746ea6 100644 --- a/axes/checks.py +++ b/axes/checks.py @@ -122,6 +122,12 @@ def axes_deprecation_check(app_configs, **kwargs): # pylint: disable=unused-arg deprecated_settings = [ "AXES_DISABLE_SUCCESS_ACCESS_LOG", "AXES_LOGGER", + # AXES_PROXY_ and AXES_META_ parameters were updated to more explicit + # AXES_IPWARE_PROXY_ and AXES_IPWARE_META_ prefixes in version 6.x + "AXES_PROXY_ORDER", + "AXES_PROXY_COUNT", + "AXES_PROXY_TRUSTED_IPS", + "AXES_META_PRECEDENCE_ORDER", ] for deprecated_setting in deprecated_settings: diff --git a/axes/conf.py b/axes/conf.py index eea9b46..9dab628 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -108,24 +108,6 @@ settings.AXES_PERMALOCK_MESSAGE = getattr( ), ) -# if your deployment is using reverse proxies, set this value to 'left-most' or 'right-most' per your configuration -settings.AXES_PROXY_ORDER = getattr(settings, "AXES_PROXY_ORDER", "left-most") - -# if your deployment is using reverse proxies, set this value to the number of proxies in front of Django -settings.AXES_PROXY_COUNT = getattr(settings, "AXES_PROXY_COUNT", None) - -# if your deployment is using reverse proxies, set to your trusted proxy IP addresses prefixes if needed -settings.AXES_PROXY_TRUSTED_IPS = getattr(settings, "AXES_PROXY_TRUSTED_IPS", None) - -# set to the names of request.META attributes that should be checked for the IP address of the client -# if your deployment is using reverse proxies, ensure that the header attributes are securely set by the proxy -# ensure that the client can not spoof the headers by setting them and sending them through the proxy -settings.AXES_META_PRECEDENCE_ORDER = getattr( - settings, - "AXES_META_PRECEDENCE_ORDER", - getattr(settings, "IPWARE_META_PRECEDENCE_ORDER", ("REMOTE_ADDR",)), -) - # set CORS allowed origins when calling authentication over ajax settings.AXES_ALLOWED_CORS_ORIGINS = getattr(settings, "AXES_ALLOWED_CORS_ORIGINS", "*") @@ -147,3 +129,44 @@ settings.AXES_HTTP_RESPONSE_CODE = getattr(settings, "AXES_HTTP_RESPONSE_CODE", settings.AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT = getattr( settings, "AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT", True ) + + +### +# django-ipware settings for client IP address calculation and proxy detection +# there are old AXES_PROXY_ and AXES_META_ legacy keys present for backwards compatibility +# see https://github.com/un33k/django-ipware for further details +### + +# if your deployment is using reverse proxies, set this value to 'left-most' or 'right-most' per your configuration +settings.AXES_IPWARE_PROXY_ORDER = getattr( + settings, + "AXES_IPWARE_PROXY_ORDER", + getattr(settings, "AXES_PROXY_ORDER", "left-most"), +) + +# if your deployment is using reverse proxies, set this value to the number of proxies in front of Django +settings.AXES_IPWARE_PROXY_COUNT = getattr( + settings, + "AXES_IPWARE_PROXY_COUNT", + getattr(settings, "AXES_PROXY_COUNT", None), +) + +# if your deployment is using reverse proxies, set to your trusted proxy IP addresses prefixes if needed +settings.AXES_IPWARE_PROXY_TRUSTED_IPS = getattr( + settings, + "AXES_IPWARE_PROXY_TRUSTED_IPS", + getattr(settings, "AXES_PROXY_TRUSTED_IPS", None), +) + +# set to the names of request.META attributes that should be checked for the IP address of the client +# if your deployment is using reverse proxies, ensure that the header attributes are securely set by the proxy +# ensure that the client can not spoof the headers by setting them and sending them through the proxy +settings.AXES_IPWARE_META_PRECEDENCE_ORDER = getattr( + settings, + "AXES_IPWARE_META_PRECEDENCE_ORDER", + getattr( + settings, + "AXES_META_PRECEDENCE_ORDER", + getattr(settings, "IPWARE_META_PRECEDENCE_ORDER", ("REMOTE_ADDR",)), + ), +) diff --git a/axes/helpers.py b/axes/helpers.py index 8c58411..331d1a6 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -192,10 +192,10 @@ def get_client_ip_address( client_ip_address, _ = ipware.ip.get_client_ip( request, - proxy_order=settings.AXES_PROXY_ORDER, - proxy_count=settings.AXES_PROXY_COUNT, - proxy_trusted_ips=settings.AXES_PROXY_TRUSTED_IPS, - request_header_order=settings.AXES_META_PRECEDENCE_ORDER, + proxy_order=settings.AXES_IPWARE_PROXY_ORDER, + proxy_count=settings.AXES_IPWARE_PROXY_COUNT, + proxy_trusted_ips=settings.AXES_IPWARE_PROXY_TRUSTED_IPS, + request_header_order=settings.AXES_IPWARE_META_PRECEDENCE_ORDER, ) return client_ip_address diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index ead231e..13091a4 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -103,8 +103,8 @@ and uses some conservative configuration parameters by default for security. If you are using reverse proxies, you will need to configure one or more of the following settings to suit your set up to correctly resolve client IP addresses: -* ``AXES_PROXY_COUNT``: The number of reverse proxies in front of Django as an integer. Default: ``None`` -* ``AXES_META_PRECEDENCE_ORDER``: The names of ``request.META`` attributes as a tuple of strings +* ``AXES_IPWARE_PROXY_COUNT``: The number of reverse proxies in front of Django as an integer. Default: ``None`` +* ``AXES_IPWARE_META_PRECEDENCE_ORDER``: The names of ``request.META`` attributes as a tuple of strings to check to get the client IP address. Check the Django documentation for header naming conventions. Default: ``IPWARE_META_PRECEDENCE_ORDER`` setting if set, else ``('REMOTE_ADDR', )`` @@ -114,7 +114,7 @@ following settings to suit your set up to correctly resolve client IP addresses: .. code-block:: python # refer to the Django request and response objects documentation - AXES_META_PRECEDENCE_ORDER = [ + AXES_IPWARE_META_PRECEDENCE_ORDER = [ 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR', ] From 107b9b8508f5c4c46b6562e467ddafb229dd153b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 14:23:59 +0300 Subject: [PATCH 210/372] Update CHANGES.rst --- CHANGES.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9d15911..ea7cfe6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changes ======= +6.0.0b2 (2023-04-28) +-------------------- + +- Make ``django-ipware`` an optional dependency. Install it with e.g. ``pip install django-axes[ipware]`` package and extras specifier. [aleksihakli] +- Deprecate and rename old configuration flags. Old flags will be removed in or after version ``6.1``. [aleksihakli] + * ``AXES_PROXY_ORDER`` is now ``AXES_IPWARE_PROXY_ORDER``, + * ``AXES_PROXY_COUNT`` is now ``AXES_IPWARE_PROXY_COUNT``, + * ``AXES_IPWARE_PROXY_TRUSTED_IPS`` is now ``AXES_PROXY_TRUSTED_IPS``, and + * ``AXES_IPWARE_META_PRECEDENCE_ORDER`` is now ``AXES_META_PRECEDENCE_ORDER``. + + 6.0.0b1 (2023-04-25) -------------------- From 63c57c898bfb8110299d56c24efcbcdc1d043449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 14:28:51 +0300 Subject: [PATCH 211/372] Correct CHANGES word ordering --- CHANGES.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ea7cfe6..fbdae95 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,10 +8,10 @@ Changes - Make ``django-ipware`` an optional dependency. Install it with e.g. ``pip install django-axes[ipware]`` package and extras specifier. [aleksihakli] - Deprecate and rename old configuration flags. Old flags will be removed in or after version ``6.1``. [aleksihakli] - * ``AXES_PROXY_ORDER`` is now ``AXES_IPWARE_PROXY_ORDER``, - * ``AXES_PROXY_COUNT`` is now ``AXES_IPWARE_PROXY_COUNT``, - * ``AXES_IPWARE_PROXY_TRUSTED_IPS`` is now ``AXES_PROXY_TRUSTED_IPS``, and - * ``AXES_IPWARE_META_PRECEDENCE_ORDER`` is now ``AXES_META_PRECEDENCE_ORDER``. + * ``AXES_PROXY_ORDER`` is now ``AXES_IPWARE_PROXY_ORDER``, + * ``AXES_PROXY_COUNT`` is now ``AXES_IPWARE_PROXY_COUNT``, + * ``AXES_PROXY_TRUSTED_IPS`` is now ``AXES_IPWARE_PROXY_TRUSTED_IPS``, and + * ``AXES_META_PRECEDENCE_ORDER`` is now ``AXES_IPWARE_META_PRECEDENCE_ORDER``. 6.0.0b1 (2023-04-25) From 9b3a75cdbb668e2b35ce05dad7338fbf2652d0f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 12:57:19 +0000 Subject: [PATCH 212/372] Bump coverage from 7.2.3 to 7.2.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.3 to 7.2.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.3...7.2.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index a7a440f..c5a60e7 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.2.3 +coverage==7.2.4 pytest==7.3.1 pytest-cov==4.0.0 pytest-django==4.5.2 From feca1ed6d4a682cd7cd48b4ac7654cdbae7dd669 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 12:58:08 +0000 Subject: [PATCH 213/372] Bump coverage from 7.2.4 to 7.2.5 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.4 to 7.2.5. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.4...7.2.5) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c5a60e7..f6277ab 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.2.4 +coverage==7.2.5 pytest==7.3.1 pytest-cov==4.0.0 pytest-django==4.5.2 From 9924077a2ae3d08756f6ba832505cb4bff62e58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 18:06:08 +0300 Subject: [PATCH 214/372] Rename get_cache_key to get_cache_keys --- axes/handlers/cache.py | 8 ++++---- axes/helpers.py | 2 +- tests/test_helpers.py | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index b85f3f6..97b36f2 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -44,7 +44,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): ) cache_keys.extend( - get_client_cache_key( + get_client_cache_keys( AccessAttempt(username=username, ip_address=ip_address) ) ) @@ -58,7 +58,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return count def get_failures(self, request, credentials: Optional[dict] = None) -> int: - cache_keys = get_client_cache_key(request, credentials) + cache_keys = get_client_cache_keys(request, credentials) failure_count = max( self.cache.get(cache_key, default=0) for cache_key in cache_keys ) @@ -126,7 +126,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): client_str, ) - cache_keys = get_client_cache_key(request, credentials) + cache_keys = get_client_cache_keys(request, credentials) for cache_key in cache_keys: failures = self.cache.get(cache_key, default=0) self.cache.set(cache_key, failures + 1, get_cache_timeout()) @@ -166,7 +166,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): log.info("AXES: Successful login by %s.", client_str) if settings.AXES_RESET_ON_SUCCESS: - cache_keys = get_client_cache_key(request, credentials) + cache_keys = get_client_cache_keys(request, credentials) for cache_key in cache_keys: failures_since_start = self.cache.get(cache_key, default=0) self.cache.delete(cache_key) diff --git a/axes/helpers.py b/axes/helpers.py index 331d1a6..c692a01 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -259,7 +259,7 @@ def make_cache_key_list(filter_kwargs_list): return cache_keys -def get_client_cache_key( +def get_client_cache_keys( request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None, ) -> str: diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 2677a17..a0a5c64 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -10,7 +10,7 @@ from axes.apps import AppConfig from axes.helpers import ( cleanse_parameters, get_cache_timeout, - get_client_cache_key, + get_client_cache_keys, get_client_ip_address, get_client_parameters, get_client_str, @@ -372,7 +372,7 @@ class ClientParametersTestCase(AxesTestCase): class ClientCacheKeyTestCase(AxesTestCase): - def test_get_cache_key(self): + def test_get_cache_keys(self): """ Test the cache key format. """ @@ -386,7 +386,7 @@ class ClientCacheKeyTestCase(AxesTestCase): "/admin/login/", data={"username": self.username, "password": "test"} ) - self.assertEqual([cache_hash_key], get_client_cache_key(request)) + self.assertEqual([cache_hash_key], get_client_cache_keys(request)) # Getting cache key from AccessAttempt Object attempt = AccessAttempt( @@ -400,7 +400,7 @@ class ClientCacheKeyTestCase(AxesTestCase): failures_since_start=0, ) - self.assertEqual([cache_hash_key], get_client_cache_key(attempt)) + self.assertEqual([cache_hash_key], get_client_cache_keys(attempt)) def test_get_cache_key_empty_ip_address(self): """ @@ -420,7 +420,7 @@ class ClientCacheKeyTestCase(AxesTestCase): REMOTE_ADDR=empty_ip_address, ) - self.assertEqual([cache_hash_key], get_client_cache_key(request)) + self.assertEqual([cache_hash_key], get_client_cache_keys(request)) # Getting cache key from AccessAttempt Object attempt = AccessAttempt( @@ -434,7 +434,7 @@ class ClientCacheKeyTestCase(AxesTestCase): failures_since_start=0, ) - self.assertEqual([cache_hash_key], get_client_cache_key(attempt)) + self.assertEqual([cache_hash_key], get_client_cache_keys(attempt)) def test_get_cache_key_credentials(self): """ @@ -454,7 +454,7 @@ class ClientCacheKeyTestCase(AxesTestCase): # Difference between the upper test: new call signature with credentials credentials = {"username": self.username} - self.assertEqual([cache_hash_key], get_client_cache_key(request, credentials)) + self.assertEqual([cache_hash_key], get_client_cache_keys(request, credentials)) # Getting cache key from AccessAttempt Object attempt = AccessAttempt( @@ -467,7 +467,7 @@ class ClientCacheKeyTestCase(AxesTestCase): path_info=request.META.get("PATH_INFO", ""), failures_since_start=0, ) - self.assertEqual([cache_hash_key], get_client_cache_key(attempt)) + self.assertEqual([cache_hash_key], get_client_cache_keys(attempt)) class UsernameTestCase(AxesTestCase): From c3cfb5150a5284b9585d09018c9a486a5ebcfafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 28 Apr 2023 18:07:09 +0300 Subject: [PATCH 215/372] Refactor cache backend to use cache.incr method for request tracking The old cache.set method has problems with correctness as well as performance on higher traffic sites where there are multiple parallel web servers running at the same time which can overwrite each others shared cache --- axes/handlers/cache.py | 20 +++++++++++++------- tests/test_handlers.py | 6 +++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 97b36f2..f5de9b6 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -6,7 +6,7 @@ from axes.handlers.base import AxesBaseHandler, AbstractAxesHandler from axes.helpers import ( get_cache, get_cache_timeout, - get_client_cache_key, + get_client_cache_keys, get_client_str, get_client_username, get_credentials, @@ -110,7 +110,18 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): log.info("AXES: Login failed from whitelisted client %s.", client_str) return - failures_since_start = 1 + self.get_failures(request, credentials) + cache_keys = get_client_cache_keys(request, credentials) + cache_timeout = get_cache_timeout() + failures = [] + for cache_key in cache_keys: + added = self.cache.add(key=cache_key, value=1, timeout=cache_timeout) + if added: + failures.append(1) + else: + failures.append(self.cache.incr(key=cache_key, delta=1)) + self.cache.touch(key=cache_key, timeout=cache_timeout) + + failures_since_start = max(failures) request.axes_failures_since_start = failures_since_start if failures_since_start > 1: @@ -126,11 +137,6 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): client_str, ) - cache_keys = get_client_cache_keys(request, credentials) - for cache_key in cache_keys: - failures = self.cache.get(cache_key, default=0) - self.cache.set(cache_key, failures + 1, get_cache_timeout()) - if ( settings.AXES_LOCK_OUT_AT_FAILURE and failures_since_start >= get_failure_limit(request, credentials) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 5ae36b0..d32af60 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -496,12 +496,12 @@ class AxesCacheHandlerTestCase(AxesHandlerBaseTestCase): "AXES: Username is None and AXES_ONLY_USER_FAILURES is enabled, new record will NOT be created." ) - @patch.object(cache, "set") - def test_user_login_failed_with_none_username(self, cache_set): + @patch.object(cache, "add") + def test_user_login_failed_with_none_username(self, cache_add): credentials = {"username": None, "password": "test"} sender = MagicMock() AxesProxyHandler.user_login_failed(sender, credentials, self.request) - self.assertTrue(cache_set.called) + self.assertTrue(cache_add.called) @override_settings(AXES_HANDLER="axes.handlers.dummy.AxesDummyHandler") From 8386f30dbbad6f534fd434ae31c9ada10942f896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 1 May 2023 21:20:55 +0300 Subject: [PATCH 216/372] Fix type annotations for cache utilities --- axes/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index c692a01..d218337 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -2,7 +2,7 @@ from datetime import timedelta from hashlib import sha256 from logging import getLogger from string import Template -from typing import Callable, Optional, Type, Union +from typing import Callable, Optional, Type, Union, List from urllib.parse import urlencode from django.core.cache import BaseCache, caches @@ -248,7 +248,7 @@ def get_client_parameters(username: str, ip_address: str, user_agent: str) -> li return filter_query -def make_cache_key_list(filter_kwargs_list): +def make_cache_key_list(filter_kwargs_list: List[dict]) -> List[str]: cache_keys = [] for filter_kwargs in filter_kwargs_list: cache_key_components = "".join( @@ -262,7 +262,7 @@ def make_cache_key_list(filter_kwargs_list): def get_client_cache_keys( request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None, -) -> str: +) -> List[str]: """ Build cache key name from request or AccessAttempt object. From 9b7b1e6aac2ee3443beaae9bd1e217960cbc3719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 1 May 2023 21:31:16 +0300 Subject: [PATCH 217/372] Version 6.0.0b3 --- CHANGES.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index fbdae95..8b93b26 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.0b3 (2023-05-01) +-------------------- + +- Use Django ``cache.incr`` API for atomic cached failure counting + [hirotasoshu, aleksihakli] + + 6.0.0b2 (2023-04-28) -------------------- @@ -114,7 +121,7 @@ Changes 5.32.0 (2022-04-08) ------------------- -- Add support for persistent failure logging +- Add support for persistent logging where failed login attempts are persisted in the database until a specific threshold is reached. [p1-gdd] From ad2f21a856e3239c70c6da847f923173a6fe9671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 1 May 2023 21:34:33 +0300 Subject: [PATCH 218/372] Revert language change --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8b93b26..2e4966d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -121,7 +121,7 @@ Changes 5.32.0 (2022-04-08) ------------------- -- Add support for persistent logging +- Add support for persistent failure logging where failed login attempts are persisted in the database until a specific threshold is reached. [p1-gdd] From b985981d119793f4b53d66fb9a5db201e3127915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 1 May 2023 21:39:04 +0300 Subject: [PATCH 219/372] Update supported Django versions specifiers --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aca2304..0b02ab9 100644 --- a/setup.py +++ b/setup.py @@ -48,8 +48,8 @@ setup( "Environment :: Plugins", "Framework :: Django", "Framework :: Django :: 3.2", - "Framework :: Django :: 4.0", "Framework :: Django :: 4.1", + "Framework :: Django :: 4.2", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", From 79660a7cc22610d9ff8474ed2f54e6117794b3da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 12:57:24 +0000 Subject: [PATCH 220/372] Bump prospector from 1.9.0 to 1.10.0 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/v1.9.0...1.10.0) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 7a7c2d3..0fba15f 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 mypy==1.2.0 -prospector==1.9.0 +prospector==1.10.0 types-pkg_resources # Type stub From 9dfea8bf8e94418a72abdd415bed109573dd792e Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 13 May 2023 13:03:45 +0500 Subject: [PATCH 221/372] chore: fix inaccuracies in russian locale --- axes/locale/ru/LC_MESSAGES/django.mo | Bin 1910 -> 2081 bytes axes/locale/ru/LC_MESSAGES/django.po | 68 ++++++++++++++++----------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/axes/locale/ru/LC_MESSAGES/django.mo b/axes/locale/ru/LC_MESSAGES/django.mo index 36806fbaba5a9151844390f50c5b9f1aceb41e37..6341f31a5c65e430fd44d31e5e034397b8cba771 100644 GIT binary patch delta 804 zcmYMxO=uHA6u|LGO;V$_HnFxys;=nGFZ3jMX+4Og9wJr{#X|{65sN8JdK1#5hbk2- zZUpflYIatm!1^COBF;AMEt+af&;U^d9yRK@6G;duXYqS65)r2 z)~k_z?je2N&E*LjJGxuJc*jXY1I8^@hE0d6R2PkYk}*y zkN6vQV~i?8eBTfolJ=Wcu1O^5aet=y&>QLqV*OQmBbt;ZKw~udwvU|=A4yGI} zXtZGZQ;yu11wXZtdnI}~ekq?BbNYreHw#7{$%>tE*P==HXY~BRC-$~1k;Srjjx78y NPdp>RJNIzwz(0e#%%uPT delta 628 zcmXxgJ!n%=6u|NGn!dcMZA@EZ3aQQO-Ys{aeP2z7!w@9J9r2ea0nmb5v=23e9|AkK->4G?>ih6S(Gjv+t@=p zUsPeeg!0yaJf%h;;7u;(93dm\n" "Language-Team: LANGUAGE \n" @@ -18,80 +18,92 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: axes/admin.py:38 +#: axes/admin.py:27 msgid "Form Data" msgstr "Данные формы" -#: axes/admin.py:41 axes/admin.py:95 +#: axes/admin.py:28 axes/admin.py:65 axes/admin.py:100 msgid "Meta Data" msgstr "Метаданные" -#: axes/conf.py:58 +#: axes/conf.py:99 msgid "Account locked: too many login attempts. Please try again later." msgstr "" -"Учетная запись заблокирована: слишком много попыток входа. " -"Повторите попытку позже." +"Учетная запись заблокирована: слишком много попыток входа. Повторите попытку " +"позже." -#: axes/conf.py:61 +#: axes/conf.py:107 msgid "" "Account locked: too many login attempts. Contact an admin to unlock your " "account." msgstr "" -"Учетная запись заблокирована: слишком много попыток входа. " -"Обратитесь к администратору для разблокирования учетной записи." +"Учетная запись заблокирована: слишком много попыток входа. Свяжитесь с " +"администратором, чтобы разблокировать учетную запись." -#: axes/models.py:9 +#: axes/models.py:6 msgid "User Agent" -msgstr "Браузер пользователя" +msgstr "User Agent" -#: axes/models.py:15 +#: axes/models.py:8 msgid "IP Address" -msgstr "Адрес IP" +msgstr "IP Адрес" -#: axes/models.py:21 +#: axes/models.py:10 msgid "Username" -msgstr "Пользователь" +msgstr "Имя пользователя" -#: axes/models.py:35 +#: axes/models.py:12 msgid "HTTP Accept" -msgstr "Запрос HTTP" +msgstr "HTTP Accept" -#: axes/models.py:40 +#: axes/models.py:14 msgid "Path" msgstr "Путь" -#: axes/models.py:45 +#: axes/models.py:16 msgid "Attempt Time" -msgstr "Время входа" +msgstr "Время попытки входа" -#: axes/models.py:57 +#: axes/models.py:26 +msgid "Access lock out" +msgstr "Доступ запрещен" + +#: axes/models.py:34 +msgid "access failure" +msgstr "Ошибка доступа" + +#: axes/models.py:35 +msgid "access failures" +msgstr "Ошибки доступа" + +#: axes/models.py:39 msgid "GET Data" msgstr "Данные GET-запроса" -#: axes/models.py:61 +#: axes/models.py:41 msgid "POST Data" msgstr "Данные POST-запроса" -#: axes/models.py:65 +#: axes/models.py:43 msgid "Failed Logins" msgstr "Ошибочные попытки" -#: axes/models.py:76 +#: axes/models.py:49 msgid "access attempt" msgstr "Запись о попытке доступа" -#: axes/models.py:77 +#: axes/models.py:50 msgid "access attempts" msgstr "Попытки доступа" -#: axes/models.py:81 +#: axes/models.py:55 msgid "Logout Time" msgstr "Время выхода" -#: axes/models.py:90 +#: axes/models.py:61 msgid "access log" msgstr "Запись о доступе" -#: axes/models.py:91 +#: axes/models.py:62 msgid "access logs" msgstr "Логи доступа" From ffc161e81499012dacb9297a1badc3d1e40ebf19 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:07:07 +0500 Subject: [PATCH 222/372] feat!: add `AXES_LOCKOUT_PARAMETERS` flag BREAKING CHANGE: add `AXES_LOCKOUT_PARAMETERS` flag which accepts an iterable of keys or a callable that resolves an iterable. The key can be a string (represents a single parameter) or an Iterable of strings (represents a combined parameter). For example, using this parameters ```python AXES_LOCKOUT_PARAMETERS = [ "ip_address", ("username", "user_agent"), ] ``` axes will block users by IP and/or combination of username and user agent --- axes/conf.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/axes/conf.py b/axes/conf.py index 9dab628..0de99b3 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -10,18 +10,30 @@ settings.AXES_FAILURE_LIMIT = getattr(settings, "AXES_FAILURE_LIMIT", 3) # see if the user has set axes to lock out logins after failure limit settings.AXES_LOCK_OUT_AT_FAILURE = getattr(settings, "AXES_LOCK_OUT_AT_FAILURE", True) -# lock out with the combination of username and IP address -settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP = getattr( - settings, "AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP", False -) +# lockout parameters +# default value will be ["ip_address"] after removing AXES_LOCK_OUT params support +settings.AXES_LOCKOUT_PARAMETERS = getattr(settings, "AXES_LOCKOUT_PARAMETERS", None) -# lock out with the username or IP address -settings.AXES_LOCK_OUT_BY_USER_OR_IP = getattr( - settings, "AXES_LOCK_OUT_BY_USER_OR_IP", False -) +# TODO: remove it in future versions +if settings.AXES_LOCKOUT_PARAMETERS is None: + if getattr(settings, "AXES_ONLY_USER_FAILURES", False): + settings.AXES_LOCKOUT_PARAMETERS = ["username"] + else: + if getattr(settings, "AXES_LOCK_OUT_BY_USER_OR_IP", False): + settings.AXES_LOCKOUT_PARAMETERS = ["username", "ip_address"] + elif getattr(settings, "AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP", False): + settings.AXES_LOCKOUT_PARAMETERS = [("username", "ip_address")] + else: + settings.AXES_LOCKOUT_PARAMETERS = ["ip_address"] -# lock out with username and never the IP or user agent -settings.AXES_ONLY_USER_FAILURES = getattr(settings, "AXES_ONLY_USER_FAILURES", False) + if getattr(settings, "AXES_USE_USER_AGENT", False): + if isinstance(settings.AXES_LOCKOUT_PARAMETERS[0], str): + settings.AXES_LOCKOUT_PARAMETERS[0] = ( + settings.AXES_LOCKOUT_PARAMETERS[0], + "user_agent", + ) + else: + settings.AXES_LOCKOUT_PARAMETERS[0] += ("user_agent",) # lock out just for admin site settings.AXES_ONLY_ADMIN_SITE = getattr(settings, "AXES_ONLY_ADMIN_SITE", False) @@ -29,9 +41,6 @@ settings.AXES_ONLY_ADMIN_SITE = getattr(settings, "AXES_ONLY_ADMIN_SITE", False) # show Axes logs in admin settings.AXES_ENABLE_ADMIN = getattr(settings, "AXES_ENABLE_ADMIN", True) -# lock out with the user agent, has no effect when ONLY_USER_FAILURES is set -settings.AXES_USE_USER_AGENT = getattr(settings, "AXES_USE_USER_AGENT", False) - # use a specific username field to retrieve from login POST data settings.AXES_USERNAME_FORM_FIELD = getattr( settings, "AXES_USERNAME_FORM_FIELD", "username" From 97022a460d18d207f9a3fe5e669e4e39686404eb Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:12:35 +0500 Subject: [PATCH 223/372] chore!: deprecate params related to lockout parameters resolution --- axes/checks.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/axes/checks.py b/axes/checks.py index 8746ea6..65fb79f 100644 --- a/axes/checks.py +++ b/axes/checks.py @@ -128,6 +128,13 @@ def axes_deprecation_check(app_configs, **kwargs): # pylint: disable=unused-arg "AXES_PROXY_COUNT", "AXES_PROXY_TRUSTED_IPS", "AXES_META_PRECEDENCE_ORDER", + # AXES_ONLY_USER_FAILURES, AXES_USE_USER_AGENT and + # AXES_LOCK_OUT parameters were replaced with AXES_LOCKOUT_PARAMETERS + # in version 6.x + "AXES_ONLY_USER_FAILURES", + "AXES_LOCK_OUT_BY_USER_OR_IP", + "AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP", + "AXES_USE_USER_AGENT", ] for deprecated_setting in deprecated_settings: From 59a57386c1125f48a4d6ae28e958aa8aa399b32c Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:17:13 +0500 Subject: [PATCH 224/372] feat: add get_lockout_parameters Accepts request or AccessAttempt and optionally credentials. If AXES_LOCKOUT_PARAMETERS is callable, this function passes request or attempt and credentials to this callable and returns the result. If AXES_LOCKOUT_PARAMETERS is iterable, returns AXES_LOCKOUT_PARAMETERS. Otherwise raises TypeError. --- axes/helpers.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/axes/helpers.py b/axes/helpers.py index d218337..1d521c5 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -218,6 +218,26 @@ def get_client_http_accept(request: HttpRequest) -> str: def get_client_parameters(username: str, ip_address: str, user_agent: str) -> list: +def get_lockout_parameters( + request_or_attempt: Union[HttpRequest, AccessBase], + credentials: Optional[dict] = None, +) -> Iterable[Union[str, Iterable]]: + if callable(settings.AXES_LOCKOUT_PARAMETERS): + return settings.AXES_LOCKOUT_PARAMETERS( + request_or_attempt, credentials + ) # pylint: disable=not-callable + + elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): + return import_string(settings.AXES_LOCKOUT_PARAMETERS)( + request_or_attempt, credentials + ) + + elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, Iterable): + return settings.AXES_LOCKOUT_PARAMETERS + + raise TypeError( + "settings.AXES_LOCKOUT_PARAMETERS needs to be a callable or iterable" + ) """ Get query parameters for filtering AccessAttempt queryset. From 2df1c1948a7b91510c71bf4917640e0376f6e79c Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:23:29 +0500 Subject: [PATCH 225/372] refactor: refactor get_client_parameters * Now accepts request_or_attempt and credentials which are passed to get_lockout_parameters * Use lockout parameters that consumed from get_lockout_parameters --- axes/helpers.py | 60 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index 1d521c5..1e2267b 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -1,3 +1,4 @@ +from collections.abc import Iterable from datetime import timedelta from hashlib import sha256 from logging import getLogger @@ -217,7 +218,6 @@ def get_client_http_accept(request: HttpRequest) -> str: return request.META.get("HTTP_ACCEPT", "")[:1025] -def get_client_parameters(username: str, ip_address: str, user_agent: str) -> list: def get_lockout_parameters( request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None, @@ -238,6 +238,15 @@ def get_lockout_parameters( raise TypeError( "settings.AXES_LOCKOUT_PARAMETERS needs to be a callable or iterable" ) + + +def get_client_parameters( + username: str, + ip_address: str, + user_agent: str, + request_or_attempt: Union[HttpRequest, AccessBase], + credentials: Optional[dict] = None, +) -> List[dict]: """ Get query parameters for filtering AccessAttempt queryset. @@ -246,26 +255,35 @@ def get_lockout_parameters( Returns list of dict, every item of list are separate parameters """ + lockout_parameters = get_lockout_parameters(request_or_attempt, credentials) - if settings.AXES_ONLY_USER_FAILURES: - # 1. Only individual usernames can be tracked with parametrization - filter_query = [{"username": username}] - else: - if settings.AXES_LOCK_OUT_BY_USER_OR_IP: - # One of `username` or `IP address` is used - filter_query = [{"username": username}, {"ip_address": ip_address}] - elif settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP: - # 2. A combination of username and IP address can be used as well - filter_query = [{"username": username, "ip_address": ip_address}] - else: - # 3. Default case is to track the IP address only, which is the most secure option - filter_query = [{"ip_address": ip_address}] + parameters_dict = { + "username": username, + "ip_address": ip_address, + "user_agent": user_agent, + } - if settings.AXES_USE_USER_AGENT: - # 4. The HTTP User-Agent can be used to track e.g. one browser - filter_query[0]["user_agent"] = user_agent + filter_kwargs = [] - return filter_query + for parameter in lockout_parameters: + try: + if isinstance(parameter, str): + filter_kwarg = {parameter: parameters_dict[parameter]} + else: + filter_kwarg = { + combined_parameter: parameters_dict[combined_parameter] + for combined_parameter in parameter + } + filter_kwargs.append(filter_kwarg) + + except KeyError as e: + error_msg = ( + f"{e} lockout parameter is not allowed. " + f"Allowed parameters: {', '.join(parameters_dict.keys())}" + ) + raise ValueError(error_msg) from e + + return filter_kwargs def make_cache_key_list(filter_kwargs_list: List[dict]) -> List[str]: @@ -300,7 +318,9 @@ def get_client_cache_keys( ip_address = get_client_ip_address(request_or_attempt) user_agent = get_client_user_agent(request_or_attempt) - filter_kwargs_list = get_client_parameters(username, ip_address, user_agent) + filter_kwargs_list = get_client_parameters( + username, ip_address, user_agent, request_or_attempt, credentials + ) return make_cache_key_list(filter_kwargs_list) @@ -343,7 +363,7 @@ def get_client_str( client_dict["user_agent"] = user_agent else: # Other modes initialize the attributes that are used for the actual lockouts - client_list = get_client_parameters(username, ip_address, user_agent) + client_list = get_client_parameters(username, ip_address, user_agent, request) client_dict = {} for client in client_list: client_dict.update(client) From 07539ff3d7dcb75bba572592e271d343a1e9cce6 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:37:36 +0500 Subject: [PATCH 226/372] chore!: use AXES_LOCKOUT_PARAMETERS in app init log message --- axes/apps.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/axes/apps.py b/axes/apps.py index a2c0834..3383623 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -33,14 +33,22 @@ class AppConfig(apps.AppConfig): # Skip startup log messages if Axes is not set to verbose if settings.AXES_VERBOSE: - if settings.AXES_ONLY_USER_FAILURES: - mode = "blocking by username only" - elif settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP: - mode = "blocking by combination of username and IP" - elif settings.AXES_LOCK_OUT_BY_USER_OR_IP: - mode = "blocking by username or IP" + + if callable(settings.AXES_LOCKOUT_PARAMETERS) or isinstance( + settings.AXES_LOCKOUT_PARAMETERS, str + ): + mode = "blocking by parameters that are calculated in a custom callable" + else: - mode = "blocking by IP only" + mode = "blocking by " + " or ".join( + [ + param + if isinstance(param, str) + else "combination of " + " and ".join(param) + for param in settings.AXES_LOCKOUT_PARAMETERS + ] + ) + log.info( "AXES: BEGIN version %s, %s", __version__, From 476d3f52bc3482a3d781dcd42d5b27d674267606 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:42:42 +0500 Subject: [PATCH 227/372] chore: use get_lockout_parameters in handlers --- axes/handlers/cache.py | 6 ++++-- axes/handlers/database.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index f5de9b6..49574d2 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -11,6 +11,7 @@ from axes.helpers import ( get_client_username, get_credentials, get_failure_limit, + get_lockout_parameters, ) from axes.models import AccessAttempt from axes.signals import user_locked_out @@ -78,9 +79,10 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return username = get_client_username(request, credentials) - if settings.AXES_ONLY_USER_FAILURES and username is None: + lockout_parameters = list(get_lockout_parameters(request, credentials)) + if lockout_parameters == ["username"] and username is None: log.warning( - "AXES: Username is None and AXES_ONLY_USER_FAILURES is enabled, new record will NOT be created." + "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." ) return diff --git a/axes/handlers/database.py b/axes/handlers/database.py index 4e4dc67..b3553bc 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -18,6 +18,7 @@ from axes.helpers import ( get_client_username, get_credentials, get_failure_limit, + get_lockout_parameters, get_query_str, ) from axes.models import AccessLog, AccessAttempt, AccessFailureLog @@ -164,9 +165,10 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return # 2. database query: Get or create access record with the new failure data - if settings.AXES_ONLY_USER_FAILURES and username is None: + lockout_parameters = list(get_lockout_parameters(request, credentials)) + if lockout_parameters == ["username"] and username is None: log.warning( - "AXES: Username is None and AXES_ONLY_USER_FAILURES is enabled, new record will NOT be created." + "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." ) else: with transaction.atomic(): From 1e3d41228da0cded611e2794d386a5705463b2fa Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:49:25 +0500 Subject: [PATCH 228/372] feat!: implement reset_request using get_lockout_parameters --- axes/utils.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/axes/utils.py b/axes/utils.py index 55c82ef..a0951ed 100644 --- a/axes/utils.py +++ b/axes/utils.py @@ -12,7 +12,7 @@ from django.http import HttpRequest from axes.conf import settings from axes.handlers.proxy import AxesProxyHandler -from axes.helpers import get_client_ip_address +from axes.helpers import get_client_ip_address, get_lockout_parameters log = getLogger(__name__) @@ -37,23 +37,38 @@ def reset_request(request: HttpRequest) -> int: This utility method is meant to be used from the CLI or via Python API. """ + lockout_paramaters = get_lockout_parameters(request) ip: Optional[str] = get_client_ip_address(request) username = request.GET.get("username", None) - ip_or_username = settings.AXES_LOCK_OUT_BY_USER_OR_IP - if settings.AXES_ONLY_USER_FAILURES: + ip_required = False + username_required = False + ip_and_username = False + + for param in lockout_paramaters: + # hack: in works with all iterables, including strings + # so this checks works with separate parameters + # and with parameters combinations + if "username" in param and "ip_address" in param: + ip_and_username = True + ip_required = True + username_required = True + break + elif "username" in param: + username_required = True + elif "ip_address" in param: + ip_required = True + + ip_or_username = not ip_and_username and ip_required and username_required + if not ip_required: ip = None - elif not ( - settings.AXES_LOCK_OUT_BY_USER_OR_IP - or settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP - ): + if not username_required: username = None if not ip and not username: return 0 # We don't want to reset everything, if there is some wrong request parameter - # if settings.AXES_USE_USER_AGENT: # TODO: reset based on user_agent? return reset(ip, username, ip_or_username) From 8d4a0aa052916b8ece869612f39ce09887a764b5 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:49:50 +0500 Subject: [PATCH 229/372] chore!: pass request and credentials to get_client_parameters --- axes/attempts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axes/attempts.py b/axes/attempts.py index fd6d552..80055c1 100644 --- a/axes/attempts.py +++ b/axes/attempts.py @@ -38,7 +38,7 @@ def filter_user_attempts( username = get_client_username(request, credentials) filter_kwargs_list = get_client_parameters( - username, request.axes_ip_address, request.axes_user_agent + username, request.axes_ip_address, request.axes_user_agent, request, credentials ) attempts_list = [ AccessAttempt.objects.filter(**filter_kwargs) From c3586ac3dcd3f08353223adef5708df3fa20b5a3 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:50:35 +0500 Subject: [PATCH 230/372] test: use AXES_LOCKOUT_PARAMETERS in attempts test --- tests/test_attempts.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_attempts.py b/tests/test_attempts.py index f532ec2..accd9c9 100644 --- a/tests/test_attempts.py +++ b/tests/test_attempts.py @@ -82,74 +82,74 @@ class ResetResponseTestCase(AxesTestCase): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_reset_user_failures(self): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_reset_ip_user_failures(self): self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_reset_username_user_failures(self): self.request.GET["username"] = self.USERNAME_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_reset_ip_username_user_failures(self): self.request.GET["username"] = self.USERNAME_1 self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_reset_user_or_ip(self): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_reset_ip_user_or_ip(self): self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_reset_username_user_or_ip(self): self.request.GET["username"] = self.USERNAME_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_reset_ip_username_user_or_ip(self): self.request.GET["username"] = self.USERNAME_1 self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 2) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_reset_user_and_ip(self): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_reset_ip_user_and_ip(self): self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_reset_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_AND=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_reset_ip_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) - self.assertEqual(AccessAttempt.objects.count(), 3) + self.assertEqual(AccessAttempt.objects.count(), 4) From 7cb0144770da0610c625c6b908c1f4e7105be5fd Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:50:57 +0500 Subject: [PATCH 231/372] test: use AXES_LOCKOUT_PARAMETERS in handlers test --- tests/test_handlers.py | 44 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index d32af60..17f8be3 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -261,7 +261,10 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): _more = 10 for i in range(settings.AXES_ACCESS_FAILURE_LOG_PER_USER_LIMIT + _more): self.create_failure_log() - self.assertEqual(_more, AxesProxyHandler.remove_out_of_limit_failure_logs(username=self.username)) + self.assertEqual( + _more, + AxesProxyHandler.remove_out_of_limit_failure_logs(username=self.username), + ) @override_settings(AXES_RESET_ON_SUCCESS=True) def test_handler(self): @@ -296,7 +299,7 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): def test_whitelist(self, log): self.check_whitelist(log) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) @patch("axes.handlers.database.log") def test_user_login_failed_only_user_failures_with_none_username(self, log): credentials = {"username": None, "password": "test"} @@ -305,7 +308,7 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): attempt = AccessAttempt.objects.all() self.assertEqual(0, AccessAttempt.objects.count()) log.warning.assert_called_with( - "AXES: Username is None and AXES_ONLY_USER_FAILURES is enabled, new record will NOT be created." + "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." ) def test_user_login_failed_with_none_username(self): @@ -318,22 +321,37 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): def test_user_login_failed_multiple_username(self): configurations = ( (2, 1, {}, ["admin", "admin1"]), - (2, 1, {"AXES_USE_USER_AGENT": True}, ["admin", "admin1"]), - (2, 1, {"AXES_ONLY_USER_FAILURES": True}, ["admin", "admin1"]), ( 2, 1, - {"AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP": True}, + {"AXES_LOCKOUT_PARAMETERS": [("ip_address", "user_agent")]}, + ["admin", "admin1"], + ), + (2, 1, {"AXES_LOCKOUT_PARAMETERS": ["username"]}, ["admin", "admin1"]), + ( + 2, + 1, + {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, ["admin", "admin1"], ), ( 1, 2, - {"AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP": True}, + {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, ["admin", "admin"], ), - (1, 2, {"AXES_LOCK_OUT_BY_USER_OR_IP": True}, ["admin", "admin"]), - (2, 1, {"AXES_LOCK_OUT_BY_USER_OR_IP": True}, ["admin", "admin1"]), + ( + 1, + 2, + {"AXES_LOCKOUT_PARAMETERS": ["username", "ip_address"]}, + ["admin", "admin"], + ), + ( + 2, + 1, + {"AXES_LOCKOUT_PARAMETERS": ["username", "ip_address"]}, + ["admin", "admin1"], + ), ) for ( @@ -400,7 +418,7 @@ class ResetAttemptsCacheHandlerTestCase(AxesHandlerBaseTestCase): with self.assertRaises(NotImplementedError): AxesProxyHandler.reset_attempts() - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_handler_reset_attempts_username(self): self.set_up_login_attempts() self.assertEqual( @@ -436,7 +454,7 @@ class ResetAttemptsCacheHandlerTestCase(AxesHandlerBaseTestCase): self.check_failures(0, ip_address=self.IP_1) self.check_failures(2, ip_address=self.IP_2) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_handler_reset_attempts_ip_and_username(self): self.set_up_login_attempts() self.check_failures(1, username=self.USERNAME_1, ip_address=self.IP_1) @@ -482,7 +500,7 @@ class AxesCacheHandlerTestCase(AxesHandlerBaseTestCase): def test_whitelist(self, log): self.check_whitelist(log) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) @patch.object(cache, "set") @patch("axes.handlers.cache.log") def test_user_login_failed_only_user_failures_with_none_username( @@ -493,7 +511,7 @@ class AxesCacheHandlerTestCase(AxesHandlerBaseTestCase): AxesProxyHandler.user_login_failed(sender, credentials, self.request) self.assertFalse(cache_set.called) log.warning.assert_called_with( - "AXES: Username is None and AXES_ONLY_USER_FAILURES is enabled, new record will NOT be created." + "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." ) @patch.object(cache, "add") From 7ee8573939696103573c27330cb50a8a5ab5a2fe Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:51:03 +0500 Subject: [PATCH 232/372] test: use AXES_LOCKOUT_PARAMETERS in helpers test --- tests/test_helpers.py | 54 ++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index a0a5c64..c4f8c28 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -150,7 +150,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_only_client_details(self): username = "test@example.com" @@ -167,7 +167,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_only_client_details(self): username = "test@example.com" @@ -182,7 +182,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -199,7 +199,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -214,7 +214,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_USE_USER_AGENT=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_agent_client_details(self): username = "test@example.com" @@ -231,7 +231,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_USE_USER_AGENT=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_agent_client_details(self): username = "test@example.com" @@ -301,66 +301,46 @@ def get_dummy_client_str_using_request( class ClientParametersTestCase(AxesTestCase): - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_get_filter_kwargs_user(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [{"username": self.username}], ) - @override_settings( - AXES_ONLY_USER_FAILURES=False, - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=False, - AXES_USE_USER_AGENT=False, - ) def test_get_filter_kwargs_ip(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [{"ip_address": self.ip_address}], ) - @override_settings( - AXES_ONLY_USER_FAILURES=False, - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True, - AXES_USE_USER_AGENT=False, - ) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_get_filter_kwargs_user_and_ip(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [{"username": self.username, "ip_address": self.ip_address}], ) - @override_settings( - AXES_ONLY_USER_FAILURES=False, - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=False, - AXES_LOCK_OUT_BY_USER_OR_IP=True, - AXES_USE_USER_AGENT=False, - ) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_get_filter_kwargs_user_or_ip(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [{"username": self.username}, {"ip_address": self.ip_address}], ) - @override_settings( - AXES_ONLY_USER_FAILURES=False, - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=False, - AXES_USE_USER_AGENT=True, - ) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) def test_get_filter_kwargs_ip_and_agent(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [{"ip_address": self.ip_address, "user_agent": self.user_agent}], ) @override_settings( - AXES_ONLY_USER_FAILURES=False, - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True, - AXES_USE_USER_AGENT=True, + AXES_LOCKOUT_PARAMETERS=[("username", "ip_address", "user_agent")] ) def test_get_filter_kwargs_user_ip_agent(self): self.assertEqual( - get_client_parameters(self.username, self.ip_address, self.user_agent), + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), [ { "username": self.username, From a12c8bd6da6a9ca8241a13d4f9153e5550b09614 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:51:16 +0500 Subject: [PATCH 233/372] test: use AXES_LOCKOUT_PARAMETERS in logging test --- tests/test_logging.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_logging.py b/tests/test_logging.py index 68f2028..11f0684 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -34,25 +34,26 @@ class AppsTestCase(AxesTestCase): AppConfig.initialize() self.assertFalse(log.info.called) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_axes_config_log_user_only(self, log): AppConfig.initialize() - log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username only") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username") - @override_settings(AXES_ONLY_USER_FAILURES=False) def test_axes_config_log_ip_only(self, log): AppConfig.initialize() - log.info.assert_called_with(_BEGIN, _VERSION, "blocking by IP only") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by ip_address") - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_axes_config_log_user_ip(self, log): AppConfig.initialize() - log.info.assert_called_with(_BEGIN, _VERSION, "blocking by combination of username and IP") + log.info.assert_called_with( + _BEGIN, _VERSION, "blocking by combination of username and ip_address" + ) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_axes_config_log_user_or_ip(self, log): AppConfig.initialize() - log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username or IP") + log.info.assert_called_with(_BEGIN, _VERSION, "blocking by username or ip_address") class AccessLogTestCase(AxesTestCase): From d5c6073bd7cf2ad548287ee192147c5d49b8106f Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 4 May 2023 14:51:21 +0500 Subject: [PATCH 234/372] test: use AXES_LOCKOUT_PARAMETERS in login test --- tests/test_login.py | 71 ++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/tests/test_login.py b/tests/test_login.py index 94d870f..07d594f 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -182,10 +182,11 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertContains(response, self.LOCKED_MESSAGE, status_code=self.BLOCKED) self.assertTrue(self.attempt_count()) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_combination_user_and_ip(self): """ - Test login failure when AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP is True. + Test login failure when lockout parameters is combination + of username and ip_address. """ # test until one try before the limit @@ -199,10 +200,10 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.login(is_valid_username=True, is_valid_password=False) self.assertContains(response, self.LOCKED_MESSAGE, status_code=429) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_only_user_failures(self): """ - Test login failure when AXES_ONLY_USER_FAILURES is True. + Test login failure when lockout parameter is username. """ # test until one try before the limit @@ -274,7 +275,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user only. # Cache disabled. When AXES_ONLY_USER_FAILURES = True - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_blocks_when_same_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -283,7 +284,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_blocks_when_same_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -292,7 +293,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_allows_when_diff_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -301,7 +302,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_allows_when_diff_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -310,7 +311,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_with_empty_username_allows_other_users_without_cache(self): # User with empty username is locked out from IP 1. self._lockout_user_from_ip(username="", ip_addr=self.IP_1) @@ -321,7 +322,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # Cache disabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -330,7 +331,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -339,7 +340,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -348,7 +349,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -357,7 +358,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_without_cache( self, ): @@ -368,7 +369,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) - @override_settings(AXES_USE_USER_AGENT=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) def test_lockout_by_user_still_allows_login_with_differnet_user_agent(self): # User with empty username is locked out with "test-browser" user agent. self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") @@ -415,7 +416,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_with_empty_username_allows_other_users_using_cache(self): # User with empty username is locked out from IP 1. self._lockout_user_from_ip(username="", ip_addr=self.IP_1) @@ -426,7 +427,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user only. # With cache enabled. When AXES_ONLY_USER_FAILURES = True - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_blocks_when_same_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -435,7 +436,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_blocks_when_same_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -444,7 +445,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_allows_when_diff_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -453,7 +454,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_ONLY_USER_FAILURES=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_lockout_by_user_allows_when_diff_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -464,7 +465,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # With cache enabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -473,7 +474,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -482,7 +483,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -491,7 +492,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -501,7 +502,7 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertEqual(response.status_code, self.ALLOWED) @override_settings( - AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True, AXES_FAILURE_LIMIT=2 + AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")], AXES_FAILURE_LIMIT=2 ) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache_multiple_attempts( self, @@ -530,7 +531,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_using_cache( self, ): @@ -543,7 +544,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user or IP together. # With cache enabled. When AXES_LOCK_OUT_BY_USER_OR_IP = True - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_lockout_by_user_or_ip_blocks_when_same_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -552,7 +553,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_lockout_by_user_or_ip_allows_when_same_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -561,7 +562,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_lockout_by_user_or_ip_allows_when_diff_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -570,7 +571,9 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True, AXES_FAILURE_LIMIT=3) + @override_settings( + AXES_LOCKOUT_PARAMETERS=["username", "ip_address"], AXES_FAILURE_LIMIT=3 + ) def test_lockout_by_user_or_ip_allows_when_diff_user_same_ip_using_cache_multiple_attempts( self, ): @@ -600,7 +603,9 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_3, self.WRONG_PASSWORD, ip_addr=self.IP_1) self.assertContains(response, self.LOCKED_MESSAGE, status_code=self.BLOCKED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True, AXES_FAILURE_LIMIT=3) + @override_settings( + AXES_LOCKOUT_PARAMETERS=["username", "ip_address"], AXES_FAILURE_LIMIT=3 + ) def test_lockout_by_user_or_ip_allows_when_diff_user_same_ip_using_cache_multiple_failed_attempts( self, ): @@ -625,7 +630,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_lockout_by_user_or_ip_allows_when_diff_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -634,7 +639,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCK_OUT_BY_USER_OR_IP=True) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_lockout_by_user_or_ip_with_empty_username_allows_other_users_using_cache( self, ): From 73c4e4501ba024183a04f6b68939fb9d06559660 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 10 May 2023 22:06:55 +0500 Subject: [PATCH 235/372] chore: adjust types: use list instead of iterables and nested lists instead of tuples --- axes/conf.py | 8 ++++---- axes/handlers/cache.py | 2 +- axes/handlers/database.py | 2 +- axes/helpers.py | 10 +++++----- tests/test_attempts.py | 8 ++++---- tests/test_handlers.py | 8 ++++---- tests/test_helpers.py | 14 +++++++------- tests/test_logging.py | 2 +- tests/test_login.py | 26 +++++++++++++------------- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/axes/conf.py b/axes/conf.py index 0de99b3..daf1c70 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -22,18 +22,18 @@ if settings.AXES_LOCKOUT_PARAMETERS is None: if getattr(settings, "AXES_LOCK_OUT_BY_USER_OR_IP", False): settings.AXES_LOCKOUT_PARAMETERS = ["username", "ip_address"] elif getattr(settings, "AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP", False): - settings.AXES_LOCKOUT_PARAMETERS = [("username", "ip_address")] + settings.AXES_LOCKOUT_PARAMETERS = [["username", "ip_address"]] else: settings.AXES_LOCKOUT_PARAMETERS = ["ip_address"] if getattr(settings, "AXES_USE_USER_AGENT", False): if isinstance(settings.AXES_LOCKOUT_PARAMETERS[0], str): - settings.AXES_LOCKOUT_PARAMETERS[0] = ( + settings.AXES_LOCKOUT_PARAMETERS[0] = [ settings.AXES_LOCKOUT_PARAMETERS[0], "user_agent", - ) + ] else: - settings.AXES_LOCKOUT_PARAMETERS[0] += ("user_agent",) + settings.AXES_LOCKOUT_PARAMETERS[0].append("user_agent") # lock out just for admin site settings.AXES_ONLY_ADMIN_SITE = getattr(settings, "AXES_ONLY_ADMIN_SITE", False) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 49574d2..a797f39 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -79,7 +79,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return username = get_client_username(request, credentials) - lockout_parameters = list(get_lockout_parameters(request, credentials)) + lockout_parameters = get_lockout_parameters(request, credentials) if lockout_parameters == ["username"] and username is None: log.warning( "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." diff --git a/axes/handlers/database.py b/axes/handlers/database.py index b3553bc..035cc60 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -165,7 +165,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return # 2. database query: Get or create access record with the new failure data - lockout_parameters = list(get_lockout_parameters(request, credentials)) + lockout_parameters = get_lockout_parameters(request, credentials) if lockout_parameters == ["username"] and username is None: log.warning( "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." diff --git a/axes/helpers.py b/axes/helpers.py index 1e2267b..ac73aef 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import timedelta from hashlib import sha256 from logging import getLogger @@ -221,18 +220,18 @@ def get_client_http_accept(request: HttpRequest) -> str: def get_lockout_parameters( request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None, -) -> Iterable[Union[str, Iterable]]: +) -> List[Union[str, List[str]]]: if callable(settings.AXES_LOCKOUT_PARAMETERS): return settings.AXES_LOCKOUT_PARAMETERS( request_or_attempt, credentials - ) # pylint: disable=not-callable + ) - elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): + if isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): return import_string(settings.AXES_LOCKOUT_PARAMETERS)( request_or_attempt, credentials ) - elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, Iterable): + if isinstance(settings.AXES_LOCKOUT_PARAMETERS, list): return settings.AXES_LOCKOUT_PARAMETERS raise TypeError( @@ -281,6 +280,7 @@ def get_client_parameters( f"{e} lockout parameter is not allowed. " f"Allowed parameters: {', '.join(parameters_dict.keys())}" ) + log.exception(error_msg) raise ValueError(error_msg) from e return filter_kwargs diff --git a/tests/test_attempts.py b/tests/test_attempts.py index accd9c9..04af617 100644 --- a/tests/test_attempts.py +++ b/tests/test_attempts.py @@ -130,24 +130,24 @@ class ResetResponseTestCase(AxesTestCase): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 2) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_user_and_ip(self): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_ip_user_and_ip(self): self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_ip_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 self.request.META["REMOTE_ADDR"] = self.IP_1 diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 17f8be3..6d216e9 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -324,20 +324,20 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): ( 2, 1, - {"AXES_LOCKOUT_PARAMETERS": [("ip_address", "user_agent")]}, + {"AXES_LOCKOUT_PARAMETERS": [["ip_address", "user_agent"]]}, ["admin", "admin1"], ), (2, 1, {"AXES_LOCKOUT_PARAMETERS": ["username"]}, ["admin", "admin1"]), ( 2, 1, - {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, + {"AXES_LOCKOUT_PARAMETERS": [["username", "ip_address"]]}, ["admin", "admin1"], ), ( 1, 2, - {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, + {"AXES_LOCKOUT_PARAMETERS": [["username", "ip_address"]]}, ["admin", "admin"], ), ( @@ -454,7 +454,7 @@ class ResetAttemptsCacheHandlerTestCase(AxesHandlerBaseTestCase): self.check_failures(0, ip_address=self.IP_1) self.check_failures(2, ip_address=self.IP_2) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_handler_reset_attempts_ip_and_username(self): self.set_up_login_attempts() self.check_failures(1, username=self.USERNAME_1, ip_address=self.IP_1) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c4f8c28..2efb59c 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -182,7 +182,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -199,7 +199,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -214,7 +214,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_agent_client_details(self): username = "test@example.com" @@ -231,7 +231,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_agent_client_details(self): username = "test@example.com" @@ -314,7 +314,7 @@ class ClientParametersTestCase(AxesTestCase): [{"ip_address": self.ip_address}], ) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_get_filter_kwargs_user_and_ip(self): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), @@ -328,7 +328,7 @@ class ClientParametersTestCase(AxesTestCase): [{"username": self.username}, {"ip_address": self.ip_address}], ) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) def test_get_filter_kwargs_ip_and_agent(self): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), @@ -336,7 +336,7 @@ class ClientParametersTestCase(AxesTestCase): ) @override_settings( - AXES_LOCKOUT_PARAMETERS=[("username", "ip_address", "user_agent")] + AXES_LOCKOUT_PARAMETERS=[["username", "ip_address", "user_agent"]] ) def test_get_filter_kwargs_user_ip_agent(self): self.assertEqual( diff --git a/tests/test_logging.py b/tests/test_logging.py index 11f0684..bbb6b3e 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -43,7 +43,7 @@ class AppsTestCase(AxesTestCase): AppConfig.initialize() log.info.assert_called_with(_BEGIN, _VERSION, "blocking by ip_address") - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_axes_config_log_user_ip(self, log): AppConfig.initialize() log.info.assert_called_with( diff --git a/tests/test_login.py b/tests/test_login.py index 07d594f..89aa105 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -182,7 +182,7 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertContains(response, self.LOCKED_MESSAGE, status_code=self.BLOCKED) self.assertTrue(self.attempt_count()) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_combination_user_and_ip(self): """ Test login failure when lockout parameters is combination @@ -322,7 +322,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # Cache disabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -331,7 +331,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -340,7 +340,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -349,7 +349,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -358,7 +358,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_without_cache( self, ): @@ -369,7 +369,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) def test_lockout_by_user_still_allows_login_with_differnet_user_agent(self): # User with empty username is locked out with "test-browser" user agent. self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") @@ -465,7 +465,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # With cache enabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -474,7 +474,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -483,7 +483,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -492,7 +492,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -502,7 +502,7 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertEqual(response.status_code, self.ALLOWED) @override_settings( - AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")], AXES_FAILURE_LIMIT=2 + AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]], AXES_FAILURE_LIMIT=2 ) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache_multiple_attempts( self, @@ -531,7 +531,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_using_cache( self, ): From 0823584b02dba6e936829422f25d924a2334c97e Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Wed, 10 May 2023 22:07:11 +0500 Subject: [PATCH 236/372] refactor: fix prospector errors --- axes/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/axes/utils.py b/axes/utils.py index a0951ed..c9983d5 100644 --- a/axes/utils.py +++ b/axes/utils.py @@ -10,7 +10,6 @@ from typing import Optional from django.http import HttpRequest -from axes.conf import settings from axes.handlers.proxy import AxesProxyHandler from axes.helpers import get_client_ip_address, get_lockout_parameters @@ -55,7 +54,7 @@ def reset_request(request: HttpRequest) -> int: ip_required = True username_required = True break - elif "username" in param: + if "username" in param: username_required = True elif "ip_address" in param: ip_required = True From 43cc1ef39e7e7996540e0c8c385d4e217b1459fe Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 11 May 2023 17:47:16 +0500 Subject: [PATCH 237/372] test: add new login tests --- tests/test_login.py | 133 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/test_login.py b/tests/test_login.py index 89aa105..1caf0ad 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -239,6 +239,139 @@ class DatabaseLoginTestCase(AxesTestCase): response, self.LOGIN_FORM_KEY, status_code=self.ALLOWED, html=True ) + @override_settings(AXES_LOCKOUT_PARAMETERS=["user_agent"]) + def test_lockout_by_user_agent_only(self): + """ + Test login failure when lockout parameter is only user_agent + """ + # User is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked with another username: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked with another ip: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test with another user agent: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser-2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + @override_settings(AXES_LOCKOUT_PARAMETERS=["ip_address", "username", "user_agent"]) + def test_lockout_by_all_parameters(self): + # User is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by username: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by ip: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser2") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by user_agent: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is allowed to login with different username, ip and user_agent + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "username", "user_agent"]]) + def test_lockout_by_combination_of_all_parameters(self): + # User is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is allowed to login with different username: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different IP: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different user_agent: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different username, ip and user_agent + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + @override_settings(AXES_LOCKOUT_PARAMETERS=["ip_address", ["username", "user_agent"]]) + def test_lockout_by_ip_or_username_and_user_agent(self): + # User is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by ip: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser2") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by username and user_agent: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is allowed to login with different username and ip + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different user_agent and ip + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different username, ip and user_agent + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"], ["username", "user_agent"]]) + def test_lockout_by_ip_and_user_agent_or_username_and_user_agent(self): + # User is locked out with "test-browser" user agent. + self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") + + # Test he is locked: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by ip and user_agent: + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is locked by username and user_agent: + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.BLOCKED) + + # Test he is allowed to login with different username and ip + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different user_agent + response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test he is allowed to login with different username, ip and user_agent + response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") + self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) + + # Test for true and false positives when blocking by IP *OR* user (default) # Cache disabled. Default settings. def test_lockout_by_ip_blocks_when_same_user_same_ip_without_cache(self): From 166c431b25dee5a1834c3dff1f5615960e02209a Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 11 May 2023 17:52:38 +0500 Subject: [PATCH 238/372] test: add callable AXES_LOCKOUT_PARAMETERS test cases and tests with wrong lockout parameters --- tests/test_helpers.py | 178 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 2efb59c..dacc2d2 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -300,6 +300,10 @@ def get_dummy_client_str_using_request( return f"{request.user.email}" +def get_dummy_lockout_parameters(request, credentials=None): + return ["ip_address", ["username", "user_agent"]] + + class ClientParametersTestCase(AxesTestCase): @override_settings(AXES_LOCKOUT_PARAMETERS=["username"]) def test_get_filter_kwargs_user(self): @@ -350,6 +354,180 @@ class ClientParametersTestCase(AxesTestCase): ], ) + @override_settings(AXES_LOCKOUT_PARAMETERS=["wrong_param"]) + @patch("axes.helpers.log") + def test_get_filter_kwargs_invalid_parameter(self, log): + with self.assertRaises(ValueError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + log.exception.assert_called_with( + ( + "wrong_param lockout parameter is not allowed. " + "Allowed lockout parameters: username, ip_address, user_agent" + ) + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "wrong_param"]]) + @patch("axes.helpers.log") + def test_get_filter_kwargs_invalid_combined_parameter(self, log): + with self.assertRaises(ValueError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + log.exception.assert_called_with( + ( + "wrong_param lockout parameter is not allowed. " + "Allowed lockout parameters: username, ip_address, user_agent" + ) + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=get_dummy_lockout_parameters) + def test_get_filter_kwargs_callable_lockout_parameters(self): + self.assertEqual( + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ), + [ + { + "ip_address": self.ip_address, + }, + { + "username": self.username, + "user_agent": self.user_agent, + }, + ], + ) + + @override_settings( + AXES_LOCKOUT_PARAMETERS="tests.test_helpers.get_dummy_lockout_parameters" + ) + def test_get_filter_kwargs_callable_str_lockout_parameters(self): + self.assertEqual( + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ), + [ + { + "ip_address": self.ip_address, + }, + { + "username": self.username, + "user_agent": self.user_agent, + }, + ], + ) + + @override_settings( + AXES_LOCKOUT_PARAMETERS=lambda request, credentials: ["username"] + ) + def test_get_filter_kwargs_callable_lambda_lockout_parameters(self): + self.assertEqual( + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ), + [ + { + "username": self.username, + }, + ], + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=True) + def test_get_filter_kwargs_not_list_or_callable(self): + with self.assertRaises(TypeError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=lambda: None) + def test_get_filter_kwargs_invalid_callable_too_few_arguments(self): + with self.assertRaises(TypeError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=lambda request, credentials, extra: None) + def test_get_filter_kwargs_invalid_callable_too_many_arguments(self): + with self.assertRaises(TypeError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + + @override_settings( + AXES_LOCKOUT_PARAMETERS=lambda request, credentials: ["wrong_param"] + ) + @patch("axes.helpers.log") + def test_get_filter_kwargs_callable_invalid_lockout_param(self, log): + with self.assertRaises(ValueError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + log.exception.assert_called_with( + ( + "wrong_param lockout parameter is not allowed. " + "Allowed lockout parameters: username, ip_address, user_agent" + ) + ) + + @override_settings( + AXES_LOCKOUT_PARAMETERS=lambda request, credentials: [ + ["ip_address", "wrong_param"] + ] + ) + @patch("axes.helpers.log") + def test_get_filter_kwargs_callable_invalid_combined_lockout_param(self, log): + with self.assertRaises(ValueError): + get_client_parameters( + self.username, + self.ip_address, + self.user_agent, + self.request, + self.credentials, + ) + log.exception.assert_called_with( + ( + "wrong_param lockout parameter is not allowed. " + "Allowed lockout parameters: username, ip_address, user_agent" + ) + ) + class ClientCacheKeyTestCase(AxesTestCase): def test_get_cache_keys(self): From c66bb1fb2b90a53f2de35ac45d79f83cc7fdb19f Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 11 May 2023 18:32:41 +0500 Subject: [PATCH 239/372] test: add new client parameters tests --- tests/test_helpers.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index dacc2d2..6562568 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -325,6 +325,27 @@ class ClientParametersTestCase(AxesTestCase): [{"username": self.username, "ip_address": self.ip_address}], ) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "user_agent"]]) + def test_get_filter_kwargs_user_and_user_agent(self): + self.assertEqual( + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), + [{"username": self.username, "user_agent": self.user_agent}], + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=["ip_address", ["username", "user_agent"]]) + def test_get_filter_kwargs_ip_or_user_and_user_agent(self): + self.assertEqual( + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), + [{"ip_address": self.ip_address}, {"username": self.username, "user_agent": self.user_agent}], + ) + + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"], ["username", "user_agent"]]) + def test_get_filter_kwargs_ip_and_user_agent_or_user_and_user_agent(self): + self.assertEqual( + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), + [{"ip_address": self.ip_address, "user_agent": self.user_agent}, {"username": self.username, "user_agent": self.user_agent}], + ) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address"]) def test_get_filter_kwargs_user_or_ip(self): self.assertEqual( @@ -332,6 +353,13 @@ class ClientParametersTestCase(AxesTestCase): [{"username": self.username}, {"ip_address": self.ip_address}], ) + @override_settings(AXES_LOCKOUT_PARAMETERS=["username", "ip_address", "user_agent"]) + def test_get_filter_kwargs_user_or_ip_or_user_agent(self): + self.assertEqual( + get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), + [{"username": self.username}, {"ip_address": self.ip_address}, {"user_agent": self.user_agent}], + ) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) def test_get_filter_kwargs_ip_and_agent(self): self.assertEqual( From 3cba78a5991e879d553143fca070e28165a1529a Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 11 May 2023 18:36:21 +0500 Subject: [PATCH 240/372] refactor: fix qa errors --- axes/apps.py | 1 - axes/helpers.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/axes/apps.py b/axes/apps.py index 3383623..9be1bf2 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -33,7 +33,6 @@ class AppConfig(apps.AppConfig): # Skip startup log messages if Axes is not set to verbose if settings.AXES_VERBOSE: - if callable(settings.AXES_LOCKOUT_PARAMETERS) or isinstance( settings.AXES_LOCKOUT_PARAMETERS, str ): diff --git a/axes/helpers.py b/axes/helpers.py index ac73aef..c951281 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -222,9 +222,7 @@ def get_lockout_parameters( credentials: Optional[dict] = None, ) -> List[Union[str, List[str]]]: if callable(settings.AXES_LOCKOUT_PARAMETERS): - return settings.AXES_LOCKOUT_PARAMETERS( - request_or_attempt, credentials - ) + return settings.AXES_LOCKOUT_PARAMETERS(request_or_attempt, credentials) if isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): return import_string(settings.AXES_LOCKOUT_PARAMETERS)( From f6e272cb61085a07dacfdc12b28cfe784f068711 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Fri, 12 May 2023 16:51:12 +0500 Subject: [PATCH 241/372] docs: add `AXES_LOCKOUT_PARAMETERS`` entires --- docs/4_configuration.rst | 10 +++++---- docs/5_customization.rst | 44 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index 13091a4..b0626cc 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -27,15 +27,15 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_ONLY_ADMIN_SITE | False | If ``True``, lock is only enabled for admin site. Admin site is determined by checking request path against the path of ``"admin:index"`` view. If admin urls are not registered in current urlconf, all requests will not be locked. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_ONLY_USER_FAILURES | False | If ``True``, only lock based on username, and never lock based on IP if attempts exceed the limit. Otherwise utilize the existing IP and user locking logic. | +| AXES_ONLY_USER_FAILURES | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS ISNTEAD``. If ``True``, only lock based on username, and never lock based on IP if attempts exceed the limit. Otherwise utilize the existing IP and user locking logic. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_ENABLE_ADMIN | True | If ``True``, admin views for access attempts and logins are shown in Django admin interface. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP | False | If ``True``, prevent login from IP under a particular username if the attempt limit has been exceeded, otherwise lock out based on IP. | +| AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS`` INSTEAD. If ``True``, prevent login from IP under a particular username if the attempt limit has been exceeded, otherwise lock out based on IP. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_LOCK_OUT_BY_USER_OR_IP | False | If ``True``, prevent login from if the attempt limit has been exceeded for IP or username. | +| AXES_LOCK_OUT_BY_USER_OR_IP | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS`` INSTEAD. If ``True``, prevent login from if the attempt limit has been exceeded for IP or username. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_USE_USER_AGENT | False | If ``True``, lock out and log based on the IP address and the user agent. This means requests from different user agents but from the same IP are treated differently. This settings has no effect if the ``AXES_ONLY_USER_FAILURES`` setting is active. | +| AXES_USE_USER_AGENT | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS`` INSTEAD. If ``True``, lock out and log based on the IP address and the user agent. This means requests from different user agents but from the same IP are treated differently. This settings has no effect if the ``AXES_ONLY_USER_FAILURES`` setting is active. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_HANDLER | 'axes.handlers.database.AxesDatabaseHandler' | The path to the handler class to use. If set, overrides the default signal handler backend. Default: ``'axes.handlers.database.AxesDatabaseHandler'`` | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -83,6 +83,8 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT | True | If ``True``, a failed login attempt during lockout will reset the cool off period. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| AXES_LOCKOUT_PARAMETERS | ["ip_address"] | A list of parameters that Axes uses to lock out users. It can also be callable, which takes an http request or AccesAttempt object and credentials and returns a list of parameters. Each parameter can be a string (a single parameter) or a list of strings (a combined parameter). For example, if you configure ``AXES_LOCKOUT_PARAMETERS = ["ip_address", ["username", "user_agent"]]``, axes will block clients by ip and/or username and user agent combination. See :ref:`customizing-lockout-parameters` for more details. | ++------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ The configuration option precedences for the access attempt monitoring are: diff --git a/docs/5_customization.rst b/docs/5_customization.rst index 47f2fd5..f21bd61 100644 --- a/docs/5_customization.rst +++ b/docs/5_customization.rst @@ -155,7 +155,6 @@ into ``my_namespace-username``: fine, but Axes does not inject these changes into the authentication flow for you. - Customizing lockout responses ----------------------------- @@ -174,9 +173,50 @@ An example of usage could be e.g. a custom view for processing lockouts. AXES_LOCKOUT_CALLABLE = "example.views.lockout" +.. _customizing-lockout-parameters: + +Customizing lockout parameters +------------------------------ + +Axes can be configured with ``AXES_LOCKOUT_PARAMETERS`` to lock out users not only by IP address. + +``AXES_LOCKOUT_PARAMETERS`` can be a list of strings (which represents a separate lockout parameter) or nested lists of strings (which represents lockout parameters used in combination) or a callable which accepts HttpRequest or AccessAttempt and credentials and returns a list of the same form as described earlier. + +Example ``AXES_LOCKOUT_PARAMETERS`` configuration: + +``settings.py``:: + + AXES_LOCKOUT_PARAMETERS = ["ip_address", ["username", "user_agent"]] + +This way, axes will lock out users using ip_address and/or combination of username and user agent + +Example of callable ``AXES_LOCKOUT_PARAMETERS``: + +``example/utils.py``:: + + from django.http import HttpRequest + + def get_lockout_parameters(request_or_attempt, credentials): + + if isinstance(request_or_attempt, HttpRequest): + is_localhost = request.META.get("REMOTE_ADDR") == "127.0.0.1" + + else: + is_localhost = request_or_attempt.ip_address == "127.0.0.1" + + if is_localhost: + return ["username"] + + return ["ip_address", "username"] + +``settings.py``:: + + AXES_LOCKOUT_CALLABLE = "example.utils.get_lockout_parameters" + +This way, if client ip_address is localhost, axes will lockout client only by username. In other case, axes will lockout client by username and/or ip_address. Customizing client ip address lookups ------------------------------ +------------------------------------- Axes can be configured with ``AXES_CLIENT_IP_CALLABLE`` to use custom client ip address lookup logic. From 3e13c8f85e4680fa5cd53a7b001e4704be963f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 13 May 2023 14:09:12 +0300 Subject: [PATCH 242/372] Version 6.0.0b4 --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2e4966d..93b0413 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,15 @@ Changes ======= +6.0.0b4 (2023-05-13) +-------------------- + +- Add ``AXES_LOCKOUT_PARAMETERS`` configuration flag that will supersede ``AXES_ONLY_USER_FAILURES``, ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``, ``AXES_LOCK_OUT_BY_USER_OR_IP``, and ``AXES_USE_USER_AGENT`` configurations. Add deprecation warnings for old flags. See project documentation on RTD for update instructions. + [hirotasoshu] +- Improve translations. + [hirotasoshu] + + 6.0.0b3 (2023-05-01) -------------------- From fe130af344156400badb1c62bd4dec489f1017d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 12:57:07 +0000 Subject: [PATCH 243/372] Bump mypy from 1.2.0 to 1.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mypy](https://github.com/python/mypy) from 1.2.0 to 1.3.0. - [Commits](https://github.com/python/mypy/compare/v1.2.0...v1.3.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Aleksi Häkli --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 0fba15f..6db02a9 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.2.0 +mypy==1.3.0 prospector==1.10.0 types-pkg_resources # Type stub From 1e7e85e1016a1b6437aa524dd8cabb2c881cd598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 13 May 2023 14:43:20 +0300 Subject: [PATCH 244/372] Improve documentation --- docs/5_customization.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/5_customization.rst b/docs/5_customization.rst index f21bd61..a1fbcfd 100644 --- a/docs/5_customization.rst +++ b/docs/5_customization.rst @@ -203,15 +203,15 @@ Example of callable ``AXES_LOCKOUT_PARAMETERS``: else: is_localhost = request_or_attempt.ip_address == "127.0.0.1" - + if is_localhost: - return ["username"] - + return ["username"] + return ["ip_address", "username"] ``settings.py``:: - AXES_LOCKOUT_CALLABLE = "example.utils.get_lockout_parameters" + AXES_LOCKOUT_PARAMETERS = "example.utils.get_lockout_parameters" This way, if client ip_address is localhost, axes will lockout client only by username. In other case, axes will lockout client by username and/or ip_address. @@ -227,4 +227,4 @@ Axes can be configured with ``AXES_CLIENT_IP_CALLABLE`` to use custom client ip ``settings.py``:: - AXES_LOCKOUT_CALLABLE = "example.utils.get_client_ip" + AXES_CLIENT_IP_CALLABLE = "example.utils.get_client_ip" From 102cdc3ecf0332e1333295e09de65412e2f39c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 13 May 2023 14:52:37 +0300 Subject: [PATCH 245/372] Fix typo --- docs/4_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index b0626cc..a2f3abe 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -27,7 +27,7 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_ONLY_ADMIN_SITE | False | If ``True``, lock is only enabled for admin site. Admin site is determined by checking request path against the path of ``"admin:index"`` view. If admin urls are not registered in current urlconf, all requests will not be locked. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_ONLY_USER_FAILURES | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS ISNTEAD``. If ``True``, only lock based on username, and never lock based on IP if attempts exceed the limit. Otherwise utilize the existing IP and user locking logic. | +| AXES_ONLY_USER_FAILURES | False | DEPRECATED: USE ``AXES_LOCKOUT_PARAMETERS`` INSTEAD. If ``True``, only lock based on username, and never lock based on IP if attempts exceed the limit. Otherwise utilize the existing IP and user locking logic. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_ENABLE_ADMIN | True | If ``True``, admin views for access attempts and logins are shown in Django admin interface. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ From 117db49091ccb79e232b9bfd9c9614aa90065be0 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sun, 14 May 2023 18:07:56 +0500 Subject: [PATCH 246/372] feat: add is_admin_request, deprecate is_admin_site --- axes/handlers/base.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/axes/handlers/base.py b/axes/handlers/base.py index 8c192ed..62d70f3 100644 --- a/axes/handlers/base.py +++ b/axes/handlers/base.py @@ -1,6 +1,7 @@ import re from abc import ABC, abstractmethod from typing import Optional +from warnings import warn from django.urls import reverse from django.urls.exceptions import NoReverseMatch @@ -81,7 +82,7 @@ class AxesBaseHandler: # pylint: disable=unused-argument and inspiration on some common checks and access restrictions before writing your own implementation. """ - if self.is_admin_site(request): + if settings.AXES_ONLY_ADMIN_SITE and not self.is_admin_request(request): return True if self.is_blacklisted(request, credentials): @@ -134,10 +135,41 @@ class AxesBaseHandler: # pylint: disable=unused-argument return False + def get_admin_url(self) -> Optional[str]: + """ + Returns admin url if exists, otherwise returns None + """ + try: + return reverse("admin:index") + except NoReverseMatch: + return None + + def is_admin_request(self, request) -> bool: + """ + Checks that request located under admin site + """ + if hasattr(request, "path"): + admin_url = self.get_admin_url() + return ( + admin_url is not None + and re.match(f"^{admin_url}", request.path) is not None + ) + + return False + def is_admin_site(self, request) -> bool: """ - Checks if the request is for admin site. + Checks if the request is NOT for admin site + if `settings.AXES_ONLY_ADMIN_SITE` is True. """ + warn( + ( + "This method is deprecated and will be removed in future versions. " + "If you looking for method that checks if `request.path` located under " + "admin site, use `is_admin_request` instead." + ), + DeprecationWarning, + ) if settings.AXES_ONLY_ADMIN_SITE and hasattr(request, "path"): try: admin_url = reverse("admin:index") From 58d5b491d8fa75c3a0a8987a97f0b34af03274fa Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sun, 14 May 2023 18:08:30 +0500 Subject: [PATCH 247/372] test: add deprecation check for is_admin_site, add is_admin_request tests --- tests/test_handlers.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 6d216e9..0c40573 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -55,14 +55,36 @@ class AxesHandlerTestCase(AxesTestCase): for setting_value, url, expected in tests: with override_settings(AXES_ONLY_ADMIN_SITE=setting_value): request.path = url - self.assertEqual(AxesProxyHandler().is_admin_site(request), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(AxesProxyHandler().is_admin_site(request), expected) + + def test_is_admin_request(self): + request = MagicMock() + tests = ( # (URL, Expected) + ("/test/", False), + (reverse("admin:index"), True), + ) + + for url, expected in tests: + request.path = url + self.assertEqual(AxesProxyHandler().is_admin_request(request), expected) @override_settings(ROOT_URLCONF="tests.urls_empty") @override_settings(AXES_ONLY_ADMIN_SITE=True) def test_is_admin_site_no_admin_site(self): request = MagicMock() request.path = "/admin/" - self.assertTrue(AxesProxyHandler().is_admin_site(self.request)) + with self.assertWarns(DeprecationWarning): + self.assertTrue(AxesProxyHandler().is_admin_site(self.request)) + + @override_settings(ROOT_URLCONF="tests.urls_empty") + def test_is_admin_request_no_admin_site(self): + request = MagicMock() + request.path = "/admin/" + self.assertFalse(AxesProxyHandler().is_admin_request(self.request)) + + def test_is_admin_request_no_path(self): + self.assertFalse(AxesProxyHandler().is_admin_request(self.request)) class AxesProxyHandlerTestCase(AxesTestCase): From 1965d2f2ba38f5158df6833cc9d2eb02ba03a114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 15 May 2023 00:42:13 +0300 Subject: [PATCH 248/372] Update CHANGES.rst Version 6.0.0b5 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 93b0413..5b68ff6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.0b5 (2023-05-15) +-------------------- + +- Deprecate ``is_admin_site`` API call with misleading naming. + [hirotasoshu] + + 6.0.0b4 (2023-05-13) -------------------- From d463787e343f80234e392435920a51a47bfe19a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 12:57:35 +0000 Subject: [PATCH 249/372] chore(deps): bump pytest-subtests from 0.10.0 to 0.11.0 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.10.0 to 0.11.0. - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f6277ab..fc04b11 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,4 +4,4 @@ coverage==7.2.5 pytest==7.3.1 pytest-cov==4.0.0 pytest-django==4.5.2 -pytest-subtests==0.10.0 +pytest-subtests==0.11.0 From 33cfcb9656005909c87d25c820ec6981b8c18ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 17 May 2023 16:30:09 +0300 Subject: [PATCH 250/372] Drop Python 3.7 support --- .github/workflows/test.yml | 9 +-------- docs/1_requirements.rst | 2 +- mypy.ini | 2 +- pyproject.toml | 3 +-- setup.py | 3 +-- 5 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ad1ea9..e687b0a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11'] django-version: ['3.2', '4.1', '4.2'] include: # Tox configuration for QA environment @@ -34,13 +34,6 @@ jobs: django-version: '4.2' experimental: true exclude: - # Exclude Python 3.7 for Django 4.x and Django main - - python-version: '3.7' - django-version: '4.1' - - python-version: '3.7' - django-version: '4.2' - - python-version: '3.7' - django-version: 'main' # Exclude Python 3.11 for Django 3.2 and Django 4.0 - python-version: '3.11' django-version: '3.2' diff --git a/docs/1_requirements.rst b/docs/1_requirements.rst index 75513f0..d690cf4 100644 --- a/docs/1_requirements.rst +++ b/docs/1_requirements.rst @@ -3,7 +3,7 @@ Requirements ============ -Axes requires a supported Django version and runs on Python versions 3.7 and above. +Axes requires a supported Django version and runs on Python versions 3.8 and above. Refer to the project source code repository in `GitHub `_ and see the diff --git a/mypy.ini b/mypy.ini index dd2b3f3..7fa5400 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,5 @@ [mypy] -python_version = 3.7 +python_version = 3.8 ignore_missing_imports = True [mypy-axes.migrations.*] diff --git a/pyproject.toml b/pyproject.toml index 070e2c5..a22f66d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{37,38,39,310,py38}-dj32 + py{38,39,310,py38}-dj32 py{38,39,310,311,py38}-dj41 py{38,39,310,311,py38}-dj42 py311-djmain @@ -18,7 +18,6 @@ envlist = [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 diff --git a/setup.py b/setup.py index 0b02ab9..4dbc3cd 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup( package_dir={"axes": "axes"}, use_scm_version=True, setup_requires=["setuptools_scm"], - python_requires=">=3.7", + python_requires=">=3.8", install_requires=["django>=3.2", "setuptools"], extras_require={ "ipware": "django-ipware>=3", @@ -56,7 +56,6 @@ setup( "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", From 84e5e357e63935d0e26c6e662a6463c9cab37bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 16 May 2023 23:14:04 +0300 Subject: [PATCH 251/372] Version 6 --- CHANGES.rst | 28 ++++----------- docs/2_installation.rst | 76 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5b68ff6..9639105 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,43 +3,27 @@ Changes ======= -6.0.0b5 (2023-05-15) --------------------- +6.0.0 (2023-05-17) +------------------ +Version 6 is a breaking release. Please see the documentation for upgrade instructions. + +- Deprecate Python 3.7 support. + [aleksihakli] - Deprecate ``is_admin_site`` API call with misleading naming. [hirotasoshu] - - -6.0.0b4 (2023-05-13) --------------------- - - Add ``AXES_LOCKOUT_PARAMETERS`` configuration flag that will supersede ``AXES_ONLY_USER_FAILURES``, ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``, ``AXES_LOCK_OUT_BY_USER_OR_IP``, and ``AXES_USE_USER_AGENT`` configurations. Add deprecation warnings for old flags. See project documentation on RTD for update instructions. [hirotasoshu] - Improve translations. [hirotasoshu] - - -6.0.0b3 (2023-05-01) --------------------- - - Use Django ``cache.incr`` API for atomic cached failure counting [hirotasoshu, aleksihakli] - - -6.0.0b2 (2023-04-28) --------------------- - - Make ``django-ipware`` an optional dependency. Install it with e.g. ``pip install django-axes[ipware]`` package and extras specifier. [aleksihakli] - Deprecate and rename old configuration flags. Old flags will be removed in or after version ``6.1``. [aleksihakli] * ``AXES_PROXY_ORDER`` is now ``AXES_IPWARE_PROXY_ORDER``, * ``AXES_PROXY_COUNT`` is now ``AXES_IPWARE_PROXY_COUNT``, * ``AXES_PROXY_TRUSTED_IPS`` is now ``AXES_IPWARE_PROXY_TRUSTED_IPS``, and * ``AXES_META_PRECEDENCE_ORDER`` is now ``AXES_IPWARE_META_PRECEDENCE_ORDER``. - - -6.0.0b1 (2023-04-25) --------------------- - - Set 429 as the default lockout response code. [hirotasoshu] diff --git a/docs/2_installation.rst b/docs/2_installation.rst index 27d1f45..21dc0dc 100644 --- a/docs/2_installation.rst +++ b/docs/2_installation.rst @@ -34,10 +34,10 @@ After installing the package, the project settings need to be configured. 'django.contrib.auth.backends.ModelBackend', ] - For backwards compatibility, ``AxesBackend`` can be used in place of ``AxesStandaloneBackend``. + For backwards compatibility, ``AxesBackend`` can be used in place of ``AxesStandaloneBackend``. The only difference is that ``AxesBackend`` also provides the permissions-checking functionality of Django's ``ModelBackend`` behind the scenes. We recommend using ``AxesStandaloneBackend`` - if you have any custom logic to override Django's standard permissions checks. + if you have any custom logic to override Django's standard permissions checks. **3.** Add ``axes.middleware.AxesMiddleware`` to your list of ``MIDDLEWARE``:: @@ -75,6 +75,76 @@ Many people have different configurations for their development and production e and running the application with misconfigured settings can prevent security features from working. +Version 6 breaking changes and upgrading from django-axes version 5 +------------------------------------------------------------------- + +If you have not specialized ``django-axes`` configuration in any way +you do not have to update any of the configuration. + +The instructions apply to users who have configured ``django-axes`` in their projects +and have used flags that are deprecated. The deprecated flags will be removed in the future +but are compatible for at least version 6.0 of ``django-axes``. + +The following flags and configuration have changed: + +``django-ipware`` has become an optional dependency. +To keep old behaviour, use ``pip install django-axes[ipware]`` +in your install script or use ``django-axes[ipware]`` +in your requirements file(s) instead of plain ``django-axes``. +The new ``django-axes`` package does not include ``django-ipware`` by default +but does use ``django-ipware`` if it is installed +and no callables for IP address resolution are configured +with the ``settings.AXES_CLIENT_IP_CALLABLE`` configuration flag. + +``django-ipware`` related flags have changed names. +The old flags have been deprecated and will be removed in the future. +To keep old behaviour, rename them in your settings file: + +- ``settings.AXES_PROXY_ORDER`` is now ``settings.AXES_IPWARE_PROXY_ORDER``, +- ``settings.AXES_PROXY_COUNT`` is now ``settings.AXES_IPWARE_PROXY_COUNT``, +- ``settings.AXES_PROXY_TRUSTED_IPS`` is now ``settings.AXES_IPWARE_PROXY_TRUSTED_IPS``, and +- ``settings.AXES_META_PRECEDENCE_ORDER`` is now ``settings.AXES_IPWARE_META_PRECEDENCE_ORDER``. + +``settings.AXES_LOCKOUT_PARAMETERS`` configuration flag has been added which supersedes the following configuration keys: + +#. No configuration for failure tracking in the following items (default behaviour). +#. ``settings.AXES_ONLY_USER_FAILURES``, +#. ``settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``, +#. ``settings.AXES_LOCK_OUT_BY_USER_OR_IP``, and +#. ``settings.AXES_USE_USER_AGENT``. + +To keep old behaviour with the new flag, configure the following: + +#. If you did not use any flags, use ``settings.AXES_LOCKOUT_PARAMETERS = ["ip_address"]``, +#. If you used ``settings.AXES_ONLY_USER_FAILURES``, use ``settings.AXES_LOCKOUT_PARAMETERS = ["username"]``, +#. If you used ``settings.AXES_LOCK_OUT_BY_USER_OR_IP``, use ``settings.AXES_LOCKOUT_PARAMETERS = ["username", "ip_address"]``, and +#. If you used ``settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``, use ``settings.AXES_LOCKOUT_PARAMETERS = [["username", "ip_address"]]``, +#. If you used ``settings.AXES_USE_USER_AGENT``, add ``"user_agent"`` to your list(s) of lockout parameters. + #. ``settings.AXES_USE_USER_AGENT`` would become ``settings.AXES_LOCKOUT_PARAMETERS = ["ip_address", "user_agent"]`` + #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_ONLY_USER_FAILURES`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["username", "user_agent"]]`` + #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_LOCK_OUT_BY_USER_OR_IP`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent"], "username"]`` + #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent", "username"]]`` + #. Other combinations of flags were previously not considered; the flags had precedence over each other as described in the documentation but were less-than-trivial to understand in their previous form. The new form is more explicit and flexible, although it requires more in-depth configuration. + +The new lockout parameters define a combined list of attributes to consider when tracking failed authentication attempts. +They can be any combination of ``username``, ``ip_address`` or ``user_agent`` in a list of strings or list of lists of strings. +The attributes defined in the lists are combined and saved into the database, cache, or other backend for failed logins. +The semantics of the evaluation are available in the documentation and ``axes.helpers.get_client_parameters`` callable. + +``settings.AXES_HTTP_RESPONSE_CODE`` default has been changed from ``403`` (Forbidden) to ``429`` (Too Many Requests). +To keep the old behavior, set ``settings.AXES_HTTP_RESPONSE_CODE = 403`` in your settings. + +``axes.handlers.base.AxesBaseHandler.is_admin_site`` has been deprecated due to misleading naming +in favour of better-named ``axes.handlers.base.AxesBaseHandler.is_admin_request``. +The old implementation has been kept for backwards compatibility, but will be removed in the future. +The old implementation checked if a request is NOT made for an admin site if ``settings.AXES_ONLY_ADMIN_SITE`` was set. +The new implementation correctly checks if a request is made for an admin site. + +``axes.handlers.cache.AxesCacheHandler`` has been updated to use atomic ``cache.incr`` calls +instead of old ``cache.set`` calls in authentication failure tracking +to enable better parallel backend support for atomic cache backends like Redis and Memcached. + + Disabling Axes system checks ---------------------------- @@ -128,7 +198,7 @@ other code, preventing the login mechanisms from working due to e.g. exception being thrown in some part of the code, preventing access attempts being logged to database with Axes or causing similar problems. -If new attempts or log objects are not being correctly written to the Axes tables, +If new attempts or log objects are not being correctly written to the Axes tables, it is possible to configure Django ``ATOMIC_REQUESTS`` setting to to ``False``:: ATOMIC_REQUESTS = False From 10d1c600278a85df12db8d2362b43695a26ffd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 17 May 2023 20:39:56 +0300 Subject: [PATCH 252/372] Allow Python >= 3.7 for RTD compatibility Documentation autobuilder fails if Python 3.7 is not permitted for package installations in RTD --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4dbc3cd..99ac508 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup( package_dir={"axes": "axes"}, use_scm_version=True, setup_requires=["setuptools_scm"], - python_requires=">=3.8", + python_requires=">=3.7", install_requires=["django>=3.2", "setuptools"], extras_require={ "ipware": "django-ipware>=3", From 1be420b95cee248d423e94729e4f943738858171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 17 May 2023 20:45:50 +0300 Subject: [PATCH 253/372] Update release.yml Use stable v1 PyPI release task --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff22dfc..11f835a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: - name: Upload packages to Jazzband if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: jazzband password: ${{ secrets.JAZZBAND_RELEASE_KEY }} From d4cbdf7010a38f71f28ade64f4beed3f8402dbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 17 May 2023 20:46:44 +0300 Subject: [PATCH 254/372] Version 6.0.1 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9639105..382e681 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.1 (2023-05-17) +------------------ + +- Fine-tune CI pipelines and RTD build requirements. + [aleksihakli] + + 6.0.0 (2023-05-17) ------------------ From fcdeb4ae3f11724b99da54dfa61df90abcf54f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 17 May 2023 20:51:51 +0300 Subject: [PATCH 255/372] Update release.yml Use repository-url instead of repository_url as parameter name for the PyPI release task --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11f835a..eace86b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,4 +40,4 @@ jobs: with: user: jazzband password: ${{ secrets.JAZZBAND_RELEASE_KEY }} - repository_url: https://jazzband.co/projects/django-axes/upload + repository-url: https://jazzband.co/projects/django-axes/upload From c228ae18919e38c7ac0ec19864314659902aab05 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 27 May 2023 20:35:46 +0500 Subject: [PATCH 256/372] chore: fix typo --- docs/2_installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/2_installation.rst b/docs/2_installation.rst index 21dc0dc..e1d6a15 100644 --- a/docs/2_installation.rst +++ b/docs/2_installation.rst @@ -120,7 +120,7 @@ To keep old behaviour with the new flag, configure the following: #. If you used ``settings.AXES_LOCK_OUT_BY_USER_OR_IP``, use ``settings.AXES_LOCKOUT_PARAMETERS = ["username", "ip_address"]``, and #. If you used ``settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``, use ``settings.AXES_LOCKOUT_PARAMETERS = [["username", "ip_address"]]``, #. If you used ``settings.AXES_USE_USER_AGENT``, add ``"user_agent"`` to your list(s) of lockout parameters. - #. ``settings.AXES_USE_USER_AGENT`` would become ``settings.AXES_LOCKOUT_PARAMETERS = ["ip_address", "user_agent"]`` + #. ``settings.AXES_USE_USER_AGENT`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent"]]`` #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_ONLY_USER_FAILURES`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["username", "user_agent"]]`` #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_LOCK_OUT_BY_USER_OR_IP`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent"], "username"]`` #. ``settings.AXES_USE_USER_AGENT`` with ``settings.AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP`` would become ``settings.AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent", "username"]]`` From ba96c12507d5191bb6dc87bd51c0e45f55be337b Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 27 May 2023 22:05:50 +0500 Subject: [PATCH 257/372] chore: add bug report template --- .github/ISSUE_TEMPLATES/bug_report.md | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/ISSUE_TEMPLATES/bug_report.md diff --git a/.github/ISSUE_TEMPLATES/bug_report.md b/.github/ISSUE_TEMPLATES/bug_report.md new file mode 100644 index 0000000..261a642 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve django-axes +title: 'BUG: Short description of the problem' +labels: 'bug' +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. +2. +3. +4. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Your environment** +python version: +django version: +django-axes version: +Operating system: + +**Additional context** +Add any other context about the problem here. + +**Possible implementation** +Not obligatory, but suggest an idea for implementing addition or change From b8afdf13cfe8c1d1f3fe187f49c2a11cd236fa69 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 27 May 2023 22:05:57 +0500 Subject: [PATCH 258/372] chore: add feature request template --- .github/ISSUE_TEMPLATES/feature_request.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/ISSUE_TEMPLATES/feature_request.md diff --git a/.github/ISSUE_TEMPLATES/feature_request.md b/.github/ISSUE_TEMPLATES/feature_request.md new file mode 100644 index 0000000..77f1005 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for django-axes +title: 'FEATURE REQUEST: Short description of requested feature' +labels: 'feature request' +assignees: '' +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 498a691e5d2582971e65dd0a12c4f40f41363352 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 27 May 2023 22:06:07 +0500 Subject: [PATCH 259/372] chore: add pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..94a1759 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +# What does this PR do? + + + + + +Fixes # (issue) + + +## 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? From 5b235b50ed377f0562082d5cb718265a22996daf Mon Sep 17 00:00:00 2001 From: Ian Fisher Date: Mon, 29 May 2023 20:45:30 -0400 Subject: [PATCH 260/372] Add check for callable settings --- axes/checks.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_checks.py | 19 +++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/axes/checks.py b/axes/checks.py index 65fb79f..8b6b0f4 100644 --- a/axes/checks.py +++ b/axes/checks.py @@ -21,6 +21,7 @@ class Messages: ) BACKEND_INVALID = "You do not have 'axes.backends.AxesStandaloneBackend' or a subclass in your settings.AUTHENTICATION_BACKENDS." SETTING_DEPRECATED = "You have a deprecated setting {deprecated_setting} configured in your project settings" + CALLABLE_INVALID = "{callable_setting} is not a valid callable." class Hints: @@ -28,6 +29,7 @@ class Hints: MIDDLEWARE_INVALID = None BACKEND_INVALID = "AxesModelBackend was renamed to AxesStandaloneBackend in django-axes version 5.0." SETTING_DEPRECATED = None + CALLABLE_INVALID = None class Codes: @@ -35,6 +37,7 @@ class Codes: MIDDLEWARE_INVALID = "axes.W002" BACKEND_INVALID = "axes.W003" SETTING_DEPRECATED = "axes.W004" + CALLABLE_INVALID = "axes.W005" @register(Tags.security, Tags.caches, Tags.compatibility) @@ -153,3 +156,49 @@ def axes_deprecation_check(app_configs, **kwargs): # pylint: disable=unused-arg pass return warnings + + +@register +def axes_conf_check(app_configs, **kwargs): # pylint: disable=unused-argument + warnings = [] + + callable_settings = [ + "AXES_CLIENT_IP_CALLABLE", + "AXES_CLIENT_STR_CALLABLE", + "AXES_LOCKOUT_CALLABLE", + "AXES_USERNAME_CALLABLE", + "AXES_WHITELIST_CALLABLE", + "AXES_COOLOFF_TIME", + "AXES_LOCKOUT_PARAMETERS", + ] + + for callable_setting in callable_settings: + value = getattr(settings, callable_setting) + if not is_valid_callable(value): + warnings.append( + Warning( + msg=Messages.CALLABLE_INVALID.format( + callable_setting=callable_setting + ), + hint=Hints.CALLABLE_INVALID, + id=Codes.CALLABLE_INVALID, + ) + ) + + return warnings + + +def is_valid_callable(value) -> bool: + if value is None: + return True + + if callable(value): + return True + + if isinstance(value, str): + try: + import_string(value) + except ImportError: + return False + + return True diff --git a/tests/test_checks.py b/tests/test_checks.py index 1ccad7c..b9ed5f6 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -110,3 +110,22 @@ class DeprecatedSettingsTestCase(AxesTestCase): def test_deprecated_success_access_log_flag(self): warnings = run_checks() self.assertEqual(warnings, [self.disable_success_access_log_warning]) + + +class ConfCheckTestCase(AxesTestCase): + @override_settings(AXES_USERNAME_CALLABLE="module.not_defined") + def test_invalid_import_path(self): + warnings = run_checks() + warning = Warning( + msg=Messages.CALLABLE_INVALID.format( + callable_setting="AXES_USERNAME_CALLABLE" + ), + hint=Hints.CALLABLE_INVALID, + id=Codes.CALLABLE_INVALID, + ) + self.assertEqual(warnings, [warning]) + + @override_settings(AXES_COOLOFF_TIME=lambda: 1) + def test_valid_callable(self): + warnings = run_checks() + self.assertEqual(warnings, []) From 75a0709c6fdedee6b227e2e56e2a06a6e776d548 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 12:58:05 +0000 Subject: [PATCH 261/372] chore(deps): bump pytest from 7.3.1 to 7.3.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.1 to 7.3.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.3.1...7.3.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index fc04b11..085cc91 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.2.5 -pytest==7.3.1 +pytest==7.3.2 pytest-cov==4.0.0 pytest-django==4.5.2 pytest-subtests==0.11.0 From 8178a63bf7dd40a1994877cb4295cb16a1032a65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:57:22 +0000 Subject: [PATCH 262/372] chore(deps): bump sphinx-rtd-theme from 1.2.0 to 1.2.2 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.2.0 to 1.2.2. - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.2.0...1.2.2) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7c4c941..7e59721 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.2.0 +sphinx_rtd_theme==1.2.2 tox==4.5.1 From 90ede53472671d1d3f648a12f778801aefeed49c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 12:57:15 +0000 Subject: [PATCH 263/372] chore(deps): bump prospector from 1.10.0 to 1.10.2 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.10.0 to 1.10.2. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/PyCQA/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.10.0...1.10.2) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 6db02a9..d4b73dc 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 mypy==1.3.0 -prospector==1.10.0 +prospector==1.10.2 types-pkg_resources # Type stub From 36b6c2c36ea33271d22077a1111ab07d14024204 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:17:00 +0000 Subject: [PATCH 264/372] chore(deps): bump coverage from 7.2.5 to 7.2.7 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.5 to 7.2.7. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.5...7.2.7) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 085cc91..9949036 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.2.5 +coverage==7.2.7 pytest==7.3.2 pytest-cov==4.0.0 pytest-django==4.5.2 From f339b4cc1a29dcb2e781e888701c60c33bcf6ef6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:16:45 +0000 Subject: [PATCH 265/372] chore(deps): bump tox from 4.5.1 to 4.6.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.5.1 to 4.6.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.5.1...4.6.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7e59721..d167931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.5.1 +tox==4.6.0 From 1e6ca7af862fe030b3d57b01eda82c7b2ece95c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:17:15 +0000 Subject: [PATCH 266/372] chore(deps): bump pytest-cov from 4.0.0 to 4.1.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 4.0.0 to 4.1.0. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 9949036..d1364f4 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,6 +2,6 @@ django-ipware>=3 coverage==7.2.7 pytest==7.3.2 -pytest-cov==4.0.0 +pytest-cov==4.1.0 pytest-django==4.5.2 pytest-subtests==0.11.0 From 81360d017d306132a8c73ee6ea306103504cc0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Tue, 13 Jun 2023 19:22:47 +0300 Subject: [PATCH 267/372] Version 6.0.2 --- CHANGES.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 382e681..eea304e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changes ======= +6.0.2 (2023-06-13) +------------------ + +- Add Django system checks for validating callable import path settings. + [iafisher] +- Improve documentation. + [hirotasoshu] +- Improve repository issue and PR templates. + [hirotasoshu] + + 6.0.1 (2023-05-17) ------------------ From 091d666677bd5fc7c5cce5fe36f9109d17dd3d02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:03:28 +0000 Subject: [PATCH 268/372] chore(deps): bump tox from 4.6.0 to 4.6.1 Bumps [tox](https://github.com/tox-dev/tox) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.6.0...4.6.1) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d167931..ab92c58 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.6.0 +tox==4.6.1 From deb0e330ad73b48850645a7f2fb05d61cdc445a6 Mon Sep 17 00:00:00 2001 From: Davide Date: Thu, 15 Jun 2023 14:14:59 +0200 Subject: [PATCH 269/372] Add username to admin fieldsets #1073 --- axes/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/axes/admin.py b/axes/admin.py index 5f8a9c4..48aa95b 100644 --- a/axes/admin.py +++ b/axes/admin.py @@ -23,7 +23,7 @@ class AccessAttemptAdmin(admin.ModelAdmin): date_hierarchy = "attempt_time" fieldsets = ( - (None, {"fields": ("path_info", "failures_since_start")}), + (None, {"fields": ("username", "path_info", "failures_since_start")}), (_("Form Data"), {"fields": ("get_data", "post_data")}), (_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}), ) @@ -61,7 +61,7 @@ class AccessLogAdmin(admin.ModelAdmin): date_hierarchy = "attempt_time" fieldsets = ( - (None, {"fields": ("path_info",)}), + (None, {"fields": ("username", "path_info")}), (_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}), ) @@ -96,7 +96,7 @@ class AccessFailureLogAdmin(admin.ModelAdmin): date_hierarchy = "attempt_time" fieldsets = ( - (None, {"fields": ("path_info",)}), + (None, {"fields": ("username", "path_info")}), (_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}), ) From 1548a589dc160a2622b6ff6d8b5761233fe12a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 18 Jun 2023 14:16:54 +0300 Subject: [PATCH 270/372] Version 6.0.3 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eea304e..25f9319 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.3 (2023-06-18) +------------------ + +- Add username to admin fieldsets. + [sevdog] + + 6.0.2 (2023-06-13) ------------------ From d871675b8ea9364682ea0ade3f9fbc2ee26aa841 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:57:31 +0000 Subject: [PATCH 271/372] chore(deps): bump tox from 4.6.1 to 4.6.2 Bumps [tox](https://github.com/tox-dev/tox) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.6.1...4.6.2) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ab92c58..814edbf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.6.1 +tox==4.6.2 From a2558c9f84506bda18f804f769f1b811dd7db76a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:57:05 +0000 Subject: [PATCH 272/372] chore(deps): bump tox from 4.6.2 to 4.6.3 Bumps [tox](https://github.com/tox-dev/tox) from 4.6.2 to 4.6.3. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.6.2...4.6.3) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 814edbf..b5fae12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.6.2 +tox==4.6.3 From 74f7670b634dd635f130f9b3d39319c2a3f8568c Mon Sep 17 00:00:00 2001 From: Pieter Goetschalckx Date: Wed, 21 Jun 2023 00:06:06 +0200 Subject: [PATCH 273/372] Remove unused methods from AxesStandaloneBackend --- axes/backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axes/backends.py b/axes/backends.py index 6cec9b5..2cec578 100644 --- a/axes/backends.py +++ b/axes/backends.py @@ -1,6 +1,6 @@ from typing import Optional from django.conf import settings -from django.contrib.auth.backends import BaseBackend, ModelBackend +from django.contrib.auth.backends import ModelBackend from django.http import HttpRequest from axes.exceptions import ( @@ -11,7 +11,7 @@ from axes.handlers.proxy import AxesProxyHandler from axes.helpers import get_credentials, get_lockout_message, toggleable -class AxesStandaloneBackend(BaseBackend): +class AxesStandaloneBackend: """ Authentication backend class that forbids login attempts for locked out users. From 21cc0234de029eeb5af47d6d6841f86080a6a590 Mon Sep 17 00:00:00 2001 From: Pieter Goetschalckx Date: Wed, 21 Jun 2023 00:06:46 +0200 Subject: [PATCH 274/372] Test request after force_login --- tests/test_login.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_login.py b/tests/test_login.py index 1caf0ad..80b602f 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -28,7 +28,7 @@ class DjangoLoginTestCase(TestCase): self.username = "john.doe" self.password = "hunter2" - self.user = get_user_model().objects.create(username=self.username) + self.user = get_user_model().objects.create(username=self.username, is_staff=True) self.user.set_password(self.password) self.user.save() self.user.backend = "django.contrib.auth.backends.ModelBackend" @@ -47,13 +47,19 @@ class DjangoContribAuthLoginTestCase(DjangoLoginTestCase): class DjangoTestClientLoginTestCase(DjangoLoginTestCase): def test_client_login(self): self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse("admin:index")) + self.assertEqual(response.status_code, 200) def test_client_logout(self): self.client.login(username=self.username, password=self.password) self.client.logout() + response = self.client.get(reverse("admin:index")) + self.assertEqual(response.status_code, 302) def test_client_force_login(self): self.client.force_login(self.user) + response = self.client.get(reverse("admin:index")) + self.assertEqual(response.status_code, 200) class DatabaseLoginTestCase(AxesTestCase): @@ -283,7 +289,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test he is locked by user_agent: response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser") self.assertEqual(response.status_code, self.BLOCKED) - + # Test he is allowed to login with different username, ip and user_agent response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) @@ -308,7 +314,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test he is allowed to login with different user_agent: response = self._login("username", self.VALID_PASSWORD, ip_addr=self.IP_1, user_agent="test-browser2") self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) - + # Test he is allowed to login with different username, ip and user_agent response = self._login("username2", self.VALID_PASSWORD, ip_addr=self.IP_2, user_agent="test-browser2") self.assertEqual(response.status_code, self.ATTEMPT_NOT_BLOCKED) From 6dee6352e2a79117c4d525333c1ccdc769b08bcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 12:57:05 +0000 Subject: [PATCH 275/372] chore(deps): bump mypy from 1.3.0 to 1.4.0 Bumps [mypy](https://github.com/python/mypy) from 1.3.0 to 1.4.0. - [Commits](https://github.com/python/mypy/compare/v1.3.0...v1.4.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index d4b73dc..2976384 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.3.0 +mypy==1.4.0 prospector==1.10.2 types-pkg_resources # Type stub From 51971ba4239c3769c259a1d342ec53f9ca401ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 22 Jun 2023 11:46:36 +0300 Subject: [PATCH 276/372] Version 6.0.4 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 25f9319..72030b1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.4 (2023-06-22) +------------------ + +- Remove unused methods from AxesStandaloneBackend. + [314eter] + + 6.0.3 (2023-06-18) ------------------ From f40d477ee7a969443fb29bc83aae2545b65a430a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:57:23 +0000 Subject: [PATCH 277/372] chore(deps): bump pytest from 7.3.2 to 7.4.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.2 to 7.4.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.3.2...7.4.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index d1364f4..3631d07 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.2.7 -pytest==7.3.2 +pytest==7.4.0 pytest-cov==4.1.0 pytest-django==4.5.2 pytest-subtests==0.11.0 From ab79e05692ce025dc38086eb6060650468cfa320 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 12:57:11 +0000 Subject: [PATCH 278/372] chore(deps): bump mypy from 1.4.0 to 1.4.1 Bumps [mypy](https://github.com/python/mypy) from 1.4.0 to 1.4.1. - [Commits](https://github.com/python/mypy/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 2976384..5d23aba 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.4.0 +mypy==1.4.1 prospector==1.10.2 types-pkg_resources # Type stub From 370fadad3631c695118a5020a630d41d7dc91345 Mon Sep 17 00:00:00 2001 From: Kira Date: Fri, 30 Jun 2023 09:37:54 +0800 Subject: [PATCH 279/372] add indonesian translation --- axes/locale/id/LC_MESSAGES/django.mo | Bin 0 -> 1618 bytes axes/locale/id/LC_MESSAGES/django.po | 105 +++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 axes/locale/id/LC_MESSAGES/django.mo create mode 100644 axes/locale/id/LC_MESSAGES/django.po diff --git a/axes/locale/id/LC_MESSAGES/django.mo b/axes/locale/id/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..b81be81e219a0635a5df5725489107d10ffc0c97 GIT binary patch literal 1618 zcmb7@zmMER6vrJ1fh@m|9~GcS9eLxspwr>YMYkk-dn zA^rp^Xz1wZ5G4f-4GlsS(Z?{84{UwqJUo&eWCiT`qEuXpw?DE>oPb1ts5KK#Bh=C^~zFm9-VSthH@ksMsf4mQ=7_;zg2bVrx~guvMR~m)deqci;)`qwh>i>VmAXi)|vSvw~FcfpP%0QrvNnu(jXOGid#Way@Drc`W1>IoF z&S)66i~TSRNUB;`XN*-G8x-hZ1IlYPrpj{b9L$coZ2{X8=eSaa28q(PS!zZ=)FB7d z6@JF7PVHMrt2Xz%JM-RW9NwuXmX#Bj^0tbsJC5Bqm8sHF`xM`1os=%$Q59|Wz5Xk` zL$xT4b*EL5#_nr%Qn_L2Q!ki>?scQsVw;QNl}{Vq#ueAwbgy{iz3OkQU-Gs*5037k zdN&pM&hO@MaOh5F?u`NM&;055(%Z9xVQ<2fbwk6t!iks0r`J=%bTz~CJvM4LN$p8J z??+{E4Zb?5IZt=e*OSh{5xe1X zfz++KV=pgq{O@gwPS{k+?i`1E?$*jrGP|XWJJeAbr#kUzYo6L(fD`B3l^&>k(u>Bd zcGkD4UG8|>J!gOSq)^I9iq z*WtJqbw-6M=5@x@2oeMLt9q2?oFRzBLX$&Zv$kp9d5be!Q*fGvG%c>d2)7}3q{~Ta zt@G5h7JA, 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-axes 6.0.4\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-06-30 09:21+0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Kira \n" +"Language-Team: LANGUAGE \n" +"Language: id\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +#: .\axes\admin.py:27 +msgid "Form Data" +msgstr "Data Formulir" + +#: .\axes\admin.py:28 .\axes\admin.py:65 .\axes\admin.py:100 +msgid "Meta Data" +msgstr "Meta Data" + +#: .\axes\conf.py:108 +msgid "Account locked: too many login attempts. Please try again later." +msgstr "Akun terkunci: terlalu banyak percobaan login. Silakan coba lagi nanti." + +#: .\axes\conf.py:116 +msgid "" +"Account locked: too many login attempts. Contact an admin to unlock your " +"account." +msgstr "Akun terkunci: terlalu banyak percobaan login. Hubungi admin untuk" +" membuka kunci akun" + +#: .\axes\models.py:6 +msgid "User Agent" +msgstr "User Agent" + +#: .\axes\models.py:8 +msgid "IP Address" +msgstr "Alamat IP" + +#: .\axes\models.py:10 +msgid "Username" +msgstr "Nama Pengguna" + +#: .\axes\models.py:12 +msgid "HTTP Accept" +msgstr "HTTP Accept" + +#: .\axes\models.py:14 +msgid "Path" +msgstr "Path" + +#: .\axes\models.py:16 +msgid "Attempt Time" +msgstr "Waktu Percobaan" + +#: .\axes\models.py:26 +msgid "Access lock out" +msgstr "Akses terkunci" + +#: .\axes\models.py:34 +msgid "access failure" +msgstr "kegagalan akses" + +#: .\axes\models.py:35 +msgid "access failures" +msgstr "kegagalan akses" + +#: .\axes\models.py:39 +msgid "GET Data" +msgstr "Data GET" + +#: .\axes\models.py:41 +msgid "POST Data" +msgstr "Data POST" + +#: .\axes\models.py:43 +msgid "Failed Logins" +msgstr "Login Gagal" + +#: .\axes\models.py:49 +msgid "access attempt" +msgstr "upaya akses" + +#: .\axes\models.py:50 +msgid "access attempts" +msgstr "upaya akses" + +#: .\axes\models.py:55 +msgid "Logout Time" +msgstr "Waktu Logout" + +#: .\axes\models.py:61 +msgid "access log" +msgstr "log akses" + +#: .\axes\models.py:62 +msgid "access logs" +msgstr "log akses" From 1c2a998a787a51906d21d03a670e1276809e8e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 1 Jul 2023 00:06:22 +0300 Subject: [PATCH 280/372] Version 6.0.5 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 72030b1..342717f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.0.5 (2023-07-01) +------------------ + +- Add Indonesion translation. + [kiraware] + + 6.0.4 (2023-06-22) ------------------ From 1d4e1e32520cc8656d532baf40d25797306d244c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:11:57 +0000 Subject: [PATCH 281/372] chore(deps): bump tox from 4.6.3 to 4.6.4 Bumps [tox](https://github.com/tox-dev/tox) from 4.6.3 to 4.6.4. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.6.3...4.6.4) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b5fae12..1685b4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.6.3 +tox==4.6.4 From cf8ffdda6028533956ea63374398fb527f71ba8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:06:41 +0000 Subject: [PATCH 282/372] chore(deps): bump black from 23.3.0 to 23.7.0 Bumps [black](https://github.com/psf/black) from 23.3.0 to 23.7.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.3.0...23.7.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5d23aba..a5af4a8 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.3.0 +black==23.7.0 mypy==1.4.1 prospector==1.10.2 types-pkg_resources # Type stub From b5a3e7ddf55129235477ee9de572544ae2d55a41 Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Fri, 21 Jul 2023 22:20:04 +0500 Subject: [PATCH 283/372] fix: rename ISSUE_TEMPLATES to ISSUE_TEMPLATE (typo) --- .github/{ISSUE_TEMPLATES => ISSUE_TEMPLATE}/bug_report.md | 1 + .github/{ISSUE_TEMPLATES => ISSUE_TEMPLATE}/feature_request.md | 1 + 2 files changed, 2 insertions(+) rename .github/{ISSUE_TEMPLATES => ISSUE_TEMPLATE}/bug_report.md (99%) rename .github/{ISSUE_TEMPLATES => ISSUE_TEMPLATE}/feature_request.md (99%) diff --git a/.github/ISSUE_TEMPLATES/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md similarity index 99% rename from .github/ISSUE_TEMPLATES/bug_report.md rename to .github/ISSUE_TEMPLATE/bug_report.md index 261a642..37747fb 100644 --- a/.github/ISSUE_TEMPLATES/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,6 +4,7 @@ about: Create a report to help us improve django-axes title: 'BUG: Short description of the problem' labels: 'bug' assignees: '' + --- **Describe the bug** diff --git a/.github/ISSUE_TEMPLATES/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md similarity index 99% rename from .github/ISSUE_TEMPLATES/feature_request.md rename to .github/ISSUE_TEMPLATE/feature_request.md index 77f1005..23c5a49 100644 --- a/.github/ISSUE_TEMPLATES/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,6 +4,7 @@ about: Suggest an idea for django-axes title: 'FEATURE REQUEST: Short description of requested feature' labels: 'feature request' assignees: '' + --- **Is your feature request related to a problem? Please describe.** From ebe1b74925fe76f538fbad1db63b6583f374622c Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Tue, 25 Jul 2023 00:12:04 +0500 Subject: [PATCH 284/372] test: fix duplicate test names in AxesCoolOffTestCase --- tests/test_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 6562568..60f9128 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -928,15 +928,15 @@ class AxesCoolOffTestCase(AxesTestCase): self.assertEqual(get_cool_off(), timedelta(hours=2)) @override_settings(AXES_COOLOFF_TIME=2.0) - def test_get_cool_off_int(self): + def test_get_cool_off_float(self): self.assertEqual(get_cool_off(), timedelta(minutes=120)) @override_settings(AXES_COOLOFF_TIME=0.25) - def test_get_cool_off_int(self): + def test_get_cool_off_float_lt_0(self): self.assertEqual(get_cool_off(), timedelta(minutes=15)) @override_settings(AXES_COOLOFF_TIME=1.7) - def test_get_cool_off_int(self): + def test_get_cool_off_float_gt_0(self): self.assertEqual(get_cool_off(), timedelta(seconds=6120)) @override_settings(AXES_COOLOFF_TIME=lambda: timedelta(seconds=30)) From 9a54187a6550471a7e8ba64a50f626caebc9297f Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Tue, 25 Jul 2023 00:17:31 +0500 Subject: [PATCH 285/372] chore: update docstrings about settings.AXES_COOLOFF_TIME --- axes/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index c951281..b500d5c 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -37,7 +37,7 @@ def get_cache_timeout() -> Optional[int]: The cache timeout can be either None if not configured or integer of seconds if configured. - Notice that the settings.AXES_COOLOFF_TIME can be None, timedelta, integer, callable, or str path, + Notice that the settings.AXES_COOLOFF_TIME can be None, timedelta, float, integer, callable, or str path, and this function offers a unified _integer or None_ representation of that configuration for use with the Django cache backends. """ @@ -54,7 +54,7 @@ def get_cool_off() -> Optional[timedelta]: The return value is either None or timedelta. - Notice that the settings.AXES_COOLOFF_TIME is either None, timedelta, or integer of hours, + Notice that the settings.AXES_COOLOFF_TIME is either None, timedelta, or integer/float of hours, and this function offers a unified _timedelta or None_ representation of that configuration for use with the Axes internal implementations. From 72f3be394deff245f68ff4e1f66eca295092802a Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Wed, 26 Jul 2023 10:05:23 +0200 Subject: [PATCH 286/372] #438: Added docs about GDPR best practice --- docs/3_usage.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/3_usage.rst b/docs/3_usage.rst index 3acc35d..73201c8 100644 --- a/docs/3_usage.rst +++ b/docs/3_usage.rst @@ -107,3 +107,25 @@ In your code, you can use the ``axes.utils.reset`` function. Please note that if you give both ``username`` and ``ip`` arguments to ``reset`` that attempts that have both the set IP and username are reset. The effective behaviour of ``reset`` is to ``and`` the terms instead of ``or`` ing them. + + + +Data privacy and GDPR +^^^^^^^^^^^^^^^^^^^^^ + +Most European countries have quite strict laws regarding data protection and privacy - especially Germany. Even if you +are not working for a German company, it's highly recommended and good practice to treat your sensitive user data with +care. The general rule here is that you shouldn't store what you don't need. + +When dealing with brute-force protection, the IP address and the username (often the email address) are most crucial. +Given that you can perfectly use `django-axes` without locking the user out by IP but by username, it does make sense to +avoid storing the IP address at all. You can lose what you don't have. + +You can adjust the AXES settings as follows:: + + # Block by Username only (i.e.: Same user different IP is still blocked, but different user same IP is not) + AXES_LOCKOUT_PARAMETERS = ["username"] + + # Disable logging the IP-Address of failed login attempts by returning None for attempts to get the IP + # Ignore assigning a lambda function to a variable for brevity + AXES_CLIENT_IP_CALLABLE = lambda x: None # noqa: E731 From c234f53e0df99335e3c03e9e99f2d65695bd71cb Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 26 Jul 2023 10:12:56 +0200 Subject: [PATCH 287/372] Changes and typo fix --- docs/3_usage.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/3_usage.rst b/docs/3_usage.rst index 73201c8..68077a0 100644 --- a/docs/3_usage.rst +++ b/docs/3_usage.rst @@ -113,13 +113,12 @@ In your code, you can use the ``axes.utils.reset`` function. Data privacy and GDPR ^^^^^^^^^^^^^^^^^^^^^ -Most European countries have quite strict laws regarding data protection and privacy - especially Germany. Even if you -are not working for a German company, it's highly recommended and good practice to treat your sensitive user data with -care. The general rule here is that you shouldn't store what you don't need. +Most European countries have quite strict laws regarding data protection and privacy It's highly recommended and good +practice to treat your sensitive user data with care. The general rule here is that you shouldn't store what you don't need. When dealing with brute-force protection, the IP address and the username (often the email address) are most crucial. Given that you can perfectly use `django-axes` without locking the user out by IP but by username, it does make sense to -avoid storing the IP address at all. You can lose what you don't have. +avoid storing the IP address at all. You can not lose what you don't have. You can adjust the AXES settings as follows:: From ab787245187aa3f94c7d2cf4262148f5712471d5 Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 26 Jul 2023 10:13:21 +0200 Subject: [PATCH 288/372] Typo fix --- docs/3_usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/3_usage.rst b/docs/3_usage.rst index 68077a0..e5a2b29 100644 --- a/docs/3_usage.rst +++ b/docs/3_usage.rst @@ -113,7 +113,7 @@ In your code, you can use the ``axes.utils.reset`` function. Data privacy and GDPR ^^^^^^^^^^^^^^^^^^^^^ -Most European countries have quite strict laws regarding data protection and privacy It's highly recommended and good +Most European countries have quite strict laws regarding data protection and privacy. It's highly recommended and good practice to treat your sensitive user data with care. The general rule here is that you shouldn't store what you don't need. When dealing with brute-force protection, the IP address and the username (often the email address) are most crucial. From e9db2d96314f429278a0436b72b5222f107cd90a Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Thu, 27 Jul 2023 09:25:08 +0200 Subject: [PATCH 289/372] #1093: "AXES_SENSITIVE_PARAMETERS" now have sensible defaults to follow "privacy-by-design" --- axes/conf.py | 2 +- docs/4_configuration.rst | 2 +- tests/test_helpers.py | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/axes/conf.py b/axes/conf.py index daf1c70..2751445 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -124,7 +124,7 @@ settings.AXES_ALLOWED_CORS_ORIGINS = getattr(settings, "AXES_ALLOWED_CORS_ORIGIN settings.AXES_SENSITIVE_PARAMETERS = getattr( settings, "AXES_SENSITIVE_PARAMETERS", - [], + ["username", "ip_address"], ) # set the callable for the readable string that can be used in diff --git a/docs/4_configuration.rst b/docs/4_configuration.rst index a2f3abe..f64320e 100644 --- a/docs/4_configuration.rst +++ b/docs/4_configuration.rst @@ -59,7 +59,7 @@ The following ``settings.py`` options are available for customizing Axes behavio +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_PASSWORD_FORM_FIELD | 'password' | The name of the form or credentials field that contains your users password. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| AXES_SENSITIVE_PARAMETERS | [] | Configures POST and GET parameter values (in addition to the value of ``AXES_PASSWORD_FORM_FIELD``) to mask in login attempt logging. | +| AXES_SENSITIVE_PARAMETERS | ["username", "ip_address"] | Configures POST and GET parameter values (in addition to the value of ``AXES_PASSWORD_FORM_FIELD``) to mask in login attempt logging. Defaults enable privacy-by-design. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | AXES_NEVER_LOCKOUT_GET | False | If ``True``, Axes will never lock out HTTP GET requests. | +------------------------------------------------------+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 60f9128..0201fbb 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -82,6 +82,7 @@ class TimestampTestCase(AxesTestCase): self.assertEqual(get_cool_off_iso8601(delta), iso_duration) +@override_settings(AXES_SENSITIVE_PARAMETERS=[]) class ClientStringTestCase(AxesTestCase): @staticmethod def get_expected_client_str(*args, **kwargs): @@ -1020,6 +1021,7 @@ class AxesCleanseParamsTestCase(AxesTestCase): "other_sensitive_data": "sensitive", } + @override_settings(AXES_SENSITIVE_PARAMETERS=[]) def test_cleanse_parameters(self): cleansed = cleanse_parameters(self.parameters) self.assertEqual("test_user", cleansed["username"]) @@ -1041,6 +1043,7 @@ class AxesCleanseParamsTestCase(AxesTestCase): self.assertEqual("********************", cleansed["password"]) self.assertEqual("********************", cleansed["other_sensitive_data"]) + @override_settings(AXES_SENSITIVE_PARAMETERS=[]) @override_settings(AXES_PASSWORD_FORM_FIELD=None) def test_cleanse_parameters_override_empty(self): cleansed = cleanse_parameters(self.parameters) From 61da8fbdfce6b644230265d487e77c83345e9278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sun, 30 Jul 2023 19:03:18 +0300 Subject: [PATCH 290/372] Version 6.1.0 --- CHANGES.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 342717f..278efb8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changes ======= +6.1.0 (2023-07-30) +------------------ + +- Set ``AXES_SENSITIVE_PARAMETERS`` default value to ``["username", "ip_address"]`` in addition to the ``AXES_PASSWORD_FORM_FIELD`` configuration flag. + This masks the username and IP address fields by default in the logs when writing information about login attempts to the application logs. + Reverting to old configuration default of ``[]`` can be done by setting ``AXES_SENSITIVE_PARAMETERS = []`` in the Django project settings file. + [GitRon] +- Improve documentation on GDPR and privacy notes and configuration flags. + [GitRon] + + 6.0.5 (2023-07-01) ------------------ From 634d54ba3d5c3d8222a96c7934279ba7721bcfec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:49:42 +0000 Subject: [PATCH 291/372] chore(deps): bump mypy from 1.4.1 to 1.5.0 Bumps [mypy](https://github.com/python/mypy) from 1.4.1 to 1.5.0. - [Commits](https://github.com/python/mypy/compare/v1.4.1...v1.5.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index a5af4a8..eacd69b 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.7.0 -mypy==1.4.1 +mypy==1.5.0 prospector==1.10.2 types-pkg_resources # Type stub From c80c812296ffaf489318ce96ed10897384d3019a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:56:46 +0000 Subject: [PATCH 292/372] chore(deps): bump coverage from 7.2.7 to 7.3.0 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.7 to 7.3.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.7...7.3.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 3631d07..a5231b9 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.2.7 +coverage==7.3.0 pytest==7.4.0 pytest-cov==4.1.0 pytest-django==4.5.2 From 2e9b3e485c30c9b76b10a95aea56a38bd528c408 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:56:55 +0000 Subject: [PATCH 293/372] chore(deps): bump tox from 4.6.4 to 4.8.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.6.4 to 4.8.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.6.4...4.8.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1685b4e..a93b41d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.6.4 +tox==4.8.0 From 7d1ae3bca2f6f9e4e7519c15de9ccead91c8d1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:13:17 +0300 Subject: [PATCH 294/372] Downgrade black to 23.3.0 to fix RTD build --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index eacd69b..c0d8d60 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.7.0 +black==23.3.0 mypy==1.5.0 prospector==1.10.2 types-pkg_resources # Type stub From c1884d257a7d365ba5fd07dd3ed4ce53391856be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:29:16 +0300 Subject: [PATCH 295/372] Downgrade mypy to 1.4.1 to fix RTD build --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index c0d8d60..5d23aba 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.5.0 +mypy==1.4.1 prospector==1.10.2 types-pkg_resources # Type stub From 16cc250622c289939f28c17a44b3cc583be8a0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:32:44 +0300 Subject: [PATCH 296/372] Add example RTD build configuration --- .readthedocs.yaml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..b468076 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,35 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # You can also specify other tool versions: + # nodejs: "20" + # rust: "1.70" + # golang: "1.20" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt From 0688526722c820353967385f41bacc6d4d336a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:35:06 +0300 Subject: [PATCH 297/372] Enable PDF and epub doc formats --- .readthedocs.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b468076..9f5919b 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -23,9 +23,9 @@ sphinx: # fail_on_warning: true # Optionally build your docs in additional formats such as PDF and ePub -# formats: -# - pdf -# - epub +formats: + - pdf + - epub # Optional but recommended, declare the Python requirements required # to build your documentation From 6d88064e243d9fd85838f3a2c77ca837adc57886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:35:11 +0300 Subject: [PATCH 298/372] Add doc requirements --- .readthedocs.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9f5919b..43ab19f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -30,6 +30,6 @@ formats: # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -# python: -# install: -# - requirements: docs/requirements.txt +python: + install: + - requirements: requirements.txt From c4fa363398b87b448be6024f638ef7d1c2ed5320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:40:46 +0300 Subject: [PATCH 299/372] Revert "Downgrade mypy to 1.4.1 to fix RTD build" This reverts commit c1884d257a7d365ba5fd07dd3ed4ce53391856be. --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5d23aba..c0d8d60 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.3.0 -mypy==1.4.1 +mypy==1.5.0 prospector==1.10.2 types-pkg_resources # Type stub From 3e54945ef5d0fda15c74932a47a6e7470471a91d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:40:52 +0300 Subject: [PATCH 300/372] Revert "Downgrade black to 23.3.0 to fix RTD build" This reverts commit 7d1ae3bca2f6f9e4e7519c15de9ccead91c8d1a4. --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index c0d8d60..eacd69b 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.3.0 +black==23.7.0 mypy==1.5.0 prospector==1.10.2 types-pkg_resources # Type stub From 976399e30a4a688ba05344e3ffa95e05048c4d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Thu, 17 Aug 2023 12:42:58 +0300 Subject: [PATCH 301/372] Clean up RTD build spec Remove unnecessary comments --- .readthedocs.yaml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 43ab19f..a9cff7d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,35 +1,15 @@ # Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required version: 2 - -# Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" - # You can also specify other tool versions: - # nodejs: "20" - # rust: "1.70" - # golang: "1.20" - -# Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/conf.py - # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs - # builder: "dirhtml" - # Fail on all warnings to avoid broken references - # fail_on_warning: true - -# Optionally build your docs in additional formats such as PDF and ePub formats: - pdf - epub - -# Optional but recommended, declare the Python requirements required -# to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: requirements.txt From b14e586f16d746a6e165f63e774e2bed3b5dd32d Mon Sep 17 00:00:00 2001 From: Ronny Vedrilla Date: Thu, 17 Aug 2023 16:33:28 +0200 Subject: [PATCH 302/372] #1093: Setting proper headline (h2) for GDPR section --- docs/3_usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/3_usage.rst b/docs/3_usage.rst index e5a2b29..cfd5a09 100644 --- a/docs/3_usage.rst +++ b/docs/3_usage.rst @@ -111,7 +111,7 @@ In your code, you can use the ``axes.utils.reset`` function. Data privacy and GDPR -^^^^^^^^^^^^^^^^^^^^^ +--------------------- Most European countries have quite strict laws regarding data protection and privacy. It's highly recommended and good practice to treat your sensitive user data with care. The general rule here is that you shouldn't store what you don't need. From 9861ac6404b5e4000d7420f3690660cb10278237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 12:56:06 +0000 Subject: [PATCH 303/372] chore(deps): bump mypy from 1.5.0 to 1.5.1 Bumps [mypy](https://github.com/python/mypy) from 1.5.0 to 1.5.1. - [Commits](https://github.com/python/mypy/compare/v1.5.0...v1.5.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index eacd69b..53f22a0 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.7.0 -mypy==1.5.0 +mypy==1.5.1 prospector==1.10.2 types-pkg_resources # Type stub From fe4aa82ca1d5bc01438f5be56d1f92f8842647cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 12:55:57 +0000 Subject: [PATCH 304/372] chore(deps): bump tox from 4.8.0 to 4.9.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.8.0 to 4.9.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.8.0...4.9.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a93b41d..4b53a1c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.2.2 -tox==4.8.0 +tox==4.9.0 From f6d5aa043dfe87f4cd4a697469a8df1d6ea6e4e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:27:04 +0000 Subject: [PATCH 305/372] chore(deps): bump sphinx-rtd-theme from 1.2.2 to 1.3.0 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.2.2 to 1.3.0. - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.2.2...1.3.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4b53a1c..ce2b1ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.2.2 +sphinx_rtd_theme==1.3.0 tox==4.9.0 From fa8d059977a1098b7a5c3ac16a652fcebd5c615f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:35:56 +0000 Subject: [PATCH 306/372] chore(deps): bump tox from 4.9.0 to 4.10.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.9.0 to 4.10.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.9.0...4.10.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ce2b1ce..c34593d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.3.0 -tox==4.9.0 +tox==4.10.0 From 5f52f4383fdac99ce037c7f2ac127dbe190f6762 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:26:05 +0000 Subject: [PATCH 307/372] chore(deps): bump tox from 4.10.0 to 4.11.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.10.0 to 4.11.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.10.0...4.11.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c34593d..1776a48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.3.0 -tox==4.10.0 +tox==4.11.0 From 4861f3c988e16bd39c9a59c19d978ec7241a80ec Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Thu, 31 Aug 2023 22:42:44 +0500 Subject: [PATCH 308/372] fix: fix TransactionManagementError --- axes/handlers/database.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/axes/handlers/database.py b/axes/handlers/database.py index 035cc60..95678b2 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -1,8 +1,8 @@ from logging import getLogger from typing import Optional -from django.db import transaction -from django.db.models import F, Sum, Value, Q +from django.db import router, transaction +from django.db.models import F, Q, Sum, Value from django.db.models.functions import Concat from django.utils import timezone @@ -171,7 +171,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." ) else: - with transaction.atomic(): + with transaction.atomic(using=router.db_for_write(AccessAttempt)): ( attempt, created, @@ -243,7 +243,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): # 5. database entry: Log for ever the attempt in the AccessFailureLog if settings.AXES_ENABLE_ACCESS_FAILURE_LOG: - with transaction.atomic(): + with transaction.atomic(using=router.db_for_write(AccessFailureLog)): AccessFailureLog.objects.create( username=username, ip_address=request.axes_ip_address, From 48bda54c30e18c5481a6424e07119a92c05b5792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 1 Sep 2023 10:54:03 +0300 Subject: [PATCH 309/372] Version 6.1.1 --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 278efb8..e2807d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,14 @@ Changes ======= +6.1.1 (2023-08-01) +------------------ + +- Fix `TransactionManagementError` when using the database handler + with a custom database with for `AccessAttempt` or `AccessFailureLog`. + [hirotasoshu] + + 6.1.0 (2023-07-30) ------------------ From e781d67514f8c67e772537d49918470c3b977e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 1 Sep 2023 10:54:43 +0300 Subject: [PATCH 310/372] Fix rST syntax --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e2807d4..d1db68c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,8 +6,8 @@ Changes 6.1.1 (2023-08-01) ------------------ -- Fix `TransactionManagementError` when using the database handler - with a custom database with for `AccessAttempt` or `AccessFailureLog`. +- Fix ``TransactionManagementError`` when using the database handler + with a custom database with for ``AccessAttempt`` or ``AccessFailureLog``. [hirotasoshu] From c007839e307a11d9c562dbbf9306046402292246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:15:41 +0000 Subject: [PATCH 311/372] chore(deps): bump coverage from 7.3.0 to 7.3.2 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.3.0 to 7.3.2. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.3.0...7.3.2) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index a5231b9..83e2d67 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.3.0 +coverage==7.3.2 pytest==7.4.0 pytest-cov==4.1.0 pytest-django==4.5.2 From 28849e0e61c9d3856820f23a9b084f78e08405b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:45:06 +0000 Subject: [PATCH 312/372] chore(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8958a0f..51d04ba 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eace86b..72b430c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e687b0a..5592c76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 From b5f464ef96d3f269beb8d6dff38cc26fd924689d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:32:00 +0000 Subject: [PATCH 313/372] chore(deps): bump pytest from 7.4.0 to 7.4.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.0 to 7.4.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.0...7.4.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index a5231b9..b0af09f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.3.0 -pytest==7.4.0 +pytest==7.4.2 pytest-cov==4.1.0 pytest-django==4.5.2 pytest-subtests==0.11.0 From c57bac8aa29e2aa8751501940b923601334dda0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:21:33 +0000 Subject: [PATCH 314/372] chore(deps): bump tox from 4.11.0 to 4.11.3 Bumps [tox](https://github.com/tox-dev/tox) from 4.11.0 to 4.11.3. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.11.0...4.11.3) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1776a48..5d5bb0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==1.3.0 -tox==4.11.0 +tox==4.11.3 From 36f828b4f58b88787ec23c8f1b71db84d226aec4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:21:39 +0000 Subject: [PATCH 315/372] chore(deps): bump black from 23.7.0 to 23.9.1 Bumps [black](https://github.com/psf/black) from 23.7.0 to 23.9.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.7.0...23.9.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 53f22a0..a3a201c 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.7.0 +black==23.9.1 mypy==1.5.1 prospector==1.10.2 types-pkg_resources # Type stub From eb7b703a7021f6af046b6aa487c49956b12f8cce Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 7 Sep 2023 10:08:42 -0700 Subject: [PATCH 316/372] Avoid running data migration on incorrect databases --- axes/migrations/0007_alter_accessattempt_unique_together.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/axes/migrations/0007_alter_accessattempt_unique_together.py b/axes/migrations/0007_alter_accessattempt_unique_together.py index 508c4fd..81c1736 100644 --- a/axes/migrations/0007_alter_accessattempt_unique_together.py +++ b/axes/migrations/0007_alter_accessattempt_unique_together.py @@ -1,12 +1,16 @@ # Generated by Django 3.2.7 on 2021-09-13 15:16 -from django.db import migrations +from django.db import migrations, router from django.db.models import Count def deduplicate_attempts(apps, schema_editor): AccessAttempt = apps.get_model("axes", "AccessAttempt") db_alias = schema_editor.connection.alias + + if db_alias != router.db_for_write(AccessAttempt): + return + duplicated_attempts = ( AccessAttempt.objects.using(db_alias) .values("username", "user_agent", "ip_address") From 6d039adb310c6dcbff7527128b25707ea9d6cdb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 12:29:28 +0000 Subject: [PATCH 317/372] chore(deps): bump prospector from 1.10.2 to 1.10.3 Bumps [prospector](https://github.com/PyCQA/prospector) from 1.10.2 to 1.10.3. - [Release notes](https://github.com/PyCQA/prospector/releases) - [Changelog](https://github.com/landscapeio/prospector/blob/master/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/prospector/compare/1.10.2...v1.10.3) --- updated-dependencies: - dependency-name: prospector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index a3a201c..ba52c77 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.9.1 mypy==1.5.1 -prospector==1.10.2 +prospector==1.10.3 types-pkg_resources # Type stub From 6d136ee6e685955d5580a4d03c89650f54c63db0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 12:29:40 +0000 Subject: [PATCH 318/372] chore(deps): bump black from 23.9.1 to 23.10.0 Bumps [black](https://github.com/psf/black) from 23.9.1 to 23.10.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.9.1...23.10.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index ba52c77..5b843d6 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.9.1 +black==23.10.0 mypy==1.5.1 prospector==1.10.3 types-pkg_resources # Type stub From f65b74121e8d9e3dee54425afaf72395b7c4d1e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:32:47 +0000 Subject: [PATCH 319/372] chore(deps): bump mypy from 1.5.1 to 1.6.1 Bumps [mypy](https://github.com/python/mypy) from 1.5.1 to 1.6.1. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.5.1...v1.6.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 5b843d6..b96a593 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.10.0 -mypy==1.5.1 +mypy==1.6.1 prospector==1.10.3 types-pkg_resources # Type stub From 734117c4326b498f8a76a7b73236a396ca20f882 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:46:39 +0000 Subject: [PATCH 320/372] chore(deps): bump black from 23.10.0 to 23.10.1 Bumps [black](https://github.com/psf/black) from 23.10.0 to 23.10.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.10.0...23.10.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index b96a593..bd36a77 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.10.0 +black==23.10.1 mypy==1.6.1 prospector==1.10.3 types-pkg_resources # Type stub From f64c1526d71abfb695fa59a2757dccb37476005d Mon Sep 17 00:00:00 2001 From: Laurent Lasudry Date: Mon, 6 Nov 2023 12:35:54 +0100 Subject: [PATCH 321/372] Add French translations --- axes/locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 1752 bytes axes/locale/fr/LC_MESSAGES/django.po | 109 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 axes/locale/fr/LC_MESSAGES/django.mo create mode 100644 axes/locale/fr/LC_MESSAGES/django.po diff --git a/axes/locale/fr/LC_MESSAGES/django.mo b/axes/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..527405696d2df48a31525cb8f0fce2430793941b GIT binary patch literal 1752 zcmbW1zmMER6vrnJAX$E2_*H;3Q~^a^b`ODqFBhHJ^<7r%53}n#DIg(D>^YwkuV*zg zK4lRVe*-jhbZi3>9W7hX00k8^bW{|95(!0opY7Ydqe6^s_A@hn^WOJ8Ki{t#=Rai_ z&tblZ`90=Km>-?P55^zhli;7=yWqJ87~26S;A7w?;N#%u;6?B&@B;V^_$c@-*Z_a5 z_rHK-_gl?-;Cbx-0?F<^NcS&3$k-F$21xN=uJ`SFzXOte08*Uy!Q0T7fG=SG6P#-B zcW?rJ@DO9qfM0`8f!~3Y^UomVaStRve}m-r!o!R`4_*Q(zbiH00`bT8@I!V(@M-WS zNdE4Cbnj#E68ITNalZs9? zi?Wr2Vp82GCmN*v*>Y9sW?mFYV|b>bJ6si(_NvHjc_3q#Tc!9^tPkg8VNF7KzfyrfwZDB4n~vuS|IEZ+c#2k zFOj(|m$^Vdj1Z}Qd~ z?j|i-Ef_jud7P5-+U}3aOe$Q|^qyf+&9jlM9}3AM-#>aDcPclY~!KJ*4Y zzvd6TUVq2ycE|o?avi35Qiw!4p%hb>_r2lXK1sgT@9$)3Q|V-F1F3`Fz+dOrZnhfK zG^nl<&S%hVd|e@1l;Yu9qL8*Q>ryjn#My==d46eY{+(JffM7mDS{cLN)(i$OB30 z5m7G}=tMDxTV!dt6*_J*YJoB@u~yB7YI+NC literal 0 HcmV?d00001 diff --git a/axes/locale/fr/LC_MESSAGES/django.po b/axes/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..2fd4ced --- /dev/null +++ b/axes/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,109 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-06 05:21-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: admin.py:27 +msgid "Form Data" +msgstr "Données de formulaire" + +#: admin.py:28 admin.py:65 admin.py:100 +msgid "Meta Data" +msgstr "Métadonnées" + +#: conf.py:108 +msgid "Account locked: too many login attempts. Please try again later." +msgstr "" +"Compte verrouillé: trop de tentatives de connexion. Veuillez réessayer plus " +"tard." + +#: conf.py:116 +msgid "" +"Account locked: too many login attempts. Contact an admin to unlock your " +"account." +msgstr "" +"Compte verrouillé: trop de tentatives de connexion. Contactez un " +"administrateur pour déverrouiller votre compte." + +#: models.py:6 +msgid "User Agent" +msgstr "User Agent" + +#: models.py:8 +msgid "IP Address" +msgstr "Adresse IP" + +#: models.py:10 +msgid "Username" +msgstr "Nom d'utilisateur" + +#: models.py:12 +msgid "HTTP Accept" +msgstr "HTTP Accept" + +#: models.py:14 +msgid "Path" +msgstr "Chemin" + +#: models.py:16 +msgid "Attempt Time" +msgstr "Date de la tentative" + +#: models.py:26 +msgid "Access lock out" +msgstr "Verrouillage de l'accès" + +#: models.py:34 +msgid "access failure" +msgstr "échec de connexion" + +#: models.py:35 +msgid "access failures" +msgstr "échecs de connexion" + +#: models.py:39 +msgid "GET Data" +msgstr "Données GET" + +#: models.py:41 +msgid "POST Data" +msgstr "Données POST" + +#: models.py:43 +msgid "Failed Logins" +msgstr "Nombre d'échecs" + +#: models.py:49 +msgid "access attempt" +msgstr "tentative de connexion" + +#: models.py:50 +msgid "access attempts" +msgstr "tentatives de connexion" + +#: models.py:55 +msgid "Logout Time" +msgstr "Date de la déconnexion" + +#: models.py:61 +msgid "access log" +msgstr "connexion" + +#: models.py:62 +msgid "access logs" +msgstr "connexions" From 95d46c638f31fad2ffb209b8b87bfabb8b9a4631 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:51:46 +0000 Subject: [PATCH 322/372] chore(deps): bump pytest-django from 4.5.2 to 4.7.0 Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.5.2 to 4.7.0. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.5.2...v4.7.0) --- updated-dependencies: - dependency-name: pytest-django dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 14166f6..dbd5395 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,5 +3,5 @@ django-ipware>=3 coverage==7.3.2 pytest==7.4.2 pytest-cov==4.1.0 -pytest-django==4.5.2 +pytest-django==4.7.0 pytest-subtests==0.11.0 From ac0a419a274bfbcb3ca74c7023727721ba068c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 12:04:58 +0000 Subject: [PATCH 323/372] chore(deps): bump mypy from 1.6.1 to 1.7.0 Bumps [mypy](https://github.com/python/mypy) from 1.6.1 to 1.7.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.6.1...v1.7.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index bd36a77..bf7eee3 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.10.1 -mypy==1.6.1 +mypy==1.7.0 prospector==1.10.3 types-pkg_resources # Type stub From 846b71ea019152f487f8aa14aa90b6106a9b29f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:01:38 +0000 Subject: [PATCH 324/372] chore(deps): bump pytest from 7.4.2 to 7.4.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.2 to 7.4.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.2...7.4.3) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index dbd5395..c7d8489 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.3.2 -pytest==7.4.2 +pytest==7.4.3 pytest-cov==4.1.0 pytest-django==4.7.0 pytest-subtests==0.11.0 From 6b175c2c20edcd7def9209faf332e7648323c99b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:03:08 +0000 Subject: [PATCH 325/372] chore(deps): bump black from 23.10.1 to 23.11.0 Bumps [black](https://github.com/psf/black) from 23.10.1 to 23.11.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.10.1...23.11.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index bf7eee3..ba9ea53 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.10.1 +black==23.11.0 mypy==1.7.0 prospector==1.10.3 types-pkg_resources # Type stub From 219b465bc2f81b50381fc7c50e1405ea79c1c306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:19:32 +0000 Subject: [PATCH 326/372] chore(deps): bump mypy from 1.7.0 to 1.7.1 Bumps [mypy](https://github.com/python/mypy) from 1.7.0 to 1.7.1. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index ba9ea53..80c241e 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.11.0 -mypy==1.7.0 +mypy==1.7.1 prospector==1.10.3 types-pkg_resources # Type stub From 45d61b21ae1766a40af3df107afd619850233168 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:20:14 +0000 Subject: [PATCH 327/372] chore(deps): bump sphinx-rtd-theme from 1.3.0 to 2.0.0 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.3.0 to 2.0.0. - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.3.0...2.0.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5d5bb0f..ba828ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e . -r requirements-qa.txt -r requirements-test.txt -sphinx_rtd_theme==1.3.0 +sphinx_rtd_theme==2.0.0 tox==4.11.3 From 6e64c62fbfe99adfdcf39059bd1a1762afca01fd Mon Sep 17 00:00:00 2001 From: Pierre Lalet Date: Wed, 15 Nov 2023 21:26:33 +0100 Subject: [PATCH 328/372] 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) From d2b65aad321bd7d80d7d10da8475f1c01c80fe8f Mon Sep 17 00:00:00 2001 From: Curtis Maloney Date: Fri, 1 Dec 2023 11:59:19 +1100 Subject: [PATCH 329/372] Update 2_installation.rst Fix block indentation in Installation documentation. --- docs/2_installation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/2_installation.rst b/docs/2_installation.rst index e1d6a15..ccf68d0 100644 --- a/docs/2_installation.rst +++ b/docs/2_installation.rst @@ -34,10 +34,10 @@ After installing the package, the project settings need to be configured. 'django.contrib.auth.backends.ModelBackend', ] - For backwards compatibility, ``AxesBackend`` can be used in place of ``AxesStandaloneBackend``. - The only difference is that ``AxesBackend`` also provides the permissions-checking functionality - of Django's ``ModelBackend`` behind the scenes. We recommend using ``AxesStandaloneBackend`` - if you have any custom logic to override Django's standard permissions checks. +For backwards compatibility, ``AxesBackend`` can be used in place of ``AxesStandaloneBackend``. +The only difference is that ``AxesBackend`` also provides the permissions-checking functionality +of Django's ``ModelBackend`` behind the scenes. We recommend using ``AxesStandaloneBackend`` +if you have any custom logic to override Django's standard permissions checks. **3.** Add ``axes.middleware.AxesMiddleware`` to your list of ``MIDDLEWARE``:: From aafc24e47909c0019de8021e3ad9900c621fc234 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:24:36 +0000 Subject: [PATCH 330/372] chore(deps): bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72b430c..1e90cc3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5592c76..130e70a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 2416fcc358b6cab5721c31f57402a4c19aa9b6d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:32:42 +0000 Subject: [PATCH 331/372] chore(deps): bump tox from 4.11.3 to 4.11.4 Bumps [tox](https://github.com/tox-dev/tox) from 4.11.3 to 4.11.4. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.11.3...4.11.4) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ba828ac..0017cfe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.11.3 +tox==4.11.4 From 2ee7ea17310c8d733f8ad1896f600d78520a2255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Fri, 8 Dec 2023 19:49:40 +0200 Subject: [PATCH 332/372] Version 6.2.0 --- CHANGES.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d1db68c..6b57b6e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,19 @@ Changes ======= +6.2.0 (2023-12-08) +------------------ + +- Update documentation. + [funkybob] +- Add new management command ``axes_reset_ip_username``. + [p-l-] +- Add French translations. + [laulaz] +- Avoid running data migration on incorrect databases. + [christianbundy] + + 6.1.1 (2023-08-01) ------------------ From 6f2584b440959fb29a5ccf2e323629c0bba3629c Mon Sep 17 00:00:00 2001 From: Taikono-Himazin Date: Wed, 13 Dec 2023 13:35:22 +0900 Subject: [PATCH 333/372] Add async support to middleware --- axes/middleware.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/axes/middleware.py b/axes/middleware.py index 1f70ce9..b965907 100644 --- a/axes/middleware.py +++ b/axes/middleware.py @@ -1,5 +1,6 @@ from typing import Callable +from asgiref.sync import iscoroutinefunction, markcoroutinefunction, sync_to_async from django.conf import settings from django.http import HttpRequest, HttpResponse @@ -30,15 +31,36 @@ class AxesMiddleware: - ``AXES_PERMALOCK_MESSAGE``. """ + async_capable = True + sync_capable = True + def __init__(self, get_response: Callable) -> None: self.get_response = get_response + if iscoroutinefunction(self.get_response): + markcoroutinefunction(self) def __call__(self, request: HttpRequest) -> HttpResponse: - response = self.get_response(request) + # Exit out to async mode, if needed + if iscoroutinefunction(self): + return self.__acall__(request) + response = self.get_response(request) if settings.AXES_ENABLED: if getattr(request, "axes_locked_out", None): credentials = getattr(request, "axes_credentials", None) response = get_lockout_response(request, credentials) # type: ignore return response + + async def __acall__(self, request: HttpRequest) -> HttpResponse: + response = await self.get_response(request) + + if settings.AXES_ENABLED: + if getattr(request, "axes_locked_out", None): + credentials = getattr(request, "axes_credentials", None) + response = await sync_to_async( + get_lockout_response, + thread_sensitive=True + )(request, credentials) # type: ignore + + return response From bd55542b8092ff62108e2c434ea06ae1ce0540c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:01:27 +0000 Subject: [PATCH 334/372] chore(deps): bump github/codeql-action from 2 to 3 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 51d04ba..4da4055 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,7 +18,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -26,7 +26,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below). - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -40,4 +40,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 73d4b71ccb7553e37fd393948daaf405444fe356 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:07:21 +0000 Subject: [PATCH 335/372] chore(deps): bump coverage from 7.3.2 to 7.3.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.3.2 to 7.3.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.3.2...7.3.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c7d8489..2652a20 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.3.2 +coverage==7.3.4 pytest==7.4.3 pytest-cov==4.1.0 pytest-django==4.7.0 From 1394e1aa2783b7a5af817736a60e5768fe08828c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:36:39 +0000 Subject: [PATCH 336/372] chore(deps): bump black from 23.11.0 to 23.12.1 Bumps [black](https://github.com/psf/black) from 23.11.0 to 23.12.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.11.0...23.12.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 80c241e..f36dd94 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.11.0 +black==23.12.1 mypy==1.7.1 prospector==1.10.3 types-pkg_resources # Type stub From 1aa273d99acec3189067e750b350b060679bbf05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Dec 2023 10:32:20 +0000 Subject: [PATCH 337/372] chore(deps): bump mypy from 1.7.1 to 1.8.0 Bumps [mypy](https://github.com/python/mypy) from 1.7.1 to 1.8.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.7.1...v1.8.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index f36dd94..10e95a4 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ black==23.12.1 -mypy==1.7.1 +mypy==1.8.0 prospector==1.10.3 types-pkg_resources # Type stub From 38e316ca87e47c53453ba5cd6aa58c296f408d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 27 Dec 2023 12:40:23 +0200 Subject: [PATCH 338/372] Fix code formatting --- axes/middleware.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/axes/middleware.py b/axes/middleware.py index b965907..0b8d16c 100644 --- a/axes/middleware.py +++ b/axes/middleware.py @@ -59,8 +59,9 @@ class AxesMiddleware: if getattr(request, "axes_locked_out", None): credentials = getattr(request, "axes_credentials", None) response = await sync_to_async( - get_lockout_response, - thread_sensitive=True - )(request, credentials) # type: ignore + get_lockout_response, thread_sensitive=True + )( + request, credentials + ) # type: ignore return response From 87bdcbee3e5999477a140c437cb7810b6394fc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Wed, 27 Dec 2023 12:43:04 +0200 Subject: [PATCH 339/372] Version 6.3.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6b57b6e..1412888 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.3.0 (2023-12-27) +------------------ + +- Add async support to middleware. + [Taikono-Himazin] + + 6.2.0 (2023-12-08) ------------------ From b3286ad1775bc4d8147f14bcb81ae637674dd3d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Dec 2023 12:02:16 +0000 Subject: [PATCH 340/372] chore(deps): bump coverage from 7.3.4 to 7.4.0 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.3.4 to 7.4.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.3.4...7.4.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 2652a20..777e13e 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.3.4 +coverage==7.4.0 pytest==7.4.3 pytest-cov==4.1.0 pytest-django==4.7.0 From 5d19320179f36919111b500ec2c0854fe861a8fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 12:57:42 +0000 Subject: [PATCH 341/372] chore(deps): bump pytest from 7.4.3 to 7.4.4 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.3 to 7.4.4. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.3...7.4.4) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 777e13e..ea33914 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.4.0 -pytest==7.4.3 +pytest==7.4.4 pytest-cov==4.1.0 pytest-django==4.7.0 pytest-subtests==0.11.0 From d1faa7c413962b884c4a78095b47efe8f380b4b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:33:39 +0000 Subject: [PATCH 342/372] chore(deps): bump tox from 4.11.4 to 4.12.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.11.4 to 4.12.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.11.4...4.12.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0017cfe..286b0a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.11.4 +tox==4.12.0 From ea95552533b3a48a4fbd308d457022fa924edee5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:37:01 +0000 Subject: [PATCH 343/372] chore(deps): bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 130e70a..2372af9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,7 +53,7 @@ jobs: echo "::set-output name=dir::$(pip cache dir)" - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.pip-cache.outputs.dir }} key: From 493789640b70c6964439482c34d2636374f3e71c Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 26 Jan 2024 21:37:26 +0100 Subject: [PATCH 344/372] chore(deps): bump black from 23.12.1 to 24.1.0 AGAIN --- axes/helpers.py | 12 ++++++------ requirements-qa.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/axes/helpers.py b/axes/helpers.py index b500d5c..8570279 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -474,13 +474,13 @@ def get_lockout_response( if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest": json_response = JsonResponse(context, status=status) - json_response[ - "Access-Control-Allow-Origin" - ] = settings.AXES_ALLOWED_CORS_ORIGINS + json_response["Access-Control-Allow-Origin"] = ( + settings.AXES_ALLOWED_CORS_ORIGINS + ) json_response["Access-Control-Allow-Methods"] = "POST, OPTIONS" - json_response[ - "Access-Control-Allow-Headers" - ] = "Origin, Content-Type, Accept, Authorization, x-requested-with" + json_response["Access-Control-Allow-Headers"] = ( + "Origin, Content-Type, Accept, Authorization, x-requested-with" + ) return json_response if settings.AXES_LOCKOUT_TEMPLATE: diff --git a/requirements-qa.txt b/requirements-qa.txt index 10e95a4..42f39fc 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==23.12.1 +black==24.1.0 mypy==1.8.0 prospector==1.10.3 types-pkg_resources # Type stub From b583c8d0edde46734e56e3494c3ff4c6d38543b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:40:41 +0000 Subject: [PATCH 345/372] chore(deps): bump pytest-django from 4.7.0 to 4.8.0 Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: pytest-django dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index ea33914..9098a35 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,5 +3,5 @@ django-ipware>=3 coverage==7.4.0 pytest==7.4.4 pytest-cov==4.1.0 -pytest-django==4.7.0 +pytest-django==4.8.0 pytest-subtests==0.11.0 From bba4c8098936de64ca477666748bc56b0c01eab4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 12:42:06 +0000 Subject: [PATCH 346/372] chore(deps): bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 130e70a..fc595ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,6 +73,6 @@ jobs: DJANGO: ${{ matrix.django-version }} - name: Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: name: Python ${{ matrix.python-version }} From f9bb78ff4a04843ac3c49d70736ec2500f2a9032 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 3 Feb 2024 16:15:41 +0100 Subject: [PATCH 347/372] black=24.1.1 --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 42f39fc..d086029 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==24.1.0 +black==24.1.1 mypy==1.8.0 prospector==1.10.3 types-pkg_resources # Type stub From ab54d68ee1373d46e4abf3cf73ca1aa25503c0fa Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:39:35 +0100 Subject: [PATCH 348/372] Remove compatibility with `pkg_resources` --- axes/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/axes/__init__.py b/axes/__init__.py index e3011df..d2719e6 100644 --- a/axes/__init__.py +++ b/axes/__init__.py @@ -1,8 +1,3 @@ -try: - from importlib.metadata import version # New in Python 3.8 -except ImportError: - from pkg_resources import get_distribution # from setuptools, deprecated +from importlib.metadata import version - __version__ = get_distribution("django-axes").version -else: - __version__ = version("django-axes") +__version__ = version("django-axes") From 885ac8ddfb4b539a9cfc257f3a730e20e8ff35fe Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:47:58 +0100 Subject: [PATCH 349/372] Remove `setuptools` dependency --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 99ac508..9f9f45e 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ setup( use_scm_version=True, setup_requires=["setuptools_scm"], python_requires=">=3.7", - install_requires=["django>=3.2", "setuptools"], + install_requires=["django>=3.2"], extras_require={ "ipware": "django-ipware>=3", }, From 45c8119604bc1cd3a07366399b787e5f5303b721 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:10:36 +0000 Subject: [PATCH 350/372] chore(deps): bump tox from 4.12.0 to 4.13.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.12.0 to 4.13.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.12.0...4.13.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 286b0a2..acc2271 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.12.0 +tox==4.13.0 From 382e1b35afbe216ec8283ad1b5b9c90fdcf55053 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:03:50 +0000 Subject: [PATCH 351/372] chore(deps): bump pytest from 7.4.4 to 8.1.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.4 to 8.1.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.4...8.1.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index ea33914..d87fcd0 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.4.0 -pytest==7.4.4 +pytest==8.1.0 pytest-cov==4.1.0 pytest-django==4.7.0 pytest-subtests==0.11.0 From d224dd11adc812f50c20a323346752e38d79343a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:51:32 +0000 Subject: [PATCH 352/372] chore(deps): bump coverage from 7.4.0 to 7.4.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.4.0 to 7.4.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.4.0...7.4.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 8ab0239..b7c8027 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.4.0 +coverage==7.4.3 pytest==8.1.0 pytest-cov==4.1.0 pytest-django==4.8.0 From c62f5d319856f932d0c4f175ffc6500dbe3e6666 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:52:28 +0000 Subject: [PATCH 353/372] chore(deps): bump black from 23.12.1 to 24.2.0 Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.2.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.2.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index d086029..b29973d 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,4 @@ -black==24.1.1 +black==24.2.0 mypy==1.8.0 prospector==1.10.3 types-pkg_resources # Type stub From dbd16dd5b0b4933997bd9da2180769e069c27233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 4 Mar 2024 20:07:47 +0200 Subject: [PATCH 354/372] Update syntax to be compliant with new black version --- axes/apps.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/axes/apps.py b/axes/apps.py index 9be1bf2..99016d1 100644 --- a/axes/apps.py +++ b/axes/apps.py @@ -41,9 +41,11 @@ class AppConfig(apps.AppConfig): else: mode = "blocking by " + " or ".join( [ - param - if isinstance(param, str) - else "combination of " + " and ".join(param) + ( + param + if isinstance(param, str) + else "combination of " + " and ".join(param) + ) for param in settings.AXES_LOCKOUT_PARAMETERS ] ) From 048b1f9e9977d11ec08ce6d7a7962629da8ac679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 4 Mar 2024 20:18:31 +0200 Subject: [PATCH 355/372] Version 6.3.1 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1412888..fd96d7f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.3.1 (2024-03-04) +------------------ + +- Drop ``setuptools`` and ``pkg_resources`` dependencies. + [Viicos] + + 6.3.0 (2023-12-27) ------------------ From 0de5ab364c7178e214c5dbcf5b8b64cebafd8e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 4 Mar 2024 20:13:49 +0200 Subject: [PATCH 356/372] Add support for Python 3.12 and Django 5.0 --- .github/workflows/test.yml | 28 +++++++++++++++++----------- pyproject.toml | 17 +++++++++-------- setup.py | 3 ++- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea01b8a..ebbe003 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,30 +13,36 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] - django-version: ['3.2', '4.1', '4.2'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + django-version: ['3.2', '4.2', '5.0'] include: # Tox configuration for QA environment - - python-version: '3.11' + - python-version: '3.12' django-version: 'qa' # Django main - - python-version: '3.11' + - python-version: '3.12' django-version: 'main' experimental: true - # PyPy 3.8 - - python-version: 'pypy-3.8' + # PyPy 3.10 + - python-version: 'pypy-3.10' django-version: '3.2' experimental: true - - python-version: 'pypy-3.8' - django-version: '4.1' - experimental: true - - python-version: 'pypy-3.8' + - python-version: 'pypy-3.10' django-version: '4.2' experimental: true + - python-version: 'pypy-3.10' + django-version: '5.0' + experimental: true exclude: - # Exclude Python 3.11 for Django 3.2 and Django 4.0 - python-version: '3.11' django-version: '3.2' + - python-version: '3.12' + django-version: '3.2' + - python-version: '3.8' + django-version: '5.0' + - python-version: '3.9' + django-version: '5.0' + steps: diff --git a/pyproject.toml b/pyproject.toml index a22f66d..fbe0f7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,9 +10,9 @@ DJANGO_SETTINGS_MODULE = "tests.settings" legacy_tox_ini = """ [tox] envlist = - py{38,39,310,py38}-dj32 - py{38,39,310,311,py38}-dj41 - py{38,39,310,311,py38}-dj42 + py{38,39,310,py310}-dj32 + py{38,39,310,311,py310}-dj42 + py{310,311,py310}-dj50 py311-djmain py311-djqa @@ -22,7 +22,8 @@ python = 3.9: py39 3.10: py310 3.11: py311 - pypy-3.8: pypy38 + 3.12: py312 + pypy-3.10: pypy310 [gh-actions:env] DJANGO = @@ -37,8 +38,8 @@ DJANGO = deps = -r requirements-test.txt dj32: django>=3.2,<3.3 - dj41: django>=4.1,<4.2 dj42: django>=4.1,<4.2 + dj50: django>=5.0,<5.1 djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = true commands = pytest @@ -47,13 +48,13 @@ setenv = # Django development version is allowed to fail the test matrix ignore_outcome = djmain: True - pypy38: True + pypy310: True ignore_errors = djmain: True - pypy38: True + pypy310: True # QA runs type checks, linting, and code formatting checks -[testenv:py311-djqa] +[testenv:py312-djqa] deps = -r requirements-qa.txt commands = mypy axes diff --git a/setup.py b/setup.py index 9f9f45e..a4c2ebf 100644 --- a/setup.py +++ b/setup.py @@ -48,8 +48,8 @@ setup( "Environment :: Plugins", "Framework :: Django", "Framework :: Django :: 3.2", - "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", @@ -60,6 +60,7 @@ setup( "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: Log Analysis", From a069a42b509df93594e04f3cc748232109f60d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 4 Mar 2024 20:27:20 +0200 Subject: [PATCH 357/372] Drop pkg_resources from tests --- requirements-qa.txt | 1 - tests/test_logging.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index b29973d..77f36e8 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,4 +1,3 @@ black==24.2.0 mypy==1.8.0 prospector==1.10.3 -types-pkg_resources # Type stub diff --git a/tests/test_logging.py b/tests/test_logging.py index bbb6b3e..d7d1331 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -2,14 +2,14 @@ from unittest.mock import patch from django.test import override_settings from django.urls import reverse -from pkg_resources import get_distribution +from axes import __version__ from axes.apps import AppConfig from axes.models import AccessAttempt, AccessLog from tests.base import AxesTestCase _BEGIN = "AXES: BEGIN version %s, %s" -_VERSION = get_distribution("django-axes").version +_VERSION = __version__ @patch("axes.apps.AppConfig.initialized", False) From 6c54cf4e297c106bf8d3631f12ae83f0d70b8a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Mon, 4 Mar 2024 20:36:17 +0200 Subject: [PATCH 358/372] Version 6.4.0 --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fd96d7f..5c8cdfe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,13 @@ Changes ======= +6.4.0 (2024-03-04) +------------------ + +- Add support for Python 3.12 and Django 5.0, drop support for Django 4.1. + [aleksihakli] + + 6.3.1 (2024-03-04) ------------------ From 61ce115eeae9d58dbad21e04312175356006b56c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:22:13 +0000 Subject: [PATCH 359/372] chore(deps): bump tox from 4.13.0 to 4.14.1 Bumps [tox](https://github.com/tox-dev/tox) from 4.13.0 to 4.14.1. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.13.0...4.14.1) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index acc2271..d232816 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.13.0 +tox==4.14.1 From d44631188cb4d8752c89d84b234a7a286711a2d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:29:58 +0000 Subject: [PATCH 360/372] chore(deps): bump pytest-subtests from 0.11.0 to 0.12.1 Bumps [pytest-subtests](https://github.com/pytest-dev/pytest-subtests) from 0.11.0 to 0.12.1. - [Changelog](https://github.com/pytest-dev/pytest-subtests/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-subtests/compare/v0.11.0...v0.12.1) --- updated-dependencies: - dependency-name: pytest-subtests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index b7c8027..765a0bb 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,4 +4,4 @@ coverage==7.4.3 pytest==8.1.0 pytest-cov==4.1.0 pytest-django==4.8.0 -pytest-subtests==0.11.0 +pytest-subtests==0.12.1 From 6fda722179db5c08616a142848e4c67667b55672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:28:04 +0000 Subject: [PATCH 361/372] chore(deps): bump mypy from 1.8.0 to 1.9.0 Bumps [mypy](https://github.com/python/mypy) from 1.8.0 to 1.9.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.8.0...1.9.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 77f36e8..6448294 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,3 +1,3 @@ black==24.2.0 -mypy==1.8.0 +mypy==1.9.0 prospector==1.10.3 From 33b76271b8f5f6d9d59c8247e254e4aa36a3c206 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:28:09 +0000 Subject: [PATCH 362/372] chore(deps): bump pytest from 8.1.0 to 8.1.1 Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.1.0 to 8.1.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.1.0...8.1.1) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 765a0bb..677b78d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.4.3 -pytest==8.1.0 +pytest==8.1.1 pytest-cov==4.1.0 pytest-django==4.8.0 pytest-subtests==0.12.1 From dba7d4ef6c5cc6b9e8ec93d5a920a0a67523b793 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:58:22 +0000 Subject: [PATCH 363/372] chore(deps): bump coverage from 7.4.3 to 7.4.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.4.3 to 7.4.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.4.3...7.4.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 677b78d..c9f5748 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -e . django-ipware>=3 -coverage==7.4.3 +coverage==7.4.4 pytest==8.1.1 pytest-cov==4.1.0 pytest-django==4.8.0 From 6e63f47a78a0c076a9d68895de05a033e9a9eabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20H=C3=A4kli?= Date: Sat, 16 Mar 2024 12:54:27 +0200 Subject: [PATCH 364/372] Revert "chore(deps): bump codecov/codecov-action from 3 to 4" This reverts commit bba4c8098936de64ca477666748bc56b0c01eab4. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebbe003..1247514 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,6 +79,6 @@ jobs: DJANGO: ${{ matrix.django-version }} - name: Upload coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v3 with: name: Python ${{ matrix.python-version }} From 462fa56c677fc83123e658ce891d047c22ef901b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 12:18:20 +0000 Subject: [PATCH 365/372] chore(deps): bump black from 24.2.0 to 24.3.0 Bumps [black](https://github.com/psf/black) from 24.2.0 to 24.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.2.0...24.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 6448294..7ca304f 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,3 +1,3 @@ -black==24.2.0 +black==24.3.0 mypy==1.9.0 prospector==1.10.3 From 69d394029bb11f7802efafcbc6ceaa770598f6a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:20:35 +0000 Subject: [PATCH 366/372] chore(deps): bump pytest-cov from 4.1.0 to 5.0.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 4.1.0 to 5.0.0. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...v5.0.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c9f5748..dc369c1 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,6 +2,6 @@ django-ipware>=3 coverage==7.4.4 pytest==8.1.1 -pytest-cov==4.1.0 +pytest-cov==5.0.0 pytest-django==4.8.0 pytest-subtests==0.12.1 From dabedb4d9f1108a311d66d6bde3c116fde103244 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:20:47 +0000 Subject: [PATCH 367/372] chore(deps): bump tox from 4.14.1 to 4.14.2 Bumps [tox](https://github.com/tox-dev/tox) from 4.14.1 to 4.14.2. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.14.1...4.14.2) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d232816..0dc2573 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.14.1 +tox==4.14.2 From 61c6dd9a08381ccfab12ffd703ec7d2f9fe10139 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Wed, 3 Apr 2024 11:00:45 +0200 Subject: [PATCH 368/372] Require `asgiref>=3.6.0` `django-axes` is making use of some functions added in `asgiref==3.6.0`, but Django 3.2 requires `asgiref>=3.3.2`. --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a4c2ebf..557a4a3 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,10 @@ setup( use_scm_version=True, setup_requires=["setuptools_scm"], python_requires=">=3.7", - install_requires=["django>=3.2"], + install_requires=[ + "django>=3.2", + "asgiref>=3.6.0", + ], extras_require={ "ipware": "django-ipware>=3", }, From 6d3cd021ac025dc2e845eb55d692a6b095b71449 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:04:42 +0000 Subject: [PATCH 369/372] chore(deps): bump black from 24.3.0 to 24.4.0 Bumps [black](https://github.com/psf/black) from 24.3.0 to 24.4.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.3.0...24.4.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 7ca304f..90e0384 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,3 +1,3 @@ -black==24.3.0 +black==24.4.0 mypy==1.9.0 prospector==1.10.3 From 78bf8ffaf6fd41d0f82529bb69ce0bda85b6a4f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:51:04 +0000 Subject: [PATCH 370/372] chore(deps): bump mypy from 1.9.0 to 1.10.0 Bumps [mypy](https://github.com/python/mypy) from 1.9.0 to 1.10.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-qa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-qa.txt b/requirements-qa.txt index 90e0384..fe3e240 100644 --- a/requirements-qa.txt +++ b/requirements-qa.txt @@ -1,3 +1,3 @@ black==24.4.0 -mypy==1.9.0 +mypy==1.10.0 prospector==1.10.3 From 480ccde8825b5981afb3633c16b8be6b78008e09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:16:54 +0000 Subject: [PATCH 371/372] chore(deps): bump tox from 4.14.2 to 4.15.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.14.2 to 4.15.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.14.2...4.15.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0dc2573..90b8248 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ -r requirements-qa.txt -r requirements-test.txt sphinx_rtd_theme==2.0.0 -tox==4.14.2 +tox==4.15.0 From a2755bf4c89be6de0f658f91a753e297bcd25889 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:16:58 +0000 Subject: [PATCH 372/372] chore(deps): bump pytest from 8.1.1 to 8.2.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.1.1 to 8.2.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.1.1...8.2.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index dc369c1..40c7f25 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -e . django-ipware>=3 coverage==7.4.4 -pytest==8.1.1 +pytest==8.2.0 pytest-cov==5.0.0 pytest-django==4.8.0 pytest-subtests==0.12.1