From 86cb317b902018a50a7a11f40864915685ddcafd Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 17:46:28 -0500 Subject: [PATCH 01/27] use python-ipware --- .vscode/settings.json | 1 + CHANGELOG.md | 7 + README.md | 94 +++--------- ipware/__version__.py | 2 +- ipware/defaults.py | 78 ---------- ipware/ip.py | 64 +++----- ipware/tests/tests_common.py | 96 ------------ ipware/tests/tests_ip.py | 20 +++ ipware/tests/tests_ipv4.py | 286 ----------------------------------- ipware/tests/tests_ipv6.py | 193 ----------------------- ipware/utils.py | 125 --------------- setup.py | 2 +- 12 files changed, 71 insertions(+), 897 deletions(-) delete mode 100644 ipware/defaults.py delete mode 100644 ipware/tests/tests_common.py create mode 100644 ipware/tests/tests_ip.py delete mode 100644 ipware/tests/tests_ipv4.py delete mode 100644 ipware/tests/tests_ipv6.py delete mode 100644 ipware/utils.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 68e31e1..bc67384 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "python.linting.enabled": false, "cSpell.words": [ "cmdclass", + "Fastly", "getattr", "ipware", "multicast", diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f18492..06e023e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 6.0.0 + +Enhancement: (breaking changes) + +- Use python-ipware under the hood +- Minor behavior changes as python-ipware is more accurate + # 5.0.2 Enhancement: diff --git a/README.md b/README.md index 92fbe84..33241ab 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,12 @@ # Alternative package -If you prefer a python only version that does not integrate with Django, you can use the [python-ipware](https://github.com/un33k/python-ipware) package instead. `django-ipware` will use python-ipware in the future. +If you prefer a python only version that does not integrate with Django directly, but allows for more flexibility and advanced features, you can use the [python-ipware](https://github.com/un33k/python-ipware) package instead. `django-ipware` is now a wrapper using python-ipware underneath. # Overview **Best attempt** to get client's IP address while keeping it **DRY**. -# Alternative package - -If you prefer a python version, you can use the [python-ipware](https://github.com/un33k/python-ipware) package instead. `python-ipware` is a newer package, with more advanced features. While this a Django specific package, `python-ipware` can be used with Django, Flask, etc. - # Notice There is no perfect `out-of-the-box` solution against fake IP addresses, aka `IP Address Spoofing`. @@ -56,30 +52,39 @@ Please use ipware `ONLY` as a complement to your `firewall` security measures! # The client's IP address is publicly routable on the Internet else: # The client's IP address is private - - # Order of precedence is (Public, Private, Loopback, None) ``` # Advanced users: - ### Precedence Order - The default meta precedence order is top to bottom. However, you may customize the order + The default meta precedence order is top to bottom. You may customize the order by providing your own `IPWARE_META_PRECEDENCE_ORDER` by adding it to your project's settings.py ```python - # The default meta precedence order + # The default meta precedence order (update as needed) IPWARE_META_PRECEDENCE_ORDER = ( - 'HTTP_X_FORWARDED_FOR', 'X_FORWARDED_FOR', # , , - 'HTTP_CLIENT_IP', - 'HTTP_X_REAL_IP', - 'HTTP_X_FORWARDED', - 'HTTP_X_CLUSTER_CLIENT_IP', - 'HTTP_FORWARDED_FOR', - 'HTTP_FORWARDED', - 'HTTP_VIA', - 'REMOTE_ADDR', - ) + "X_FORWARDED_FOR", # AWS ELB (default client is `left-most` [`, , `]) + "HTTP_X_FORWARDED_FOR", # Similar to X_FORWARDED_TO + "HTTP_CLIENT_IP", # Standard headers used by providers such as Amazon EC2, Heroku etc. + "HTTP_X_REAL_IP", # Standard headers used by providers such as Amazon EC2, Heroku etc. + "HTTP_X_FORWARDED", # Squid and others + "HTTP_X_CLUSTER_CLIENT_IP", # Rackspace LB and Riverbed Stingray + "HTTP_FORWARDED_FOR", # RFC 7239 + "HTTP_FORWARDED", # RFC 7239 + "HTTP_VIA", # Squid and others + "X-CLIENT-IP", # Microsoft Azure + "X-REAL-IP", # NGINX + "X-CLUSTER-CLIENT-IP", # Rackspace Cloud Load Balancers + "X_FORWARDED", # Squid + "FORWARDED_FOR", # RFC 7239 + "CF-CONNECTING-IP", # CloudFlare + "TRUE-CLIENT-IP", # CloudFlare Enterprise, + "FASTLY-CLIENT-IP", # Firebase, Fastly + "FORWARDED", # RFC 7239 + "CLIENT-IP", # Akamai and Cloudflare: True-Client-IP and Fastly: Fastly-Client-IP + "REMOTE_ADDR", # Default + ) ``` **Alternatively**, you can provide your custom _request header meta precedence order_ when calling `get_client_ip()`. @@ -89,57 +94,6 @@ get_client_ip(request, request_header_order=['X_FORWARDED_FOR']) get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'HTTP_X_FORWARDED_FOR']) ``` -### Private Prefixes - -You may customize the prefixes to indicate an IP address is private. This is done by adding your -own `IPWARE_PRIVATE_IP_PREFIX` to your project's settings.py. IP addresses matching the following -prefixes are considered _private_ & are **not** publicly routable. - -```python -# The default private IP prefixes -IPWARE_PRIVATE_IP_PREFIX = getattr(settings, - 'IPWARE_PRIVATE_IP_PREFIX', ( - '0.', # messages to software - '10.', # class A private block - '100.64.', '100.65.', '100.66.', '100.67.', '100.68.', '100.69.', - '100.70.', '100.71.', '100.72.', '100.73.', '100.74.', '100.75.', - '100.76.', '100.77.', '100.78.', '100.79.', '100.80.', '100.81.', - '100.82.', '100.83.', '100.84.', '100.85.', '100.86.', '100.87.', - '100.88.', '100.89.', '100.90.', '100.91.', '100.92.', '100.93.', - '100.94.', '100.95.', '100.96.', '100.97.', '100.98.', '100.99.', - '100.100.', '100.101.', '100.102.', '100.103.', '100.104.', '100.105.', - '100.106.', '100.107.', '100.108.', '100.109.', '100.110.', '100.111.', - '100.112.', '100.113.', '100.114.', '100.115.', '100.116.', '100.117.', - '100.118.', '100.119.', '100.120.', '100.121.', '100.122.', '100.123.', - '100.124.', '100.125.', '100.126.', '100.127.', # carrier-grade NAT - '169.254.', # link-local block - '172.16.', '172.17.', '172.18.', '172.19.', - '172.20.', '172.21.', '172.22.', '172.23.', - '172.24.', '172.25.', '172.26.', '172.27.', - '172.28.', '172.29.', '172.30.', '172.31.', # class B private blocks - '192.0.0.', # reserved for IANA special purpose address registry - '192.0.2.', # reserved for documentation and example code - '192.168.', # class C private block - '198.18.', '198.19.', # reserved for inter-network communications between two separate subnets - '198.51.100.', # reserved for documentation and example code - '203.0.113.', # reserved for documentation and example code - '224.', '225.', '226.', '227.', '228.', '229.', '230.', '231.', '232.', - '233.', '234.', '235.', '236.', '237.', '238.', '239.', # multicast - '240.', '241.', '242.', '243.', '244.', '245.', '246.', '247.', '248.', - '249.', '250.', '251.', '252.', '253.', '254.', '255.', # reserved - '::', # Unspecified address - '::ffff:', '2001:10:', '2001:20:', # messages to software - '2001::', # TEREDO - '2001:2::', # benchmarking - '2001:db8:', # reserved for documentation and example code - 'fc', # IPv6 ULA (RFC4193) - NOTE: Reserved for future use, not currently in widespread use. - 'fd', # IPv6 ULA (RFC4193) - Mainly used for private network addressing - 'fe80:', # link-local unicast - 'ff00:', # IPv6 multicast - ) -) -``` - ### Trusted Proxies If your Django server is behind one or more known proxy server(s), you can filter out unwanted requests diff --git a/ipware/__version__.py b/ipware/__version__.py index a27dbf0..0a2cd3f 100644 --- a/ipware/__version__.py +++ b/ipware/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/django-ipware' __license__ = 'MIT' __copyright__ = 'Copyright 2020 Val Neekman @ Neekware Inc.' -__version__ = '5.0.2' +__version__ = '6.0.0' diff --git a/ipware/defaults.py b/ipware/defaults.py deleted file mode 100644 index 85e0fc7..0000000 --- a/ipware/defaults.py +++ /dev/null @@ -1,78 +0,0 @@ -from django.conf import settings - -# Search for the real IP address in the following order -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For -# X-Forwarded-For: , , -# Configurable via settings.py -IPWARE_META_PRECEDENCE_ORDER = getattr(settings, - 'IPWARE_META_PRECEDENCE_ORDER', ( - 'HTTP_X_FORWARDED_FOR', 'X_FORWARDED_FOR', - 'HTTP_CLIENT_IP', - 'HTTP_X_REAL_IP', - 'HTTP_X_FORWARDED', - 'HTTP_X_CLUSTER_CLIENT_IP', - 'HTTP_FORWARDED_FOR', - 'HTTP_FORWARDED', - 'HTTP_VIA', - 'HTTP_X_CLIENT_IP', - 'REMOTE_ADDR', - ) -) - -# Private IP addresses -# http://en.wikipedia.org/wiki/List_of_assigned_/8_IPv4_address_blocks -# https://en.wikipedia.org/wiki/Reserved_IP_addresses -# https://www.ietf.org/rfc/rfc1112.txt (IPv4 multicast) -# http://www.ietf.org/rfc/rfc3330.txt (IPv4) -# http://www.ietf.org/rfc/rfc5156.txt (IPv6) -# https://www.ietf.org/rfc/rfc6890.txt -# Regex would be ideal here, but this is keeping it simple -# Configurable via settings.py -IPWARE_PRIVATE_IP_PREFIX = getattr(settings, - 'IPWARE_PRIVATE_IP_PREFIX', ( - '0.', # messages to software - '10.', # class A private block - '100.64.', '100.65.', '100.66.', '100.67.', '100.68.', '100.69.', - '100.70.', '100.71.', '100.72.', '100.73.', '100.74.', '100.75.', - '100.76.', '100.77.', '100.78.', '100.79.', '100.80.', '100.81.', - '100.82.', '100.83.', '100.84.', '100.85.', '100.86.', '100.87.', - '100.88.', '100.89.', '100.90.', '100.91.', '100.92.', '100.93.', - '100.94.', '100.95.', '100.96.', '100.97.', '100.98.', '100.99.', - '100.100.', '100.101.', '100.102.', '100.103.', '100.104.', '100.105.', - '100.106.', '100.107.', '100.108.', '100.109.', '100.110.', '100.111.', - '100.112.', '100.113.', '100.114.', '100.115.', '100.116.', '100.117.', - '100.118.', '100.119.', '100.120.', '100.121.', '100.122.', '100.123.', - '100.124.', '100.125.', '100.126.', '100.127.', # carrier-grade NAT - '169.254.', # link-local block - '172.16.', '172.17.', '172.18.', '172.19.', - '172.20.', '172.21.', '172.22.', '172.23.', - '172.24.', '172.25.', '172.26.', '172.27.', - '172.28.', '172.29.', '172.30.', '172.31.', # class B private blocks - '192.0.0.', # reserved for IANA special purpose address registry - '192.0.2.', # reserved for documentation and example code - '192.168.', # class C private block - '198.18.', '198.19.', # reserved for inter-network communications between two separate subnets - '198.51.100.', # reserved for documentation and example code - '203.0.113.', # reserved for documentation and example code - '224.', '225.', '226.', '227.', '228.', '229.', '230.', '231.', '232.', - '233.', '234.', '235.', '236.', '237.', '238.', '239.', # multicast - '240.', '241.', '242.', '243.', '244.', '245.', '246.', '247.', '248.', - '249.', '250.', '251.', '252.', '253.', '254.', '255.', # reserved - '::', # Unspecified address - '::ffff:', '2001:10:', '2001:20:', # messages to software - '2001::', # TEREDO - '2001:2::', # benchmarking - '2001:db8:', # reserved for documentation and example code - 'fc', # IPv6 ULA (RFC4193) - NOTE: Reserved for future use, not currently in widespread use. - 'fd', # IPv6 ULA (RFC4193) - Mainly used for private network addressing - 'fe80:', # link-local unicast - 'ff00:', # IPv6 multicast - ) -) - -IPWARE_LOOPBACK_PREFIX = ( - '127.', # IPv4 loopback device (Host) - '::1', # IPv6 loopback device (Host) -) - -IPWARE_NON_PUBLIC_IP_PREFIX = IPWARE_PRIVATE_IP_PREFIX + IPWARE_LOOPBACK_PREFIX diff --git a/ipware/ip.py b/ipware/ip.py index e064526..9a1a65c 100644 --- a/ipware/ip.py +++ b/ipware/ip.py @@ -1,6 +1,5 @@ -from . import defaults as defs -from . import utils as util - +from django.conf import settings +from python_ipware import IpWare def get_client_ip( request, @@ -9,53 +8,24 @@ def get_client_ip( proxy_trusted_ips=None, request_header_order=None, ): - client_ip = None - routable = False - - if proxy_count is None: - proxy_count = -1 - - if proxy_trusted_ips is None: - proxy_trusted_ips = [] - - if request_header_order is None: - request_header_order = defs.IPWARE_META_PRECEDENCE_ORDER + leftmost = True if proxy_order == 'left-most' else False + proxy_count = proxy_count if proxy_count is not None else 0 + proxy_list = proxy_trusted_ips if proxy_trusted_ips is not None else [] + request_header_order = request_header_order if request_header_order is not None else getattr(settings, 'IPWARE_META_PRECEDENCE_ORDER', None) - for key in request_header_order: - value = util.get_request_meta(request, key) - if value: - ips, ip_count = util.get_ips_from_string(value) + # Instantiate IpWare with values from the function arguments + ipw = IpWare(precedence=request_header_order, + leftmost=leftmost, + proxy_count=proxy_count, + proxy_list=proxy_list) - if ip_count < 1: - # we are expecting at least one IP address to process - continue + ip, _ = ipw.get_client_ip(request.META, True) - if proxy_count == 0 and ip_count > 1: - # we are not expecting requests via any proxies - continue - - if proxy_count > 0 and proxy_count != ip_count - 1: - # we are expecting requests via `proxy_count` number of proxies - continue - - if proxy_trusted_ips and ip_count < 2: - # we are expecting requests via at least one trusted proxy - continue - - if proxy_order == 'right-most' and ip_count > 1: - # we are expecting requests via proxies to be custom as per `, , ` - ips.reverse() + client_ip = None + routable = False - if proxy_trusted_ips: - for proxy in proxy_trusted_ips: - # right most proxy is the most reliable proxy that talks to the django server - if ips[-1].startswith(proxy): - client_ip, routable = util.get_ip_info(ips[0]) - if client_ip and routable: - return client_ip, routable - else: - client_ip, routable = util.get_ip_info(util.get_best_ip(client_ip, ips[0])) - if client_ip and routable: - return client_ip, routable + if ip: + client_ip = str(ip) + routable = ip.is_global return client_ip, routable diff --git a/ipware/tests/tests_common.py b/ipware/tests/tests_common.py deleted file mode 100644 index 7b989d3..0000000 --- a/ipware/tests/tests_common.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.http import HttpRequest -from django.test import TestCase - -from .. import utils as util - - -class IPv4TestCase(TestCase): - """IP address Test""" - - - def test_is_valid_ip(self): - ip = '177.139.233.139' - self.assertTrue(util.is_valid_ip(ip)) - - ip = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' - self.assertTrue(util.is_valid_ip(ip)) - - def test_is_invalid_ip(self): - ip = '177.139.233.139x' - self.assertFalse(util.is_valid_ip(ip)) - - ip = '3ffe:1900:4545:3:200:f8ff:fe21:67cz' - self.assertFalse(util.is_valid_ip(ip)) - - def test_is_private_ip(self): - ip = '127.0.0.1' - self.assertTrue(util.is_private_ip(ip)) - - ip = '::1/128' - self.assertTrue(util.is_private_ip(ip)) - - def test_is_public_ip(self): - ip = '177.139.233.139' - self.assertTrue(util.is_public_ip(ip)) - - ip = '74dc::02ba' - self.assertTrue(util.is_public_ip(ip)) - - def test_is_loopback_ip(self): - ip = '127.0.0.1' - self.assertTrue(util.is_loopback_ip(ip)) - - ip = '177.139.233.139' - self.assertFalse(util.is_loopback_ip(ip)) - - ip = '10.0.0.1' - self.assertFalse(util.is_loopback_ip(ip)) - - ip = '::1/128' - self.assertTrue(util.is_loopback_ip(ip)) - - ip = '74dc::02ba' - self.assertFalse(util.is_loopback_ip(ip)) - - ip = '2001:db8:' - self.assertFalse(util.is_loopback_ip(ip)) - - def test_http_request_meta_headers(self): - request = HttpRequest() - ip_str = '192.168.255.182, 10.0.0.0, 127.0.0.1, 198.84.193.157, 177.139.233.139,' - request.META = { 'HTTP_X_FORWARDED_FOR': ip_str } - value = util.get_request_meta(request, 'HTTP_X_FORWARDED_FOR') - self.assertEqual(value, ip_str) - - def test_ips_from_strings(self): - ip_str = '192.168.255.182, 198.84.193.157, 177.139.233.139 ,' - result = util.get_ips_from_string(ip_str) - self.assertEqual(result, (['192.168.255.182', '198.84.193.157', '177.139.233.139'], 3)) - - def test_get_ip_info(self): - ip = '127.0.0.1' - result = util.get_ip_info(ip) - self.assertTrue(result, (ip, False)) - - ip = '10.0.01' - result = util.get_ip_info(ip) - self.assertTrue(result, (ip, False)) - - ip = '74dc::02ba' - result = util.get_ip_info(ip) - self.assertTrue(result, (ip, True)) - - def test_is_ipv6_private_block(self): - ip = 'fc00::1' - self.assertTrue(util.is_private_ip(ip)) - - ip = 'fd00::1' - self.assertTrue(util.is_private_ip(ip)) - - # Test an address that is outside the fc00::/7 private block range - ip = 'fb00::1' - self.assertFalse(util.is_private_ip(ip)) - ip = 'fe00::1' - self.assertFalse(util.is_private_ip(ip)) diff --git a/ipware/tests/tests_ip.py b/ipware/tests/tests_ip.py new file mode 100644 index 0000000..2d124dd --- /dev/null +++ b/ipware/tests/tests_ip.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from django.http import HttpRequest +from django.test import TestCase + +from ipware import get_client_ip + + +class IpTestCase(TestCase): + """IP address Test""" + + # run one test, to ensure we are loading python ipware correctly + # python-ipware has all the tests, so we don't need to test it here + def test_load(self): + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', + } + result = get_client_ip(request) + self.assertEqual(result, ("177.139.233.139", True)) diff --git a/ipware/tests/tests_ipv4.py b/ipware/tests/tests_ipv4.py deleted file mode 100644 index 1b24694..0000000 --- a/ipware/tests/tests_ipv4.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.http import HttpRequest -from django.test import TestCase - -from ipware import get_client_ip - - -class IPv4TestCase(TestCase): - """IP address Test""" - - def test_meta_none(self): - request = HttpRequest() - request.META = {} - ip, routable = get_client_ip(request) - self.assertIsNone(ip) - self.assertFalse(routable) - - def test_meta_single(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_multi(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - 'REMOTE_ADDR': '177.139.233.133', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_multi_precedence_order(self): - request = HttpRequest() - request.META = { - 'X_FORWARDED_FOR': '177.139.233.138, 198.84.193.157, 198.84.193.158', - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - 'REMOTE_ADDR': '177.139.233.133', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_order_left_most(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_order='left-most') - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_order_right_most(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_order='right-most') - self.assertEqual(result, ("198.84.193.158", True)) - - def test_meta_multi_precedence_private_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '10.0.0.0, 10.0.0.1, 10.0.0.2', - 'X_FORWARDED_FOR': '177.139.233.138, 198.84.193.157, 198.84.193.158', - 'REMOTE_ADDR': '177.139.233.133', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.138", True)) - - def test_meta_multi_precedence_invalid_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': 'unknown, 10.0.0.1, 10.0.0.2', - 'X_FORWARDED_FOR': '177.139.233.138, 198.84.193.157, 198.84.193.158', - 'REMOTE_ADDR': '177.139.233.133', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.138", True)) - - def test_meta_error_only(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': 'unknown, 177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request) - self.assertEqual(result, (None, False)) - - def test_meta_error_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': 'unknown, 177.139.233.139, 198.84.193.157, 198.84.193.158', - 'X_FORWARDED_FOR': '177.139.233.138, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.138", True)) - - def test_meta_singleton(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_singleton_proxy_count(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139', - } - result = get_client_ip(request, proxy_count=1) - self.assertEqual(result, (None, False)) - - def test_meta_singleton_proxy_count_private(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '10.0.0.0', - 'HTTP_X_REAL_IP': '177.139.233.139', - } - result = get_client_ip(request, proxy_count=1) - self.assertEqual(result, (None, False)) - - def test_meta_singleton_private_fallback(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '10.0.0.0', - 'HTTP_X_REAL_IP': '177.139.233.139', - } - result = get_client_ip(request) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_trusted_ips_exact_ip_check(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_trusted_ips=['198.84.193.158']) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_trusted_ips_exact_ips_check(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_trusted_ips=['198.84.193.157', '198.84.193.158']) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_trusted_ips_subnet_start_with_check(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_trusted_ips=['198.84.193']) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_trusted_ips_does_not_start_with_check(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_trusted_ips=['84.193.158']) - self.assertEqual(result, (None, False)) - - def test_meta_proxy_trusted_ips_proxy_count(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_count=2, proxy_trusted_ips=['198.84.193.158']) - self.assertEqual(result, ("177.139.233.139", True)) - - def test_meta_proxy_trusted_ips_proxy_count_less_error(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.158', - } - result = get_client_ip(request, proxy_count=2, proxy_trusted_ips=['198.84.193.158']) - self.assertEqual(result, (None, False)) - - def test_meta_proxy_trusted_ips_proxy_count_more_error(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - result = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['198.84.193.158']) - self.assertEqual(result, (None, False)) - - def test_meta_proxy_trusted_ips_proxy_count_more_error_fallback(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - 'HTTP_X_REAL_IP': '177.139.233.139', - } - result = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['198.84.193.158']) - self.assertEqual(result, (None, False)) - - def test_best_matched_ip(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '192.168.1.1', - 'REMOTE_ADDR': '177.31.233.133', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("177.31.233.133", True)) - - def test_best_matched_ip_public(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '177.31.233.122', - 'REMOTE_ADDR': '177.31.233.133', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("177.31.233.122", True)) - - def test_best_matched_ip_private(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '192.168.1.1', - 'REMOTE_ADDR': '127.0.0.1', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("192.168.1.1", False)) - - def test_best_matched_ip_private_loopback_precedence(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '127.0.0.1', - 'REMOTE_ADDR': '192.168.1.1', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("192.168.1.1", False)) - - def test_best_matched_ip_private_precedence(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '172.25.0.1', - 'REMOTE_ADDR': '172.25.0.3', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("172.25.0.3", False)) - - def test_100_low_range_public(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '100.63.0.9', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("100.63.0.9", True)) - - def test_100_block_private(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '100.76.0.9', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("100.76.0.9", False)) - - def test_100_high_range_public(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '100.128.0.9', - } - ip = get_client_ip(request) - self.assertEqual(ip, ("100.128.0.9", True)) - - def test_request_header_order_specific(self): - request = HttpRequest() - request.META = { - 'HTTP_X_REAL_IP': '192.168.1.1', - 'REMOTE_ADDR': '177.139.233.139', - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - } - ip = get_client_ip(request, request_header_order=['HTTP_X_FORWARDED_FOR']) - self.assertEqual(ip, ("177.139.233.139", True)) - - - def test_request_header_order_multiple(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', - 'X_FORWARDED_FOR': '177.139.233.138, 198.84.193.157, 198.84.193.158', - 'REMOTE_ADDR': '177.139.233.133', - } - ip = get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'HTTP_X_FORWARDED_FOR']) - self.assertEqual(ip, ("177.139.233.138", True)) diff --git a/ipware/tests/tests_ipv6.py b/ipware/tests/tests_ipv6.py deleted file mode 100644 index 8ee8fc1..0000000 --- a/ipware/tests/tests_ipv6.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.http import HttpRequest -from django.test import TestCase - -from ipware import get_client_ip - - -class IPv4TestCase(TestCase): - """IP address Test""" - - def test_meta_none(self): - request = HttpRequest() - request.META = {} - ip, routable = get_client_ip(request) - self.assertIsNone(ip) - self.assertFalse(routable) - - def test_meta_single(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_multi(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'REMOTE_ADDR': '74dc::02bc', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_multi_precedence_order(self): - request = HttpRequest() - request.META = { - 'X_FORWARDED_FOR': '74dc::02be, 74dc::02bf', - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'REMOTE_ADDR': '74dc::02bc', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_proxy_order_left_most(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request, proxy_order='left-most') - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_proxy_order_right_most(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request, proxy_order='right-most') - self.assertEqual(result, ("74dc::02bb", True)) - - def test_meta_multi_precedence_private_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '2001:db8:, ::1', - 'X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'REMOTE_ADDR': '74dc::02bc', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_multi_precedence_invalid_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': 'unknown, 2001:db8:, ::1', - 'X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'REMOTE_ADDR': '74dc::02bc', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_error_only(self): - request = HttpRequest() - request.META = { - 'X_FORWARDED_FOR': 'unknown, 3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request) - self.assertEqual(result, (None, False)) - - def test_meta_error_first(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': 'unknown, 3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_singleton(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_singleton_proxy_count(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf', - 'HTTP_X_REAL_IP': '74dc::02ba', - } - result = get_client_ip(request, proxy_count=1) - self.assertEqual(result, (None, False)) - - def test_meta_singleton_proxy_count_private(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '::1', - 'HTTP_X_REAL_IP': '3ffe:1900:4545:3:200:f8ff:fe21:67cf', - } - result = get_client_ip(request, proxy_count=1) - self.assertEqual(result, (None, False)) - - def test_meta_singleton_private_fallback(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '::1', - 'HTTP_X_REAL_IP': '3ffe:1900:4545:3:200:f8ff:fe21:67cf', - } - result = get_client_ip(request) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_proxy_trusted_ips(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request, proxy_trusted_ips=['74dc::02bb']) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_proxy_trusted_ips_proxy_count(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request, proxy_count=2, proxy_trusted_ips=['74dc::02bb']) - self.assertEqual(result, ("3ffe:1900:4545:3:200:f8ff:fe21:67cf", True)) - - def test_meta_proxy_trusted_ips_proxy_count_less_error(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02bb', - } - result = get_client_ip(request, proxy_count=2, proxy_trusted_ips=['74dc::02bb']) - self.assertEqual(result, (None, False)) - - def test_meta_proxy_trusted_ips_proxy_count_more_error(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - } - result = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['74dc::02bb']) - self.assertEqual(result, (None, False)) - - def test_meta_proxy_trusted_ips_proxy_count_more_error_ignore_fallback(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '3ffe:1900:4545:3:200:f8ff:fe21:67cf, 74dc::02ba, 74dc::02bb', - 'HTTP_X_REAL_IP': '74dc::02bb', - } - result = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['74dc::02bb']) - self.assertEqual(result, (None, False)) - - -class IPv6EncapsulationOfIPv4TestCase(TestCase): - """IPv6 Encapsulation of IPv4 - IP address Test""" - - def test_ipv6_encapsulation_of_ipv4_private(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '::ffff:127.0.0.1', - } - result = get_client_ip(request) - self.assertEqual(result, ('127.0.0.1', False)) - - def test_ipv6_encapsulation_of_ipv4_public(self): - request = HttpRequest() - request.META = { - 'HTTP_X_FORWARDED_FOR': '::ffff:177.139.233.139', - } - result = get_client_ip(request) - self.assertEqual(result, ('177.139.233.139', True)) diff --git a/ipware/utils.py b/ipware/utils.py deleted file mode 100644 index 43961a6..0000000 --- a/ipware/utils.py +++ /dev/null @@ -1,125 +0,0 @@ -import socket - -from . import defaults as defs - - -def cleanup_ip(ip): - """ - Given ip address string, it cleans it up - """ - ip = ip.strip().lower() - if (ip.startswith('::ffff:')): - return ip.replace('::ffff:', '') - return ip - - -def is_valid_ipv4(ip_str): - """ - Check the validity of an IPv4 address - """ - try: - socket.inet_pton(socket.AF_INET, ip_str) - except AttributeError: # pragma: no cover - try: # Fall-back on legacy API or False - socket.inet_aton(ip_str) - except (AttributeError, socket.error): - return False - return ip_str.count('.') == 3 - except socket.error: - return False - return True - - -def is_valid_ipv6(ip_str): - """ - Check the validity of an IPv6 address - """ - try: - socket.inet_pton(socket.AF_INET6, ip_str) - except socket.error: - return False - return True - - -def is_valid_ip(ip_str): - """ - Check the validity of an IP address - """ - return is_valid_ipv4(ip_str) or is_valid_ipv6(ip_str) - - -def is_private_ip(ip_str): - """ - Returns true of ip_str is private & not routable, else return false - """ - return ip_str.startswith(defs.IPWARE_NON_PUBLIC_IP_PREFIX) - - -def is_public_ip(ip_str): - """ - Returns true of ip_str is public & routable, else return false - """ - return not is_private_ip(ip_str) - - -def is_loopback_ip(ip_str): - """ - Returns true of ip_str is public & routable, else return false - """ - return ip_str.startswith(defs.IPWARE_LOOPBACK_PREFIX) - - -def get_request_meta(request, key): - """ - Given a key, it returns a cleaned up version of the value from request.META, or None - """ - value = request.META.get(key, request.META.get(key.replace('_', '-'), '')).strip() - if value == '': - return None - return value - - -def get_ips_from_string(ip_str): - """ - Given a string, it returns a list of one or more valid IP addresses - """ - ip_list = [] - - for ip in ip_str.split(','): - clean_ip = ip.strip().lower() - if clean_ip: - ip_list.append(clean_ip) - - ip_count = len(ip_list) - if ip_count > 0 and is_valid_ip(ip_list[0]) and is_valid_ip(ip_list[-1]): - return ip_list, ip_count - - return [], 0 - - -def get_ip_info(ip_str): - """ - Given a string, it returns a tuple of (IP, Routable). - """ - ip = None - is_routable_ip = False - clean_ip = cleanup_ip(ip_str) - if is_valid_ip(clean_ip): - ip = clean_ip - is_routable_ip = is_public_ip(ip) - return ip, is_routable_ip - - -def get_best_ip(last_ip, next_ip): - """ - Given two IP addresses, it returns the the best match ip. - Order of precedence is (Public, Private, Loopback, None) - Right-most IP is returned - """ - if last_ip is None: - return next_ip - if is_public_ip(last_ip) and not is_public_ip(next_ip): - return last_ip - if is_private_ip(last_ip) and is_loopback_ip(next_ip): - return last_ip - return next_ip diff --git a/setup.py b/setup.py index fb1c8be..f0c4608 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ python_requires = ">=3.8" here = os.path.abspath(os.path.dirname(__file__)) -requires = [] +requires = ['python-ipware>=2.0.0'] test_requirements = [] about = {} From de8a5e4610dbc39d77294cc44a9de5a7c14187ba Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 17:47:32 -0500 Subject: [PATCH 02/27] format --- ipware/ip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ipware/ip.py b/ipware/ip.py index 9a1a65c..d5c1d8e 100644 --- a/ipware/ip.py +++ b/ipware/ip.py @@ -1,6 +1,7 @@ from django.conf import settings from python_ipware import IpWare + def get_client_ip( request, proxy_order='left-most', From 8d2b3e60ecb75d0ed710c7d27c5c98e5108e1625 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 17:49:02 -0500 Subject: [PATCH 03/27] logic --- ipware/ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipware/ip.py b/ipware/ip.py index d5c1d8e..5dbf23a 100644 --- a/ipware/ip.py +++ b/ipware/ip.py @@ -9,7 +9,7 @@ def get_client_ip( proxy_trusted_ips=None, request_header_order=None, ): - leftmost = True if proxy_order == 'left-most' else False + leftmost = proxy_order == 'left-most' proxy_count = proxy_count if proxy_count is not None else 0 proxy_list = proxy_trusted_ips if proxy_trusted_ips is not None else [] request_header_order = request_header_order if request_header_order is not None else getattr(settings, 'IPWARE_META_PRECEDENCE_ORDER', None) From 549363d301e117c4cc6bf731b2d90755f2b220a9 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 17:50:07 -0500 Subject: [PATCH 04/27] format --- ipware/ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipware/ip.py b/ipware/ip.py index 5dbf23a..0d9e648 100644 --- a/ipware/ip.py +++ b/ipware/ip.py @@ -12,7 +12,7 @@ def get_client_ip( leftmost = proxy_order == 'left-most' proxy_count = proxy_count if proxy_count is not None else 0 proxy_list = proxy_trusted_ips if proxy_trusted_ips is not None else [] - request_header_order = request_header_order if request_header_order is not None else getattr(settings, 'IPWARE_META_PRECEDENCE_ORDER', None) + request_header_order = getattr(settings, 'IPWARE_META_PRECEDENCE_ORDER', request_header_order) # Instantiate IpWare with values from the function arguments ipw = IpWare(precedence=request_header_order, From 82cb8431911ed27a04f1c3d9440f4647dcab930e Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 17:57:44 -0500 Subject: [PATCH 05/27] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33241ab..290bcff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # Alternative package -If you prefer a python only version that does not integrate with Django directly, but allows for more flexibility and advanced features, you can use the [python-ipware](https://github.com/un33k/python-ipware) package instead. `django-ipware` is now a wrapper using python-ipware underneath. +If you prefer a python only version that does not integrate with Django directly, but allows for more flexibility and advanced features, you can use the [python-ipware](https://github.com/un33k/python-ipware) package instead. `django-ipware` is a wrapper using [python-ipware](https://github.com/un33k/python-ipware) under the hood staring from version `6.0.0`. # Overview From ad8b2f7f414199451ca466f65b6a61f23abecf31 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:14:05 -0500 Subject: [PATCH 06/27] add more tests --- ipware/tests/tests_ip.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ipware/tests/tests_ip.py b/ipware/tests/tests_ip.py index 2d124dd..11d10a1 100644 --- a/ipware/tests/tests_ip.py +++ b/ipware/tests/tests_ip.py @@ -18,3 +18,27 @@ def test_load(self): } result = get_client_ip(request) self.assertEqual(result, ("177.139.233.139", True)) + + def test_meta_proxy_order_left_most(self): + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', + } + result = get_client_ip(request, proxy_order='left-most') + self.assertEqual(result, ("177.139.233.139", True)) + + def test_meta_singleton_proxy_count(self): + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', + } + result = get_client_ip(request, proxy_count=2) + self.assertEqual(result, ("177.139.233.139", True)) + + def test_meta_proxy_trusted_ips_exact_ip_check(self): + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', + } + result = get_client_ip(request, proxy_trusted_ips=['198.84.193.157', '198.84.193.158']) + self.assertEqual(result, ("177.139.233.139", True)) \ No newline at end of file From 8c9dceed48905bf51c0c5484eaa58952a6a184dc Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:16:45 -0500 Subject: [PATCH 07/27] format --- ipware/tests/tests_ip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipware/tests/tests_ip.py b/ipware/tests/tests_ip.py index 11d10a1..7616901 100644 --- a/ipware/tests/tests_ip.py +++ b/ipware/tests/tests_ip.py @@ -34,11 +34,11 @@ def test_meta_singleton_proxy_count(self): } result = get_client_ip(request, proxy_count=2) self.assertEqual(result, ("177.139.233.139", True)) - + def test_meta_proxy_trusted_ips_exact_ip_check(self): request = HttpRequest() request.META = { 'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158', } result = get_client_ip(request, proxy_trusted_ips=['198.84.193.157', '198.84.193.158']) - self.assertEqual(result, ("177.139.233.139", True)) \ No newline at end of file + self.assertEqual(result, ("177.139.233.139", True)) From 7b89b79ef52927c652790990cceb5391442c1ca7 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:23:27 -0500 Subject: [PATCH 08/27] omit version in coverage --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00c8fd2..27dbc06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Run ruff run: ruff . - name: Run test - run: coverage run --source=ipware manage.py test + run: coverage run --omit=ipware/__version.py --source=ipware manage.py test - name: Coveralls run: coveralls --service=github env: From 2ef857353a28e6be3b5e034206baec002d838835 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:29:48 -0500 Subject: [PATCH 09/27] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e023e..a3e47be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Enhancement: (breaking changes) - Use python-ipware under the hood - Minor behavior changes as python-ipware is more accurate +- Use 5.0.2 if you need the old behavior # 5.0.2 From 6e4fc909e312872f51fdccaad3a5215beaa3dbc6 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:33:28 -0500 Subject: [PATCH 10/27] ci/cd --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27dbc06..0dfece9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Run ruff run: ruff . - name: Run test - run: coverage run --omit=ipware/__version.py --source=ipware manage.py test + run: coverage run --omit=ipware/__version__.py,ipware/tests* --source=ipware manage.py test - name: Coveralls run: coveralls --service=github env: From 94a2e831265b7fde0d334d0e724b2cb2d3d53536 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:37:58 -0500 Subject: [PATCH 11/27] coverall exclude --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dfece9..2546740 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Run ruff run: ruff . - name: Run test - run: coverage run --omit=ipware/__version__.py,ipware/tests* --source=ipware manage.py test + run: coverage run --exclude="ipware/__version__.py" --exclude="ipware/tests/*" --source=ipware manage.py test - name: Coveralls run: coveralls --service=github env: From c7e409ad283b89172dc844c27f4a54c2774bf2fb Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:41:25 -0500 Subject: [PATCH 12/27] omit coverall --- .github/workflows/ci.yml | 2 +- pyproject.toml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2546740..00c8fd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Run ruff run: ruff . - name: Run test - run: coverage run --exclude="ipware/__version__.py" --exclude="ipware/tests/*" --source=ipware manage.py test + run: coverage run --source=ipware manage.py test - name: Coveralls run: coveralls --service=github env: diff --git a/pyproject.toml b/pyproject.toml index 228f38f..798fafd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,3 +35,9 @@ target-version = "py311" [tool.ruff.mccabe] max-complexity = 16 + +[tool.coverage.run] +omit = [ + "ipware/__version__.py", + "ipware/tests/*", +] \ No newline at end of file From 4f71957d173b4dc9fda86fcc5db532844515495b Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:43:40 -0500 Subject: [PATCH 13/27] omit coverall --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 798fafd..a86b8a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,14 @@ requires = [ ] build-backend = "setuptools.build_meta" +[project.optional-dependencies] +dev = [ + "ruff", + "coveralls~=3.3", + "coverage[toml]", + "twine" +] + [tool.ruff] select = [ "B", From ff256e968983cdbc84b7b3a807d28781ad56c8e4 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:46:12 -0500 Subject: [PATCH 14/27] omit coverall revert --- pyproject.toml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a86b8a1..3b95112 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,14 +5,6 @@ requires = [ ] build-backend = "setuptools.build_meta" -[project.optional-dependencies] -dev = [ - "ruff", - "coveralls~=3.3", - "coverage[toml]", - "twine" -] - [tool.ruff] select = [ "B", @@ -42,10 +34,4 @@ line-length = 107 target-version = "py311" [tool.ruff.mccabe] -max-complexity = 16 - -[tool.coverage.run] -omit = [ - "ipware/__version__.py", - "ipware/tests/*", -] \ No newline at end of file +max-complexity = 16 \ No newline at end of file From 5851f881b16348d2d3df4a00e094bbb4f216b43b Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 18:54:17 -0500 Subject: [PATCH 15/27] toml --- format.sh | 10 +----- ipware/py.typed | 0 pyproject.toml | 61 ++++++++++++++++++++++++++++++++++--- setup.cfg | 2 -- setup.py | 81 ------------------------------------------------- 5 files changed, 58 insertions(+), 96 deletions(-) create mode 100644 ipware/py.typed delete mode 100644 setup.cfg delete mode 100755 setup.py diff --git a/format.sh b/format.sh index 566d462..96619a0 100755 --- a/format.sh +++ b/format.sh @@ -1,11 +1,3 @@ #!/bin/bash -# Ignoring autogenerated files -# -- Migration directories -# Ignoring error codes -# -- E128 continuation line under-indented for visual indent -# -- E225 missing whitespace around operator -# -- E501 line too long - -pycodestyle --exclude=migrations,tests --ignore=E128,E225,E241,E501 . -flake8 --exclude=migrations,tests --ignore=E128,E225,E241,E501 . +ruff . \ No newline at end of file diff --git a/ipware/py.typed b/ipware/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 3b95112..327a3a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,53 @@ requires = [ ] build-backend = "setuptools.build_meta" +[project] +name = "django-ipware" +authors = [ + {name = "Val Neekman", email = "info@neekware.com"}, +] +description = "A Python package to retrieve user's IP address" +requires-python = ">=3.7" +dynamic = ["version", "readme"] +license = {text = "MIT"} +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Natural Language :: English", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] + +[project.urls] +Documentation = "https://github.com/un33k/django-ipware#readme" +Issues = "https://github.com/un33k/django-ipware/issues" +Source = "https://github.com/un33k/django-ipware" +Changelog = "https://github.com/un33k/django-ipware/blob/master/CHANGELOG.md" + +[project.optional-dependencies] +dev = [ + "ruff", + "coveralls~=3.3", + "coverage[toml]", + "twine" +] + +[tool.setuptools] +packages = ["ipware"] + +[tool.setuptools.dynamic] +version = {attr = "ipware.__version__"} +readme = {file = ["README.md"], content-type = "text/markdown"} + +[tool.setuptools.package-data] +"ipware" = ["py.typed"] + [tool.ruff] select = [ "B", @@ -29,9 +76,15 @@ select = [ "TID", "W", ] -ignore = ["PGH004", "TID252"] -line-length = 107 -target-version = "py311" +ignore = ["PGH004", "TID252", "E501", "I001", "EM101", "SIM108", "SIM110"] +line-length = 120 +target-version = "py37" [tool.ruff.mccabe] -max-complexity = 16 \ No newline at end of file +max-complexity = 16 + +[tool.coverage.run] +omit = [ + "ipware/__version__.py", + "ipware/tests/*", +] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 29b21fb..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -license_file = LICENSE \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100755 index f0c4608..0000000 --- a/setup.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# Learn more: https://github.com/un33k/setup.py -import os -import sys -from codecs import open -from shutil import rmtree - -from setuptools import setup - -package = 'ipware' -python_requires = ">=3.8" -here = os.path.abspath(os.path.dirname(__file__)) - -requires = ['python-ipware>=2.0.0'] -test_requirements = [] - -about = {} -with open(os.path.join(here, package, '__version__.py'), 'r', 'utf-8') as f: - exec(f.read(), about) - -with open('README.md', 'r', 'utf-8') as f: - readme = f.read() - - -def status(s): - print('\033[1m{0}\033[0m'.format(s)) - - -# 'setup.py publish' shortcut. -if sys.argv[-1] == 'publish': - try: - status('Removing previous builds…') - rmtree(os.path.join(here, 'dist')) - except OSError: - pass - - status('Building Source and Wheel (universal) distribution…') - os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)) - - status('Uploading the package to PyPI via Twine…') - os.system('twine upload dist/*') - - status('Pushing git tags…') - os.system('git tag v{0}'.format(about['__version__'])) - os.system('git push --tags') - sys.exit() - -setup( - name=about['__title__'], - version=about['__version__'], - description=about['__description__'], - long_description=readme, - long_description_content_type='text/markdown', - author=about['__author__'], - author_email=about['__author_email__'], - url=about['__url__'], - packages=[package], - package_data={'': ['LICENSE']}, - package_dir={'ipware': 'ipware'}, - include_package_data=True, - python_requires=python_requires, - install_requires=requires, - license=about['__license__'], - zip_safe=False, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Natural Language :: English', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - ], - cmdclass={}, - tests_require=test_requirements, - extras_require={}, - project_urls={}, -) From bd4194d8d31bdf88317165af70108729022f2719 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:01:05 -0500 Subject: [PATCH 16/27] add deps --- pyproject.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 327a3a5..4c0c6d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,8 @@ name = "django-ipware" authors = [ {name = "Val Neekman", email = "info@neekware.com"}, ] -description = "A Python package to retrieve user's IP address" -requires-python = ">=3.7" +description = "A Django package to retrieve user's IP address" +requires-python = ">=3.8" dynamic = ["version", "readme"] license = {text = "MIT"} classifiers = [ @@ -20,8 +20,6 @@ classifiers = [ "Natural Language :: English", "License :: OSI Approved :: MIT License", "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -34,6 +32,9 @@ Issues = "https://github.com/un33k/django-ipware/issues" Source = "https://github.com/un33k/django-ipware" Changelog = "https://github.com/un33k/django-ipware/blob/master/CHANGELOG.md" +[project.dependencies] +python-ipware>="2.0.0" + [project.optional-dependencies] dev = [ "ruff", From 9629ca9e3ecad62efec18f18408f609b0043424e Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:03:40 -0500 Subject: [PATCH 17/27] add deps --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4c0c6d1..8c20aab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,9 @@ description = "A Django package to retrieve user's IP address" requires-python = ">=3.8" dynamic = ["version", "readme"] license = {text = "MIT"} +dependencies = [ + "python-ipware>=2.0.0" +] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -32,9 +35,6 @@ Issues = "https://github.com/un33k/django-ipware/issues" Source = "https://github.com/un33k/django-ipware" Changelog = "https://github.com/un33k/django-ipware/blob/master/CHANGELOG.md" -[project.dependencies] -python-ipware>="2.0.0" - [project.optional-dependencies] dev = [ "ruff", From 90567766d4fcb349187143ec8722e7bc71e4e6fc Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:05:35 -0500 Subject: [PATCH 18/27] django install order --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00c8fd2..2c5f6d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e . pip install --upgrade coveralls ruff pip install "django~=${{ matrix.django-version }}" + pip install -e . - name: Run ruff run: ruff . - name: Run test From c191689de83a291d93a6581a6e15b47312acb0b3 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:09:53 -0500 Subject: [PATCH 19/27] revert toml --- pyproject.toml | 62 +++----------------------------------- setup.cfg | 2 ++ setup.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 58 deletions(-) create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml index 8c20aab..3b95112 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,54 +5,6 @@ requires = [ ] build-backend = "setuptools.build_meta" -[project] -name = "django-ipware" -authors = [ - {name = "Val Neekman", email = "info@neekware.com"}, -] -description = "A Django package to retrieve user's IP address" -requires-python = ">=3.8" -dynamic = ["version", "readme"] -license = {text = "MIT"} -dependencies = [ - "python-ipware>=2.0.0" -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Natural Language :: English", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", -] - -[project.urls] -Documentation = "https://github.com/un33k/django-ipware#readme" -Issues = "https://github.com/un33k/django-ipware/issues" -Source = "https://github.com/un33k/django-ipware" -Changelog = "https://github.com/un33k/django-ipware/blob/master/CHANGELOG.md" - -[project.optional-dependencies] -dev = [ - "ruff", - "coveralls~=3.3", - "coverage[toml]", - "twine" -] - -[tool.setuptools] -packages = ["ipware"] - -[tool.setuptools.dynamic] -version = {attr = "ipware.__version__"} -readme = {file = ["README.md"], content-type = "text/markdown"} - -[tool.setuptools.package-data] -"ipware" = ["py.typed"] - [tool.ruff] select = [ "B", @@ -77,15 +29,9 @@ select = [ "TID", "W", ] -ignore = ["PGH004", "TID252", "E501", "I001", "EM101", "SIM108", "SIM110"] -line-length = 120 -target-version = "py37" +ignore = ["PGH004", "TID252"] +line-length = 107 +target-version = "py311" [tool.ruff.mccabe] -max-complexity = 16 - -[tool.coverage.run] -omit = [ - "ipware/__version__.py", - "ipware/tests/*", -] \ No newline at end of file +max-complexity = 16 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..29b21fb --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_file = LICENSE \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..60adc34 --- /dev/null +++ b/setup.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# Learn more: https://github.com/un33k/setup.py +import os +import sys +from codecs import open +from shutil import rmtree + +from setuptools import setup + +package = 'ipware' +python_requires = ">=3.8" +here = os.path.abspath(os.path.dirname(__file__)) + +requires = [] +test_requirements = [] + +about = {} +with open(os.path.join(here, package, '__version__.py'), 'r', 'utf-8') as f: + exec(f.read(), about) + +with open('README.md', 'r', 'utf-8') as f: + readme = f.read() + + +def status(s): + print('\033[1m{0}\033[0m'.format(s)) + + +# 'setup.py publish' shortcut. +if sys.argv[-1] == 'publish': + try: + status('Removing previous builds…') + rmtree(os.path.join(here, 'dist')) + except OSError: + pass + + status('Building Source and Wheel (universal) distribution…') + os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)) + + status('Uploading the package to PyPI via Twine…') + os.system('twine upload dist/*') + + status('Pushing git tags…') + os.system('git tag v{0}'.format(about['__version__'])) + os.system('git push --tags') + sys.exit() + +setup( + name=about['__title__'], + version=about['__version__'], + description=about['__description__'], + long_description=readme, + long_description_content_type='text/markdown', + author=about['__author__'], + author_email=about['__author_email__'], + url=about['__url__'], + packages=[package], + package_data={'': ['LICENSE']}, + package_dir={'ipware': 'ipware'}, + include_package_data=True, + python_requires=python_requires, + install_requires=requires, + license=about['__license__'], + zip_safe=False, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + ], + cmdclass={}, + tests_require=test_requirements, + extras_require={}, + project_urls={}, +) \ No newline at end of file From f7cee2b7e64d76b8a39ec28c46c3078386d7b6c5 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:10:45 -0500 Subject: [PATCH 20/27] format --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 29b21fb..0c9e0fc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [metadata] -license_file = LICENSE \ No newline at end of file +license_file = LICENSE diff --git a/setup.py b/setup.py index 60adc34..fb1c8be 100644 --- a/setup.py +++ b/setup.py @@ -78,4 +78,4 @@ def status(s): tests_require=test_requirements, extras_require={}, project_urls={}, -) \ No newline at end of file +) From 1bcfebe1b280f5b31457f4e5197110f69ea7d49e Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:11:37 -0500 Subject: [PATCH 21/27] format --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3b95112..732438f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ select = [ "TID", "W", ] -ignore = ["PGH004", "TID252"] +ignore = ["PGH004", "TID252", "E501", "I001", "EM101", "SIM108", "SIM110"] line-length = 107 target-version = "py311" From c8e0dbcbf3ec10871368f228a7951639284d8ef4 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:13:13 -0500 Subject: [PATCH 22/27] setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fb1c8be..f0c4608 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ python_requires = ">=3.8" here = os.path.abspath(os.path.dirname(__file__)) -requires = [] +requires = ['python-ipware>=2.0.0'] test_requirements = [] about = {} From 186b9ccfb2ef55089f836bde253a5a601c769212 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 22 Nov 2023 19:14:59 -0500 Subject: [PATCH 23/27] actions --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c5f6d5..d202e5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install -e . pip install --upgrade coveralls ruff pip install "django~=${{ matrix.django-version }}" - pip install -e . - name: Run ruff run: ruff . - name: Run test @@ -46,4 +46,4 @@ jobs: - name: Coveralls run: coveralls --service=github env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 2430b9c91ebb19ce962eb287788d743f1fdc4d93 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Sun, 4 Feb 2024 15:36:56 -0300 Subject: [PATCH 24/27] Add types for get_client_ip parameters (#110) --- ipware/ip.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ipware/ip.py b/ipware/ip.py index e8ae6d9..c64a3bd 100644 --- a/ipware/ip.py +++ b/ipware/ip.py @@ -1,14 +1,17 @@ +from typing import Iterable, Literal, Optional, Tuple + from django.conf import settings +from django.http import HttpRequest from python_ipware import IpWare def get_client_ip( - request, - proxy_order='left-most', - proxy_count=None, - proxy_trusted_ips=None, - request_header_order=None, -): + request: HttpRequest, + proxy_order: Literal['left-most', 'right-most'] = 'left-most', + proxy_count: Optional[int] = None, + proxy_trusted_ips: Optional[Iterable[str]] = None, + request_header_order: Optional[Iterable[str]] = None, +) -> Tuple[str, bool]: leftmost = proxy_order == 'left-most' proxy_count = proxy_count if proxy_count is not None else getattr(settings, 'IPWARE_META_PROXY_COUNT', 0) proxy_list = proxy_trusted_ips if proxy_trusted_ips is not None else [] From a831870f96654d8ee0d83c0d5cadcf3517c8046e Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Sun, 4 Feb 2024 13:39:19 -0500 Subject: [PATCH 25/27] up version 6.0.4 --- CHANGELOG.md | 5 +++++ ipware/__version__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1acb52..2468a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 6.0.4 + +Enhancement: +- Add typings (thx: @federicobond) + # 6.0.3 Enhancement: diff --git a/ipware/__version__.py b/ipware/__version__.py index 30b3948..7a25cf8 100644 --- a/ipware/__version__.py +++ b/ipware/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/django-ipware' __license__ = 'MIT' __copyright__ = 'Copyright 2023 Val Neekman @ Neekware Inc.' -__version__ = '6.0.3' +__version__ = '6.0.4' From 25b21385635366f32453229b55dc60370e9830fc Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Sun, 4 Feb 2024 14:05:34 -0500 Subject: [PATCH 26/27] up version ci action --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8a13c1..172cf15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 3993ee94d79da4046948d455a35d7c10cc0dbeb7 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Sun, 4 Feb 2024 14:18:46 -0500 Subject: [PATCH 27/27] coverallapp --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 172cf15..339cfc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,6 @@ jobs: - name: Run test run: coverage run --source=ipware manage.py test - name: Coveralls - run: coveralls --service=github + uses: coverallsapp/github-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}