Skip to content

Commit

Permalink
Fixed #34818 -- Prevented GenericIPAddressField from mutating error m…
Browse files Browse the repository at this point in the history
…essages.

Co-authored-by: Parth Verma <[email protected]>
  • Loading branch information
parth-verma and Parth Verma authored Nov 24, 2023
1 parent a8adb6a commit eabfa2d
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 15 deletions.
18 changes: 11 additions & 7 deletions django/core/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,18 @@ def validate_ipv4_address(value):
ipaddress.IPv4Address(value)
except ValueError:
raise ValidationError(
_("Enter a valid IPv4 address."), code="invalid", params={"value": value}
_("Enter a valid %(protocol)s address."),
code="invalid",
params={"protocol": _("IPv4"), "value": value},
)


def validate_ipv6_address(value):
if not is_valid_ipv6_address(value):
raise ValidationError(
_("Enter a valid IPv6 address."), code="invalid", params={"value": value}
_("Enter a valid %(protocol)s address."),
code="invalid",
params={"protocol": _("IPv6"), "value": value},
)


Expand All @@ -295,16 +299,16 @@ def validate_ipv46_address(value):
validate_ipv6_address(value)
except ValidationError:
raise ValidationError(
_("Enter a valid IPv4 or IPv6 address."),
_("Enter a valid %(protocol)s address."),
code="invalid",
params={"value": value},
params={"protocol": _("IPv4 or IPv6"), "value": value},
)


ip_address_validator_map = {
"both": ([validate_ipv46_address], _("Enter a valid IPv4 or IPv6 address.")),
"ipv4": ([validate_ipv4_address], _("Enter a valid IPv4 address.")),
"ipv6": ([validate_ipv6_address], _("Enter a valid IPv6 address.")),
"both": [validate_ipv46_address],
"ipv4": [validate_ipv4_address],
"ipv6": [validate_ipv6_address],
}


Expand Down
10 changes: 4 additions & 6 deletions django/db/models/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2201,7 +2201,7 @@ def get_internal_type(self):
class GenericIPAddressField(Field):
empty_strings_allowed = False
description = _("IP address")
default_error_messages = {}
default_error_messages = {"invalid": _("Enter a valid %(protocol)s address.")}

def __init__(
self,
Expand All @@ -2214,11 +2214,9 @@ def __init__(
):
self.unpack_ipv4 = unpack_ipv4
self.protocol = protocol
(
self.default_validators,
invalid_error_message,
) = validators.ip_address_validators(protocol, unpack_ipv4)
self.default_error_messages["invalid"] = invalid_error_message
self.default_validators = validators.ip_address_validators(
protocol, unpack_ipv4
)
kwargs["max_length"] = 39
super().__init__(verbose_name, name, *args, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion django/forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ def __init__(self, *, protocol="both", unpack_ipv4=False, **kwargs):
self.unpack_ipv4 = unpack_ipv4
self.default_validators = validators.ip_address_validators(
protocol, unpack_ipv4
)[0]
)
super().__init__(**kwargs)

def to_python(self, value):
Expand Down
4 changes: 3 additions & 1 deletion django/utils/ipv6.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def clean_ipv6_address(
try:
addr = ipaddress.IPv6Address(int(ipaddress.IPv6Address(ip_str)))
except ValueError:
raise ValidationError(error_message, code="invalid")
raise ValidationError(
error_message, code="invalid", params={"protocol": _("IPv6")}
)

if unpack_ipv4 and addr.ipv4_mapped:
return str(addr.ipv4_mapped)
Expand Down
14 changes: 14 additions & 0 deletions tests/validation/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,17 @@ def test_empty_generic_ip_passes(self):
self.assertIsNone(giptm.full_clean())
giptm = GenericIPAddressTestModel(generic_ip=None)
self.assertIsNone(giptm.full_clean())

def test_multiple_invalid_ip_raises_error(self):
giptm = GenericIPAddressTestModel(
v6_ip="1.2.3.4", v4_ip="::ffff:10.10.10.10", generic_ip="fsad"
)
self.assertFieldFailsValidationWithMessage(
giptm.full_clean, "v6_ip", ["Enter a valid IPv6 address."]
)
self.assertFieldFailsValidationWithMessage(
giptm.full_clean, "v4_ip", ["Enter a valid IPv4 address."]
)
self.assertFieldFailsValidationWithMessage(
giptm.full_clean, "generic_ip", ["Enter a valid IPv4 or IPv6 address."]
)

0 comments on commit eabfa2d

Please sign in to comment.