mirror of
https://github.com/jazzband/django-axes.git
synced 2026-03-16 22:30:23 +00:00
Fix and add tests for IPv4 and IPv6 parsing
This patch does not fix IPv6 parsing with ports
This commit is contained in:
parent
ef3d527bee
commit
41877cdecd
8 changed files with 133 additions and 15 deletions
|
|
@ -9,7 +9,9 @@ env:
|
|||
install:
|
||||
- pip install -q $DJANGO coveralls
|
||||
script:
|
||||
- coverage run --source=axes runtests.py
|
||||
- coverage run -a --source=axes runtests.py
|
||||
- coverage run -a --source=axes runtests_proxy.py
|
||||
- coverage run -a --source=axes runtests_proxy_custom_header.py
|
||||
- coverage report
|
||||
after_success:
|
||||
- coveralls
|
||||
|
|
|
|||
|
|
@ -37,15 +37,18 @@ def is_ipv6(ip):
|
|||
|
||||
|
||||
def get_ip(request):
|
||||
ip = request.META.get('REMOTE_ADDR', '')
|
||||
"""Parse IP address from REMOTE_ADDR or
|
||||
AXES_REVERSE_PROXY_HEADER if AXES_BEHIND_REVERSE_PROXY is set."""
|
||||
|
||||
if BEHIND_REVERSE_PROXY:
|
||||
ip = request.META.get(REVERSE_PROXY_HEADER, '').split(',', 1)[0]
|
||||
ip = ip.strip()
|
||||
# For requests originating from behind a reverse proxy,
|
||||
# resolve the IP address from the given AXES_REVERSE_PROXY_HEADER.
|
||||
# AXES_REVERSE_PROXY_HEADER defaults to HTTP_X_FORWARDED_FOR if not given,
|
||||
# which is the Django calling format for the HTTP X-Forwarder-For header.
|
||||
# Please see RFC7239 for additional information:
|
||||
# https://tools.ietf.org/html/rfc7239#section-5
|
||||
|
||||
# IIS seems to add the port number to HTTP_X_FORWARDED_FOR
|
||||
if ':' in ip:
|
||||
ip = ip.split(':')[0]
|
||||
ip = request.META.get(REVERSE_PROXY_HEADER, '')
|
||||
|
||||
if not ip:
|
||||
raise Warning(
|
||||
|
|
@ -54,11 +57,21 @@ def get_ip(request):
|
|||
'server settings to make sure this header value is being '
|
||||
'passed. Header value {0}'.format(REVERSE_PROXY_HEADER)
|
||||
)
|
||||
if not is_ipv6(ip):
|
||||
# Fix for IIS adding client port number to 'HTTP_X_FORWARDED_FOR' header (removes port number).
|
||||
ip = ''.join(ip.split(':')[:-1])
|
||||
|
||||
return ip
|
||||
# X-Forwarded-For IPs can have multiple IPs of which the first one is the
|
||||
# originating reverse and the rest are proxies that are between the client
|
||||
ip = ip.split(',', 1)[0]
|
||||
|
||||
# As spaces are permitted between given X-Forwarded-For IP addresses, strip them as well
|
||||
ip = ip.strip()
|
||||
|
||||
# Fix IIS adding client port number to 'X-Forwarded-For' header (strip port)
|
||||
if not is_ipv6(ip):
|
||||
ip = ip.split(':', 1)[0]
|
||||
|
||||
return ip
|
||||
|
||||
return request.META.get('REMOTE_ADDR', '')
|
||||
|
||||
|
||||
def query2str(items, max_length=1024):
|
||||
|
|
|
|||
3
axes/test_settings_proxy.py
Normal file
3
axes/test_settings_proxy.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .test_settings import *
|
||||
|
||||
AXES_BEHIND_REVERSE_PROXY = True
|
||||
3
axes/test_settings_proxy_custom_header.py
Normal file
3
axes/test_settings_proxy_custom_header.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .test_settings_proxy import *
|
||||
|
||||
AXES_REVERSE_PROXY_HEADER = 'HTTP_X_AXES_CUSTOM_HEADER'
|
||||
|
|
@ -4,14 +4,17 @@ import time
|
|||
import json
|
||||
import datetime
|
||||
|
||||
from mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import NoReverseMatch
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import six
|
||||
from mock import patch
|
||||
|
||||
from axes.decorators import get_ip
|
||||
from axes.settings import COOLOFF_TIME
|
||||
from axes.settings import FAILURE_LIMIT
|
||||
from axes.models import AccessAttempt, AccessLog
|
||||
|
|
@ -19,6 +22,11 @@ from axes.signals import user_locked_out
|
|||
from axes.utils import reset, iso8601
|
||||
|
||||
|
||||
class MockRequest:
|
||||
def __init__(self):
|
||||
self.META = dict()
|
||||
|
||||
|
||||
class AccessAttemptTest(TestCase):
|
||||
"""Test case using custom settings for testing
|
||||
"""
|
||||
|
|
@ -392,3 +400,67 @@ class UtilsTest(TestCase):
|
|||
self.assertTrue(is_ipv6('ff80::220:16ff:fec9:1'))
|
||||
self.assertFalse(is_ipv6('67.255.125.204'))
|
||||
self.assertFalse(is_ipv6('foo'))
|
||||
|
||||
|
||||
class GetIPProxyTest(TestCase):
|
||||
"""Test get_ip returns correct addresses with proxy
|
||||
"""
|
||||
def setUp(self):
|
||||
self.request = MockRequest()
|
||||
|
||||
def test_iis_ipv4_port_stripping(self):
|
||||
self.ip = '192.168.1.1'
|
||||
|
||||
valid_headers = [
|
||||
'192.168.1.1:6112',
|
||||
'192.168.1.1:6033, 192.168.1.2:9001',
|
||||
]
|
||||
|
||||
for header in valid_headers:
|
||||
self.request.META['HTTP_X_FORWARDED_FOR'] = header
|
||||
self.assertEqual(self.ip, get_ip(self.request))
|
||||
|
||||
def test_valid_ipv4_parsing(self):
|
||||
self.ip = '192.168.1.1'
|
||||
|
||||
valid_headers = [
|
||||
'192.168.1.1',
|
||||
'192.168.1.1, 192.168.1.2',
|
||||
' 192.168.1.1 , 192.168.1.2 ',
|
||||
' 192.168.1.1 , 2001:db8:cafe::17 ',
|
||||
]
|
||||
|
||||
for header in valid_headers:
|
||||
self.request.META['HTTP_X_FORWARDED_FOR'] = header
|
||||
self.assertEqual(self.ip, get_ip(self.request))
|
||||
|
||||
def test_valid_ipv6_parsing(self):
|
||||
self.ip = '2001:db8:cafe::17'
|
||||
|
||||
valid_headers = [
|
||||
'2001:db8:cafe::17',
|
||||
'2001:db8:cafe::17 , 2001:db8:cafe::18',
|
||||
'2001:db8:cafe::17, 2001:db8:cafe::18, 192.168.1.1',
|
||||
]
|
||||
|
||||
for header in valid_headers:
|
||||
self.request.META['HTTP_X_FORWARDED_FOR'] = header
|
||||
self.assertEqual(self.ip, get_ip(self.request))
|
||||
|
||||
|
||||
class GetIPProxyCustomHeaderTest(TestCase):
|
||||
"""Test that get_ip returns correct addresses with a custom proxy header
|
||||
"""
|
||||
def setUp(self):
|
||||
self.request = MockRequest()
|
||||
|
||||
def test_custom_header_parsing(self):
|
||||
self.ip = '2001:db8:cafe::17'
|
||||
|
||||
valid_headers = [
|
||||
' 2001:db8:cafe::17 , 2001:db8:cafe::18',
|
||||
]
|
||||
|
||||
for header in valid_headers:
|
||||
self.request.META[settings.AXES_REVERSE_PROXY_HEADER] = header
|
||||
self.assertEqual(self.ip, get_ip(self.request))
|
||||
|
|
|
|||
15
runtests.py
15
runtests.py
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
|
@ -6,10 +7,18 @@ import django
|
|||
from django.conf import settings
|
||||
from django.test.utils import get_runner
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'axes.test_settings'
|
||||
|
||||
def run_tests(settings_module, *modules):
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
|
||||
django.setup()
|
||||
TestRunner = get_runner(settings)
|
||||
test_runner = TestRunner()
|
||||
failures = test_runner.run_tests(["axes"])
|
||||
failures = test_runner.run_tests(*modules)
|
||||
sys.exit(bool(failures))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests('axes.test_settings', [
|
||||
'axes.tests.AccessAttemptTest',
|
||||
'axes.tests.UtilsTest',
|
||||
])
|
||||
|
|
|
|||
8
runtests_proxy.py
Normal file
8
runtests_proxy.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from runtests import run_tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests('axes.test_settings_proxy', [
|
||||
'axes.tests.GetIPProxyTest',
|
||||
])
|
||||
8
runtests_proxy_custom_header.py
Normal file
8
runtests_proxy_custom_header.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from runtests import run_tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests('axes.test_settings_proxy_custom_header', [
|
||||
'axes.tests.GetIPProxyCustomHeaderTest',
|
||||
])
|
||||
Loading…
Reference in a new issue