Skip to content

Commit

Permalink
Added AXES_ONLY_ADMIN_SITE flag.
Browse files Browse the repository at this point in the history
  • Loading branch information
hramezani authored and aleksihakli committed Jul 9, 2019
1 parent 9fa0e7f commit 6f2048f
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 0 deletions.
3 changes: 3 additions & 0 deletions axes/conf.py
Expand Up @@ -20,6 +20,9 @@ class AxesAppConf(AppConf):
# lock out with username and never the IP or user agent
ONLY_USER_FAILURES = False

# lock out just for admin site
ONLY_ADMIN_SITE = False

# lock out with the user agent, has no effect when ONLY_USER_FAILURES is set
USE_USER_AGENT = False

Expand Down
19 changes: 19 additions & 0 deletions axes/handlers/base.py
@@ -1,3 +1,7 @@
import re

from django.urls import reverse

from axes.conf import settings
from axes.helpers import (
get_failure_limit,
Expand Down Expand Up @@ -34,6 +38,9 @@ def is_allowed(self, request, credentials: dict = None) -> bool:
and inspiration on some common checks and access restrictions before writing your own implementation.
"""

if self.is_admin_site(request):
return True

if self.is_blacklisted(request, credentials):
return False

Expand Down Expand Up @@ -112,3 +119,15 @@ def get_failures(self, request, credentials: dict = None) -> int:
"""

raise NotImplementedError('The Axes handler class needs a method definition for get_failures')

def is_admin_site(self, request) -> bool:
"""
Checks if the request is for admin site.
"""
if (
settings.AXES_ONLY_ADMIN_SITE and hasattr(request, 'path') and
not re.match('^%s' % reverse('admin:index'), request.path)
):
return True

return False
21 changes: 21 additions & 0 deletions axes/tests/test_handlers.py
@@ -1,6 +1,7 @@
from unittest.mock import MagicMock, patch

from django.test import override_settings
from django.urls import reverse
from django.utils.timezone import timedelta

from axes.conf import settings
Expand Down Expand Up @@ -35,6 +36,12 @@ def test_is_allowed_with_whitelisted_method(self):
def test_is_allowed_no_lock_out(self):
self.assertTrue(AxesProxyHandler.is_allowed(self.request))

@override_settings(AXES_ONLY_ADMIN_SITE=True)
def test_only_admin_site(self):
request = MagicMock()
request.path = '/test/'
self.assertTrue(AxesProxyHandler.is_allowed(self.request))


class AxesProxyHandlerTestCase(AxesTestCase):
def setUp(self):
Expand Down Expand Up @@ -96,6 +103,20 @@ def check_empty_request(self, log, handler):
AxesProxyHandler.user_login_failed(sender=None, credentials={}, request=None)
log.error.assert_called_with(f'AXES: {handler}.user_login_failed does not function without a request.')

def test_is_admin_site(self):
request = MagicMock()
tests = ( # (AXES_ONLY_ADMIN_SITE, URL, Expected)
(True, '/test/', True),
(True, reverse('admin:index'), False),
(False, '/test/', False),
(False, reverse('admin:index'), False),
)

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)


@override_settings(
AXES_HANDLER='axes.handlers.database.AxesDatabaseHandler',
Expand Down
2 changes: 2 additions & 0 deletions docs/4_configuration.rst
Expand Up @@ -38,6 +38,8 @@ The following ``settings.py`` options are available for customizing Axes behavio
and never lock based on IP if attempts exceed the limit.
Otherwise utilize the existing IP and user locking logic.
Default: ``False``
* ``AXES_ONLY_ADMIN_SITE`` : If ``True``, lock is only enable for admin site,
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
Expand Down

0 comments on commit 6f2048f

Please sign in to comment.