From b49e685603064b75b01d32c435938ebf4185c3dc Mon Sep 17 00:00:00 2001 From: Yi Ming Yung Date: Fri, 4 Nov 2016 14:09:48 +0100 Subject: [PATCH] Added settings for disabling success accesslogs and added complete disabling of accesslogs --- axes/decorators.py | 19 ++++++++--------- axes/settings.py | 2 ++ axes/tests.py | 46 +++++++++++++++++++++++++++++++++++++++--- docs/configuration.rst | 3 ++- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/axes/decorators.py b/axes/decorators.py index 3e69716..5f8a4a0 100644 --- a/axes/decorators.py +++ b/axes/decorators.py @@ -221,15 +221,16 @@ def watch_login(func): user_agent = request.META.get('HTTP_USER_AGENT', '')[:255] http_accept = request.META.get('HTTP_ACCEPT', '')[:1025] path_info = request.META.get('PATH_INFO', '')[:255] - if login_unsuccessful or not DISABLE_ACCESS_LOG: - AccessLog.objects.create( - user_agent=user_agent, - ip_address=get_ip(request), - username=request.POST.get(USERNAME_FORM_FIELD, None), - http_accept=http_accept, - path_info=path_info, - trusted=not login_unsuccessful, - ) + if not DISABLE_ACCESS_LOG: + if login_unsuccessful or not DISABLE_SUCCESS_ACCESS_LOG: + AccessLog.objects.create( + user_agent=user_agent, + ip_address=get_ip(request), + username=request.POST.get(USERNAME_FORM_FIELD, None), + http_accept=http_accept, + path_info=path_info, + trusted=not login_unsuccessful, + ) if check_request(request, login_unsuccessful): return response diff --git a/axes/settings.py b/axes/settings.py index 9e0beb4..9b9ab92 100644 --- a/axes/settings.py +++ b/axes/settings.py @@ -36,6 +36,8 @@ if (isinstance(COOLOFF_TIME, int) or isinstance(COOLOFF_TIME, float)): DISABLE_ACCESS_LOG = getattr(settings, 'AXES_DISABLE_ACCESS_LOG', False) +DISABLE_SUCCESS_ACCESS_LOG = getattr(settings, 'AXES_DISABLE_SUCCESS_ACCESS_LOG', False) + LOGGER = getattr(settings, 'AXES_LOGGER', 'axes.watch_login') LOCKOUT_TEMPLATE = getattr(settings, 'AXES_LOCKOUT_TEMPLATE', None) diff --git a/axes/tests.py b/axes/tests.py index 6fabb34..7bd60c7 100644 --- a/axes/tests.py +++ b/axes/tests.py @@ -289,6 +289,42 @@ class AccessAttemptTest(TestCase): self.assertEquals(response.status_code, 403) self.assertEquals(response.get('Content-Type'), 'application/json') + @patch('axes.decorators.DISABLE_SUCCESS_ACCESS_LOG', True) + def test_valid_logout_without_success_log(self): + AccessLog.objects.all().delete() + + response = self._login(is_valid_username=True, is_valid_password=True) + response = self.client.get(reverse('admin:logout')) + + self.assertEquals(AccessLog.objects.all().count(), 0) + self.assertContains(response, 'Logged out') + + @patch('axes.decorators.DISABLE_SUCCESS_ACCESS_LOG', True) + def test_non_valid_login_without_success_log(self): + """ + A non-valid login does generate an AccessLog when + `DISABLE_SUCCESS_ACCESS_LOG=True`. + """ + AccessLog.objects.all().delete() + + response = self._login(is_valid_username=True, is_valid_password=False) + self.assertEquals(response.status_code, 200) + + self.assertEquals(AccessLog.objects.all().count(), 1) + + @patch('axes.decorators.DISABLE_SUCCESS_ACCESS_LOG', True) + def test_valid_login_without_success_log(self): + """ + A valid login doesn't generate an AccessLog when + `DISABLE_SUCCESS_ACCESS_LOG=True`. + """ + AccessLog.objects.all().delete() + + response = self._login(is_valid_username=True, is_valid_password=True) + + self.assertEqual(response.status_code, 302) + self.assertEqual(AccessLog.objects.all().count(), 0) + @patch('axes.decorators.DISABLE_ACCESS_LOG', True) def test_valid_logout_without_log(self): AccessLog.objects.all().delete() @@ -301,18 +337,22 @@ class AccessAttemptTest(TestCase): @patch('axes.decorators.DISABLE_ACCESS_LOG', True) def test_non_valid_login_without_log(self): + """ + A non-valid login does generate an AccessLog when + `DISABLE_ACCESS_LOG=True`. + """ AccessLog.objects.all().delete() response = self._login(is_valid_username=True, is_valid_password=False) self.assertEquals(response.status_code, 200) - self.assertEquals(AccessLog.objects.all().count(), 1) + self.assertEquals(AccessLog.objects.all().count(), 0) @patch('axes.decorators.DISABLE_ACCESS_LOG', True) def test_valid_login_without_log(self): """ - A valid login doesn't generate an access attempt when - `AXES_DISABLE_ACCESS_LOG=True`. + A valid login doesn't generate an AccessLog when + `DISABLE_ACCESS_LOG=True`. """ AccessLog.objects.all().delete() diff --git a/docs/configuration.rst b/docs/configuration.rst index 9d17b0f..7b77eac 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -63,4 +63,5 @@ These should be defined in your ``settings.py`` file. Default: ``False`` * ``AXES_REVERSE_PROXY_HEADER``: If ``AXES_BEHIND_REVERSE_PROXY`` is ``True``, it will look for the IP address from this header. Default: ``HTTP_X_FORWARDED_FOR`` -* ``AXES_DISABLE_ACCESS_LOG``: If ``True``, successful logins will not be logged, so the access log shown in the admin interface will only list unsuccessful login attempts. +* ``AXES_DISABLE_ACCESS_LOG``: If ``True``, disable all access logging, so the admin interface will be empty. +* ``AXES_DISABLE_SUCCESS_ACCESS_LOG``: If ``True``, successful logins will not be logged, so the access log shown in the admin interface will only list unsuccessful login attempts.