mirror of
https://github.com/django/django.git
synced 2025-06-07 04:29:12 +00:00
Fixed #35091 -- Allowed GeoIP2 querying using IPv4Address/IPv6Address.
This commit is contained in:
parent
53fc6ac649
commit
cc56c22a24
@ -12,6 +12,7 @@ Grab GeoLite2-Country.mmdb.gz and GeoLite2-City.mmdb.gz, and unzip them in the
|
|||||||
directory corresponding to settings.GEOIP_PATH.
|
directory corresponding to settings.GEOIP_PATH.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
@ -172,10 +173,10 @@ class GeoIP2:
|
|||||||
|
|
||||||
def _check_query(self, query, city=False, city_or_country=False):
|
def _check_query(self, query, city=False, city_or_country=False):
|
||||||
"Check the query and database availability."
|
"Check the query and database availability."
|
||||||
# Making sure a string was passed in for the query.
|
if not isinstance(query, (str, ipaddress.IPv4Address, ipaddress.IPv6Address)):
|
||||||
if not isinstance(query, str):
|
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"GeoIP query must be a string, not type %s" % type(query).__name__
|
"GeoIP query must be a string or instance of IPv4Address or "
|
||||||
|
"IPv6Address, not type %s" % type(query).__name__,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extra checks for the existence of country and city databases.
|
# Extra checks for the existence of country and city databases.
|
||||||
|
@ -107,10 +107,11 @@ and given cache setting.
|
|||||||
Querying
|
Querying
|
||||||
--------
|
--------
|
||||||
|
|
||||||
All the following querying routines may take either a string IP address
|
All the following querying routines may take an instance of
|
||||||
or a fully qualified domain name (FQDN). For example, both
|
:class:`~ipaddress.IPv4Address` or :class:`~ipaddress.IPv6Address`, a string IP
|
||||||
``'205.186.163.125'`` and ``'djangoproject.com'`` would be valid query
|
address, or a fully qualified domain name (FQDN). For example,
|
||||||
parameters.
|
``IPv4Address("205.186.163.125")``, ``"205.186.163.125"``, and
|
||||||
|
``"djangoproject.com"`` would all be valid query parameters.
|
||||||
|
|
||||||
.. method:: GeoIP2.city(query)
|
.. method:: GeoIP2.city(query)
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ Minor features
|
|||||||
* :class:`~django.contrib.gis.db.models.Collect` is now supported on MySQL
|
* :class:`~django.contrib.gis.db.models.Collect` is now supported on MySQL
|
||||||
8.0.24+.
|
8.0.24+.
|
||||||
|
|
||||||
|
* :class:`~django.contrib.gis.geoip2.GeoIP2` now allows querying using
|
||||||
|
:class:`ipaddress.IPv4Address` or :class:`ipaddress.IPv6Address` objects.
|
||||||
|
|
||||||
:mod:`django.contrib.messages`
|
:mod:`django.contrib.messages`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import ipaddress
|
||||||
import itertools
|
import itertools
|
||||||
import pathlib
|
import pathlib
|
||||||
from unittest import mock, skipUnless
|
from unittest import mock, skipUnless
|
||||||
@ -25,15 +26,20 @@ def build_geoip_path(*parts):
|
|||||||
)
|
)
|
||||||
class GeoLite2Test(SimpleTestCase):
|
class GeoLite2Test(SimpleTestCase):
|
||||||
fqdn = "sky.uk"
|
fqdn = "sky.uk"
|
||||||
ipv4 = "2.125.160.216"
|
ipv4_str = "2.125.160.216"
|
||||||
ipv6 = "::ffff:027d:a0d8"
|
ipv6_str = "::ffff:027d:a0d8"
|
||||||
|
ipv4_addr = ipaddress.ip_address(ipv4_str)
|
||||||
|
ipv6_addr = ipaddress.ip_address(ipv6_str)
|
||||||
|
query_values = (fqdn, ipv4_str, ipv6_str, ipv4_addr, ipv6_addr)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
# Avoid referencing __file__ at module level.
|
# Avoid referencing __file__ at module level.
|
||||||
cls.enterClassContext(override_settings(GEOIP_PATH=build_geoip_path()))
|
cls.enterClassContext(override_settings(GEOIP_PATH=build_geoip_path()))
|
||||||
# Always mock host lookup to avoid test breakage if DNS changes.
|
# Always mock host lookup to avoid test breakage if DNS changes.
|
||||||
cls.enterClassContext(mock.patch("socket.gethostbyname", return_value=cls.ipv4))
|
cls.enterClassContext(
|
||||||
|
mock.patch("socket.gethostbyname", return_value=cls.ipv4_str)
|
||||||
|
)
|
||||||
|
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
@ -86,7 +92,10 @@ class GeoLite2Test(SimpleTestCase):
|
|||||||
|
|
||||||
functions += (g.country, g.country_code, g.country_name)
|
functions += (g.country, g.country_code, g.country_name)
|
||||||
values = (123, 123.45, b"", (), [], {}, set(), frozenset(), GeoIP2)
|
values = (123, 123.45, b"", (), [], {}, set(), frozenset(), GeoIP2)
|
||||||
msg = "GeoIP query must be a string, not type"
|
msg = (
|
||||||
|
"GeoIP query must be a string or instance of IPv4Address or IPv6Address, "
|
||||||
|
"not type"
|
||||||
|
)
|
||||||
for function, value in itertools.product(functions, values):
|
for function, value in itertools.product(functions, values):
|
||||||
with self.subTest(function=function.__qualname__, type=type(value)):
|
with self.subTest(function=function.__qualname__, type=type(value)):
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
@ -94,7 +103,7 @@ class GeoLite2Test(SimpleTestCase):
|
|||||||
|
|
||||||
def test_country(self):
|
def test_country(self):
|
||||||
g = GeoIP2(city="<invalid>")
|
g = GeoIP2(city="<invalid>")
|
||||||
for query in (self.fqdn, self.ipv4, self.ipv6):
|
for query in self.query_values:
|
||||||
with self.subTest(query=query):
|
with self.subTest(query=query):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
g.country(query),
|
g.country(query),
|
||||||
@ -108,7 +117,7 @@ class GeoLite2Test(SimpleTestCase):
|
|||||||
|
|
||||||
def test_city(self):
|
def test_city(self):
|
||||||
g = GeoIP2(country="<invalid>")
|
g = GeoIP2(country="<invalid>")
|
||||||
for query in (self.fqdn, self.ipv4, self.ipv6):
|
for query in self.query_values:
|
||||||
with self.subTest(query=query):
|
with self.subTest(query=query):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
g.city(query),
|
g.city(query),
|
||||||
@ -188,15 +197,17 @@ class GeoLite2Test(SimpleTestCase):
|
|||||||
|
|
||||||
def test_check_query(self):
|
def test_check_query(self):
|
||||||
g = GeoIP2()
|
g = GeoIP2()
|
||||||
self.assertEqual(g._check_query(self.ipv4), self.ipv4)
|
self.assertEqual(g._check_query(self.fqdn), self.ipv4_str)
|
||||||
self.assertEqual(g._check_query(self.ipv6), self.ipv6)
|
self.assertEqual(g._check_query(self.ipv4_str), self.ipv4_str)
|
||||||
self.assertEqual(g._check_query(self.fqdn), self.ipv4)
|
self.assertEqual(g._check_query(self.ipv6_str), self.ipv6_str)
|
||||||
|
self.assertEqual(g._check_query(self.ipv4_addr), self.ipv4_addr)
|
||||||
|
self.assertEqual(g._check_query(self.ipv6_addr), self.ipv6_addr)
|
||||||
|
|
||||||
def test_coords_deprecation_warning(self):
|
def test_coords_deprecation_warning(self):
|
||||||
g = GeoIP2()
|
g = GeoIP2()
|
||||||
msg = "GeoIP2.coords() is deprecated. Use GeoIP2.lon_lat() instead."
|
msg = "GeoIP2.coords() is deprecated. Use GeoIP2.lon_lat() instead."
|
||||||
with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
|
with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
|
||||||
e1, e2 = g.coords(self.ipv4)
|
e1, e2 = g.coords(self.ipv4_str)
|
||||||
self.assertIsInstance(e1, float)
|
self.assertIsInstance(e1, float)
|
||||||
self.assertIsInstance(e2, float)
|
self.assertIsInstance(e2, float)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user