Codebase list django-axes / 3c78ae7
New upstream version 4.4.0 James Valleroy 5 years ago
10 changed file(s) with 85 addition(s) and 7 deletion(s). Raw diff Collapse all Expand all
00 Changes
11 =======
2
3 4.4.0 (2018-05-26)
4 ------------------
5
6 - Added AXES_USERNAME_CALLABLE
7 [jaadus]
8
29
310 4.3.1 (2018-04-21)
411 ------------------
00 from __future__ import unicode_literals
11
2 __version__ = '4.3.1'
2 __version__ = '4.4.0'
33
44 default_app_config = 'axes.apps.AppConfig'
55
5757 def has_add_permission(self, request):
5858 return False
5959
60
6061 admin.site.register(AccessAttempt, AccessAttemptAdmin)
6162
6263
108109 def has_add_permission(self, request):
109110 return False
110111
112
111113 admin.site.register(AccessLog, AccessLogAdmin)
77
88 from axes.conf import settings
99 from axes.models import AccessAttempt
10 from axes.utils import get_axes_cache, get_client_ip
10 from axes.utils import get_axes_cache, get_client_ip, get_client_username
1111
1212
1313 def _query_user_attempts(request):
1515 Otherwise return None.
1616 """
1717 ip = get_client_ip(request)
18 username = request.POST.get(settings.AXES_USERNAME_FORM_FIELD, None)
18 username = get_client_username(request)
1919
2020 if settings.AXES_ONLY_USER_FAILURES:
2121 attempts = AccessAttempt.objects.filter(username=username)
157157 try:
158158 field = getattr(get_user_model(), 'USERNAME_FIELD', 'username')
159159 kwargs = {
160 field: request.POST.get(settings.AXES_USERNAME_FORM_FIELD)
160 field: get_client_username(request)
161161 }
162162 user = get_user_model().objects.get(**kwargs)
163163
1818
1919 # use a specific password field to retrieve from login POST data
2020 PASSWORD_FORM_FIELD = 'password'
21
22 # use a provided callable to transform the POSTed username into the one used in credentials
23 USERNAME_CALLABLE = None
2124
2225 # only check user name and not location or user_agent
2326 ONLY_USER_FAILURES = False
1111 from axes import get_version
1212 from axes.conf import settings
1313 from axes.attempts import is_already_locked
14 from axes.utils import iso8601, get_lockout_message
14 from axes.utils import iso8601, get_client_username, get_lockout_message
1515
1616 log = logging.getLogger(settings.AXES_LOGGER)
1717 if settings.AXES_VERBOSE:
4949 def lockout_response(request):
5050 context = {
5151 'failure_limit': settings.AXES_FAILURE_LIMIT,
52 'username': request.POST.get(settings.AXES_USERNAME_FORM_FIELD, '')
52 'username': get_client_username(request) or ''
5353 }
5454
5555 cool_off = settings.AXES_COOLOFF_TIME
11
22 import datetime
33
4 from django.http import HttpRequest
45 from django.test import TestCase, override_settings
56 from django.utils import six
67
7 from axes.utils import iso8601, is_ipv6, get_client_str
8 from axes.utils import iso8601, is_ipv6, get_client_str, get_client_username
89
910
1011 class UtilsTest(TestCase):
142143 actual = get_client_str(username, ip, user_agent, path_info)
143144
144145 self.assertEqual(expected, actual)
146
147 @override_settings(AXES_USERNAME_FORM_FIELD='username')
148 def test_default_get_client_username(self):
149 expected = 'test-username'
150
151 request = HttpRequest()
152 request.POST['username'] = expected
153
154 actual = get_client_username(request)
155
156 self.assertEqual(expected, actual)
157
158 def sample_customize_username(request):
159 return 'prefixed-' + request.POST.get('username')
160
161 @override_settings(AXES_USERNAME_FORM_FIELD='username')
162 @override_settings(AXES_USERNAME_CALLABLE=sample_customize_username)
163 def test_custom_get_client_username(self):
164 provided = 'test-username'
165 expected = 'prefixed-' + provided
166
167 request = HttpRequest()
168 request.POST['username'] = provided
169
170 actual = get_client_username(request)
171
172 self.assertEqual(expected, actual)
6868 return getattr(request, client_ip_attribute)
6969
7070
71 def get_client_username(request):
72 if settings.AXES_USERNAME_CALLABLE:
73 return settings.AXES_USERNAME_CALLABLE(request)
74 return request.POST.get(settings.AXES_USERNAME_FORM_FIELD, None)
75
76
7177 def is_ipv6(ip):
7278 try:
7379 inet_pton(AF_INET6, ip)
123123 Default: ``True``
124124 * ``AXES_USERNAME_FORM_FIELD``: the name of the form field that contains your
125125 users usernames. Default: ``username``
126 * ``AXES_USERNAME_CALLABLE``: A callable function that takes a request object
127 and returns the username. If empty, axes just fetches it from the POST body
128 based on ``AXES_USERNAME_FORM_FIELD``. Default: ``None``
126129 * ``AXES_PASSWORD_FORM_FIELD``: the name of the form field that contains your
127130 users password. Default: ``password``
128131 * ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``: If ``True`` prevents the login
176176 url(r'^accounts/', include('allauth.urls')),
177177 # ...
178178 ]
179
180 Altering username before login
181 ------------------------------
182
183 In special cases, you may have the need to modify the username that is
184 submitted before attempting to authenticate. For example, adding namespacing or
185 removing client-set prefixes. In these cases, ``axes`` needs to know how to make
186 these changes so that it can correctly identify the user without any form
187 cleaning or validation. This is where the ``AXES_USERNAME_CALLABLE`` setting
188 comes in. You can define how to make these modifications in a callable that
189 takes a request object, and provide that callable to ``axes`` via this setting.
190
191 For example, a function like this could take a post body with something like
192 ``username='prefixed-username'`` and ``namespace=my_namespace`` and turn it
193 into ``my_namespace-username``:
194
195 *settings.py:* ::
196
197 def sample_username_modifier(request):
198 provided_username = request.POST.get('username')
199 some_namespace = request.POST.get('namespace')
200 return '-'.join([some_namespace, provided_username[9:]])
201
202 AXES_USERNAME_CALLABLE = sample_username_modifier
203
204 NOTE: You still have to make these modifications yourself before calling
205 authenticate. If you want to re-use the same function for consistency, that's
206 fine, but ``axes`` doesn't inject these changes into the authentication flow
207 for you.