Merge tag 'upstream/3.0.3'
Upstream version 3.0.3
James Valleroy
6 years ago
1 | 1 | language: python |
2 | 2 | cache: pip |
3 | 3 | python: |
4 | - 3.6 | |
4 | - 2.7 | |
5 | - 3.4 | |
6 | - 3.5 | |
7 | - 3.6 | |
5 | 8 | install: pip install tox-travis |
6 | 9 | script: tox |
7 | 10 | after_success: |
9 | 12 | deploy: |
10 | 13 | provider: pypi |
11 | 14 | user: jazzband |
12 | distributions: "sdist bdist_wheel" | |
15 | server: https://jazzband.co/projects/django-axes/upload | |
16 | distributions: sdist bdist_wheel | |
13 | 17 | password: |
14 | secure: VD+63Tnv0VYNfFQv9f1KZ0k79HSX8veNk4dTy42Hriteci50z5uSQdZMnqqD83xQJa4VF6N7DHkxHnBVOWLCqGQZeYqR/5BuDFNUewcr6O14dk31HvxMsWDaN1KW0Qwtus8ZrztwGhZtZ/92ODA6luHI4mCTzqX0gcG0/aKd75s= | |
18 | secure: TCH5tGIggL2wsWce2svMwpEpPiwVOYqq1R3uSBTexszleP0OafNq/wZk2KZEReR5w1Aq68qp5F5Eeh2ZjJTq4f9M4LtTvqQzrmyNP55DYk/uB1rBJm9b4gBgMtAknxdI2g7unkhQEDo4suuPCVofM7rrDughySNpmvlUQYDttHQ= | |
15 | 19 | on: |
16 | 20 | tags: true |
17 | 21 | repo: jazzband/django-axes |
0 | 0 | Changes |
1 | 1 | ======= |
2 | ||
3 | 3.0.3 (2017-11-23) | |
4 | ------------------ | |
5 | ||
6 | - Test against Python 2.7. | |
7 | [mbaechtold] | |
8 | ||
9 | - Test against Python 3.4. | |
10 | [pope1ni] | |
11 | ||
12 | ||
13 | 3.0.2 (2017-11-21) | |
14 | ------------------ | |
15 | ||
16 | - Added form_invalid decorator. Fixes #265 | |
17 | [camilonova] | |
18 | ||
2 | 19 | |
3 | 20 | 3.0.1 (2017-11-17) |
4 | 21 | ------------------ |
7 | 7 | from django.contrib.auth.views import LoginView |
8 | 8 | from django.utils.decorators import method_decorator |
9 | 9 | |
10 | from axes.decorators import watch_login | |
10 | from axes import signals # we must load signals | |
11 | from axes.decorators import axes_dispatch | |
12 | from axes.decorators import axes_form_invalid | |
11 | 13 | |
12 | LoginView.dispatch = method_decorator(watch_login)(LoginView.dispatch) | |
14 | LoginView.dispatch = method_decorator(axes_dispatch)(LoginView.dispatch) | |
15 | LoginView.form_invalid = method_decorator(axes_form_invalid)(LoginView.form_invalid) |
0 | 0 | from datetime import timedelta |
1 | from functools import wraps | |
1 | 2 | import json |
2 | 3 | import logging |
3 | 4 | |
9 | 10 | from axes.conf import settings |
10 | 11 | from axes.attempts import is_already_locked |
11 | 12 | from axes.utils import iso8601 |
12 | from axes.signals import * # load all signals | |
13 | 13 | |
14 | 14 | |
15 | 15 | log = logging.getLogger(settings.AXES_LOGGER) |
37 | 37 | ) |
38 | 38 | |
39 | 39 | |
40 | def watch_login(func): | |
40 | def axes_dispatch(func): | |
41 | 41 | def inner(request, *args, **kwargs): |
42 | # If the request is currently under lockout, do not proceed to the | |
43 | # login function, go directly to lockout url, do not pass go, do not | |
44 | # collect messages about this login attempt | |
45 | 42 | if is_already_locked(request): |
46 | 43 | return lockout_response(request) |
47 | 44 | |
48 | # call the login function | |
49 | 45 | return func(request, *args, **kwargs) |
46 | ||
47 | return inner | |
48 | ||
49 | ||
50 | def axes_form_invalid(func): | |
51 | @wraps(func) | |
52 | def inner(self, *args, **kwargs): | |
53 | if is_already_locked(self.request): | |
54 | return lockout_response(self.request) | |
55 | ||
56 | return func(self, *args, **kwargs) | |
50 | 57 | |
51 | 58 | return inner |
52 | 59 |
39 | 39 | if settings.AXES_NEVER_LOCKOUT_WHITELIST and ip_in_whitelist(ip_address): |
40 | 40 | return |
41 | 41 | |
42 | failures = 1 | |
42 | failures = 0 | |
43 | 43 | attempts = get_user_attempts(request) |
44 | 44 | cache_hash_key = get_cache_key(request) |
45 | 45 | cache_timeout = get_cache_timeout() |
12 | 12 | |
13 | 13 | SITE_ID = 1 |
14 | 14 | |
15 | MIDDLEWARE_CLASSES = ( | |
15 | MIDDLEWARE = ( | |
16 | 16 | 'django.middleware.common.CommonMiddleware', |
17 | 17 | 'django.contrib.sessions.middleware.SessionMiddleware', |
18 | 18 | 'django.contrib.auth.middleware.AuthenticationMiddleware', |
27 | 27 | 'django.contrib.sites', |
28 | 28 | 'django.contrib.messages', |
29 | 29 | 'django.contrib.admin', |
30 | ||
31 | 'axes.apps.AppConfig', | |
30 | 'axes', | |
32 | 31 | ) |
33 | 32 | |
34 | 33 | TEMPLATES = [ |
74 | 73 | |
75 | 74 | LOGIN_REDIRECT_URL = '/admin/' |
76 | 75 | |
77 | AXES_LOGIN_FAILURE_LIMIT = 10 | |
76 | AXES_FAILURE_LIMIT = 10 |
0 | from unittest.mock import patch | |
1 | 0 | import datetime |
2 | 1 | import hashlib |
3 | 2 | import json |
14 | 13 | from axes.attempts import get_cache_key |
15 | 14 | from axes.models import AccessAttempt, AccessLog |
16 | 15 | from axes.signals import user_locked_out |
16 | from axes.tests.compatibility import patch | |
17 | 17 | from axes.utils import reset |
18 | 18 | |
19 | 19 |
131 | 131 | |
132 | 132 | if attempts: |
133 | 133 | count = attempts.count() |
134 | from axes.decorators import get_cache_key | |
134 | # import should be here to avoid circular dependency with get_ip | |
135 | from axes.attempts import get_cache_key | |
135 | 136 | for attempt in attempts: |
136 | 137 | cache_hash_key = get_cache_key(attempt) |
137 | 138 | if cache.get(cache_hash_key): |
24 | 24 | You have a couple options available to you to customize ``django-axes`` a bit. |
25 | 25 | These should be defined in your ``settings.py`` file. |
26 | 26 | |
27 | * ``AXES_LOGIN_FAILURE_LIMIT``: The number of login attempts allowed before a | |
27 | * ``AXES_FAILURE_LIMIT``: The number of login attempts allowed before a | |
28 | 28 | record is created for the failed logins. Default: ``3`` |
29 | 29 | * ``AXES_LOCK_OUT_AT_FAILURE``: After the number of allowed login attempts |
30 | 30 | are exceeded, should we lock out this IP (and optional user agent)? |
11 | 11 | -------- |
12 | 12 | |
13 | 13 | .. toctree:: |
14 | :maxdepth: 2 | |
14 | :maxdepth: 2 | |
15 | 15 | |
16 | 16 | installation |
17 | 17 | configuration |
18 | 18 | usage |
19 | 19 | requirements |
20 | 20 | development |
21 | issues | |
22 | 21 | captcha |
23 | 22 | |
24 | 23 | Indices and tables |
0 | .. _issues: | |
1 | ||
2 | Issues | |
3 | ====== | |
4 | ||
5 | Not being locked out after failed attempts | |
6 | ------------------------------------------ | |
7 | ||
8 | You may find that Axes is not capturing your failed login attempts. It may | |
9 | be that you need to manually add watch_login to your login url. | |
10 | ||
11 | For example, in your urls.py:: | |
12 | ||
13 | ... | |
14 | from my.custom.app import login | |
15 | from axes.decorators import watch_login | |
16 | ... | |
17 | urlpatterns = patterns('', | |
18 | (r'^login/$', watch_login(login)), | |
19 | ... | |
20 | ||
21 | ||
22 | Locked out without reason | |
23 | ------------------------- | |
24 | ||
25 | It may happen that you have suddenly become locked out without a single failed | |
26 | attempt. One possible reason is that you are using some custom login form and the | |
27 | username field is named something different than "username", e.g. "email". This | |
28 | leads to all users attempts being lumped together. To fix this add the following | |
29 | to your settings: | |
30 | ||
31 | AXES_USERNAME_FORM_FIELD = "email" | |
32 |
16 | 16 | author_email='codekoala@gmail.com', |
17 | 17 | maintainer='Alex Clark', |
18 | 18 | maintainer_email='aclark@aclark.net', |
19 | url='https://github.com/django-pci/django-axes', | |
19 | url='https://github.com/jazzband/django-axes', | |
20 | 20 | license='MIT', |
21 | 21 | package_dir={'axes': 'axes'}, |
22 | 22 | install_requires=['pytz', 'django-appconf'], |
26 | 26 | 'Development Status :: 5 - Production/Stable', |
27 | 27 | 'Environment :: Web Environment', |
28 | 28 | 'Framework :: Django', |
29 | 'Framework :: Django :: 1.11', | |
30 | 'Framework :: Django :: 2.0', | |
29 | 31 | 'Intended Audience :: Developers', |
30 | 32 | 'Intended Audience :: System Administrators', |
31 | 33 | 'License :: OSI Approved :: MIT License', |
32 | 34 | 'Operating System :: OS Independent', |
33 | 35 | 'Programming Language :: Python', |
36 | 'Programming Language :: Python :: 2', | |
37 | 'Programming Language :: Python :: 2.7', | |
34 | 38 | 'Programming Language :: Python :: 3', |
39 | 'Programming Language :: Python :: 3.4', | |
40 | 'Programming Language :: Python :: 3.5', | |
41 | 'Programming Language :: Python :: 3.6', | |
35 | 42 | 'Topic :: Internet :: Log Analysis', |
36 | 43 | 'Topic :: Security', |
37 | 44 | 'Topic :: System :: Logging', |
0 | 0 | [tox] |
1 | 1 | envlist = |
2 | py{36}-django-111 | |
3 | py{36}-django-20 | |
4 | py{36}-django-master | |
2 | py{27,34,35,36}-django-111 | |
3 | py{34,35,36}-django-20 | |
4 | py{35,36}-django-master | |
5 | 5 | |
6 | 6 | [testenv] |
7 | 7 | deps = |
8 | py27: mock | |
8 | 9 | django-appconf |
9 | 10 | coveralls |
10 | 11 | django-111: Django>=1.11,<2.0 |
12 | 13 | django-master: https://github.com/django/django/archive/master.tar.gz |
13 | 14 | usedevelop = True |
14 | 15 | ignore_outcome = |
15 | django-20: True | |
16 | 16 | django-master: True |
17 | 17 | commands = |
18 | 18 | coverage run -a --source=axes runtests.py -v2 |