mirror of
https://github.com/jazzband/django-axes.git
synced 2026-03-16 22:30:23 +00:00
parent
521b6adb97
commit
512452e580
7 changed files with 75 additions and 30 deletions
|
|
@ -4,18 +4,16 @@ from hashlib import md5
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.utils import timezone
|
||||
|
||||
from ipware.ip2 import get_client_ip
|
||||
|
||||
from axes.conf import settings
|
||||
from axes.models import AccessAttempt
|
||||
from axes.utils import get_axes_cache
|
||||
from axes.utils import get_axes_cache, get_client_ip
|
||||
|
||||
|
||||
def _query_user_attempts(request):
|
||||
"""Returns access attempt record if it exists.
|
||||
Otherwise return None.
|
||||
"""
|
||||
ip, _ = get_client_ip(request)
|
||||
ip = get_client_ip(request)
|
||||
username = request.POST.get(settings.AXES_USERNAME_FORM_FIELD, None)
|
||||
|
||||
if settings.AXES_ONLY_USER_FAILURES:
|
||||
|
|
@ -60,7 +58,7 @@ def get_cache_key(request_or_obj):
|
|||
un = request_or_obj.username
|
||||
ua = request_or_obj.user_agent
|
||||
else:
|
||||
ip, _ = get_client_ip(request_or_obj)
|
||||
ip = get_client_ip(request_or_obj)
|
||||
un = request_or_obj.POST.get(settings.AXES_USERNAME_FORM_FIELD, None)
|
||||
ua = request_or_obj.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
|
||||
|
|
@ -176,7 +174,7 @@ def is_user_lockable(request):
|
|||
|
||||
|
||||
def is_already_locked(request):
|
||||
ip, _ = get_client_ip(request)
|
||||
ip = get_client_ip(request)
|
||||
|
||||
if (
|
||||
settings.AXES_ONLY_USER_FAILURES or
|
||||
|
|
|
|||
20
axes/conf.py
20
axes/conf.py
|
|
@ -53,3 +53,23 @@ class MyAppConf(AppConf):
|
|||
|
||||
# message to show when locked out and have cooloff disabled
|
||||
PERMALOCK_MESSAGE = 'Account locked: too many login attempts. Contact an admin to unlock your account.'
|
||||
|
||||
# if your deployment is using reverse proxies, set this value to 'left-most' or 'right-most' per your configuration
|
||||
PROXY_ORDER = 'left-most'
|
||||
|
||||
# if your deployment is using reverse proxies, set this value to the number of proxies in front of Django
|
||||
PROXY_COUNT = None
|
||||
|
||||
# if your deployment is using reverse proxies, set to your trusted proxy IP addresses prefixes if needed
|
||||
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
|
||||
META_PRECEDENCE_ORDER = getattr(
|
||||
settings, 'AXES_META_PRECEDENCE_ORDER', getattr(
|
||||
settings, 'IPWARE_META_PRECEDENCE_ORDER', (
|
||||
'REMOTE_ADDR',
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ from django.dispatch import receiver
|
|||
from django.dispatch import Signal
|
||||
from django.utils import timezone
|
||||
|
||||
from ipware.ip import get_ip
|
||||
|
||||
from axes.conf import settings
|
||||
from axes.attempts import get_cache_key
|
||||
from axes.attempts import get_cache_timeout
|
||||
|
|
@ -19,7 +17,7 @@ from axes.attempts import ip_in_whitelist
|
|||
from axes.models import AccessLog, AccessAttempt
|
||||
from axes.utils import get_client_str
|
||||
from axes.utils import query2str
|
||||
from axes.utils import get_axes_cache
|
||||
from axes.utils import get_axes_cache, get_client_ip
|
||||
|
||||
|
||||
log = logging.getLogger(settings.AXES_LOGGER)
|
||||
|
|
@ -36,7 +34,7 @@ def log_user_login_failed(sender, credentials, request, **kwargs):
|
|||
log.error('Attempt to authenticate with a custom backend failed.')
|
||||
return
|
||||
|
||||
ip_address = get_ip(request)
|
||||
ip_address = get_client_ip(request)
|
||||
username = credentials[settings.AXES_USERNAME_FORM_FIELD]
|
||||
user_agent = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
path_info = request.META.get('PATH_INFO', '<unknown>')[:255]
|
||||
|
|
@ -128,7 +126,7 @@ def log_user_logged_in(sender, request, user, **kwargs):
|
|||
""" When a user logs in, update the access log
|
||||
"""
|
||||
username = user.get_username()
|
||||
ip_address = get_ip(request)
|
||||
ip_address = get_client_ip(request)
|
||||
user_agent = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
path_info = request.META.get('PATH_INFO', '<unknown>')[:255]
|
||||
http_accept = request.META.get('HTTP_ACCEPT', '<unknown>')[:1025]
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ class AccessAttemptTest(TestCase):
|
|||
# Make a login attempt again
|
||||
self.test_valid_login()
|
||||
|
||||
@patch('ipware.ip.get_ip', return_value='127.0.0.1')
|
||||
@patch('axes.utils.get_client_ip', return_value='127.0.0.1')
|
||||
def test_get_cache_key(self, get_ip_mock):
|
||||
""" Test the cache key format"""
|
||||
# Getting cache key from request
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from socket import inet_pton, AF_INET6, error
|
|||
from django.core.cache import cache, caches
|
||||
from django.utils import six
|
||||
|
||||
import ipware.ip2
|
||||
|
||||
from axes.conf import settings
|
||||
from axes.models import AccessAttempt
|
||||
|
||||
|
|
@ -49,6 +51,21 @@ def get_client_str(username, ip_address, user_agent=None, path_info=None):
|
|||
return client
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
client_ip_attribute = 'axes_client_ip'
|
||||
|
||||
if not hasattr(request, client_ip_attribute):
|
||||
client_ip, _ = ipware.ip2.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,
|
||||
)
|
||||
setattr(request, client_ip_attribute, client_ip)
|
||||
return getattr(request, client_ip_attribute)
|
||||
|
||||
|
||||
def is_ipv6(ip):
|
||||
try:
|
||||
inet_pton(AF_INET6, ip)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
Add `axes` to your ``INSTALLED_APPS``::
|
||||
Add ``axes`` to your ``INSTALLED_APPS``::
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.admin',
|
||||
|
|
@ -27,25 +27,15 @@ Add ``axes.backends.AxesModelBackend`` to the top of ``AUTHENTICATION_BACKENDS``
|
|||
|
||||
Run ``python manage.py migrate`` to sync the database.
|
||||
|
||||
Configure `django-ipware <https://github.com/un33k/django-ipware/>`_ to your liking. Pay close attention to the `IPWARE_META_PRECEDENCE_ORDER <https://github.com/un33k/django-ipware#precedence-order>`_ setting. Please note that this configuration is required for functional security in your project. A good starting point for a project running without a reverse proxy could be::
|
||||
|
||||
IPWARE_META_PRECEDENCE_ORDER = (
|
||||
'REMOTE_ADDR',
|
||||
)
|
||||
|
||||
Things to you might need to change in your code, especially if you get a ``AxesModelBackend.RequestParameterRequired``:
|
||||
|
||||
- make sure any calls to ``django.contrib.auth.authenticate`` pass the request.
|
||||
|
||||
- make sure any auth libraries you use that call the authentication middleware stack pass request. Notably Django Rest
|
||||
Framework (DRF) ``BasicAuthentication`` does not pass request. `Here is an example workaround for DRF`_.
|
||||
|
||||
.. _Here is an example workaround for DRF: https://gist.github.com/markddavidoff/7e442b1ea2a2e68d390e76731c35afe7
|
||||
|
||||
|
||||
Known configuration problems
|
||||
----------------------------
|
||||
|
||||
Axes has a few configuration issues with external packages and specific cache backends
|
||||
due to their internal implementations.
|
||||
|
||||
Cache problems
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
If you are running Axes on a deployment with in-memory Django cache,
|
||||
the ``axes_reset`` functionality might not work predictably.
|
||||
|
||||
|
|
@ -80,6 +70,28 @@ to your ``settings.py`` file::
|
|||
There are no known problems in other cache backends such as
|
||||
``DummyCache``, ``FileBasedCache``, or ``MemcachedCache`` backends.
|
||||
|
||||
Authentication backend problems
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you get ``AxesModelBackend.RequestParameterRequired`` exceptions,
|
||||
make sure any auth libraries and middleware you use pass the request object to authenticate.
|
||||
Notably Django Rest Framework (DRF) ``BasicAuthentication`` does not pass request.
|
||||
`Here is an example workaround for DRF <https://gist.github.com/markddavidoff/7e442b1ea2a2e68d390e76731c35afe7>`_.
|
||||
|
||||
Reverse proxy configuration
|
||||
---------------------------
|
||||
|
||||
Django Axes makes use of ``django-ipware`` package to detect the IP address of the client
|
||||
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
|
||||
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', )``
|
||||
|
||||
Customizing Axes
|
||||
----------------
|
||||
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -23,7 +23,7 @@ setup(
|
|||
install_requires=[
|
||||
'pytz',
|
||||
'django-appconf',
|
||||
'django-ipware',
|
||||
'django-ipware>=2.0.2',
|
||||
'win_inet_pton ; python_version < "3.4" and sys_platform == "win32"'
|
||||
],
|
||||
include_package_data=True,
|
||||
|
|
|
|||
Loading…
Reference in a new issue