mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
validate netixlan ip against parent prefixes (#8)
This commit is contained in:
@ -100,6 +100,30 @@ EMAIL = "test@20c.com"
|
|||||||
|
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
|
|
||||||
|
PREFIXES_V4 = [
|
||||||
|
u"206.223.114.0/24",
|
||||||
|
u"206.223.115.0/24",
|
||||||
|
u"206.223.116.0/24",
|
||||||
|
u"206.223.117.0/24",
|
||||||
|
u"206.223.118.0/24",
|
||||||
|
u"206.223.119.0/24",
|
||||||
|
u"206.223.120.0/24",
|
||||||
|
u"206.223.121.0/24",
|
||||||
|
u"206.223.122.0/24",
|
||||||
|
]
|
||||||
|
|
||||||
|
PREFIXES_V6 = [
|
||||||
|
u"2001:504:0:1::/64",
|
||||||
|
u"2001:504:0:2::/64",
|
||||||
|
u"2001:504:0:3::/64",
|
||||||
|
u"2001:504:0:4::/64",
|
||||||
|
u"2001:504:0:5::/64",
|
||||||
|
u"2001:504:0:6::/64",
|
||||||
|
u"2001:504:0:7::/64",
|
||||||
|
u"2001:504:0:8::/64",
|
||||||
|
u"2001:504:0:9::/64",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestJSON(unittest.TestCase):
|
class TestJSON(unittest.TestCase):
|
||||||
|
|
||||||
@ -110,17 +134,32 @@ class TestJSON(unittest.TestCase):
|
|||||||
IP6_COUNT = 1
|
IP6_COUNT = 1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ip6(cls):
|
def get_ip6(cls, ixlan):
|
||||||
r = u"2001:7f8:4::1154:%d" % cls.IP6_COUNT
|
hosts = []
|
||||||
|
for host in ixlan.ixpfx_set.filter(status=ixlan.status, protocol=6).first().prefix.hosts():
|
||||||
|
if len(hosts) < 100:
|
||||||
|
hosts.append(host)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
r = u"{}".format(hosts[cls.IP6_COUNT])
|
||||||
cls.IP6_COUNT += 1
|
cls.IP6_COUNT += 1
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ip4(cls):
|
def get_ip4(cls, ixlan):
|
||||||
r = u"1.1.1.%d" % cls.IP4_COUNT
|
hosts = []
|
||||||
|
for host in ixlan.ixpfx_set.filter(status=ixlan.status, protocol=4).first().prefix.hosts():
|
||||||
|
if len(hosts) < 100:
|
||||||
|
hosts.append(host)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
r = u"{}".format(hosts[cls.IP4_COUNT])
|
||||||
cls.IP4_COUNT += 1
|
cls.IP4_COUNT += 1
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_prefix4(cls):
|
def get_prefix4(cls):
|
||||||
r = u"206.41.{}.0/24".format(cls.PREFIX_COUNT)
|
r = u"206.41.{}.0/24".format(cls.PREFIX_COUNT)
|
||||||
@ -318,14 +357,17 @@ class TestJSON(unittest.TestCase):
|
|||||||
"notes": NOTE,
|
"notes": NOTE,
|
||||||
"speed": 30000,
|
"speed": 30000,
|
||||||
"asn": 12345,
|
"asn": 12345,
|
||||||
"ipaddr4": self.get_ip4(),
|
|
||||||
"ipaddr6": self.get_ip6()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data.update(**kwargs)
|
data.update(**kwargs)
|
||||||
for k, v in rename.items():
|
for k, v in rename.items():
|
||||||
data[v] = data[k]
|
data[v] = data[k]
|
||||||
del data[k]
|
del data[k]
|
||||||
|
|
||||||
|
data.update(
|
||||||
|
ipaddr4=self.get_ip4(IXLan.objects.get(id=data["ixlan_id"])),
|
||||||
|
ipaddr6=self.get_ip6(IXLan.objects.get(id=data["ixlan_id"])),
|
||||||
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -615,6 +657,20 @@ class TestJSON(unittest.TestCase):
|
|||||||
SHARED["%s_r_ok_public" % target].id,
|
SHARED["%s_r_ok_public" % target].id,
|
||||||
SHARED["%s_rw_ok_public" % target].id
|
SHARED["%s_rw_ok_public" % target].id
|
||||||
]
|
]
|
||||||
|
elif target == "ixpfx":
|
||||||
|
|
||||||
|
valid_s = [
|
||||||
|
SHARED["%s_r_ok" % target].id,
|
||||||
|
SHARED["%s_r_v6_ok" % target].id,
|
||||||
|
]
|
||||||
|
|
||||||
|
valid_m = [
|
||||||
|
SHARED["%s_r_ok" % target].id,
|
||||||
|
SHARED["%s_rw_ok" % target].id,
|
||||||
|
SHARED["%s_r_v6_ok" % target].id,
|
||||||
|
SHARED["%s_rw_v6_ok" % target].id,
|
||||||
|
]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
valid_s = [SHARED["%s_r_ok" % target].id]
|
valid_s = [SHARED["%s_r_ok" % target].id]
|
||||||
@ -1329,12 +1385,13 @@ class TestJSON(unittest.TestCase):
|
|||||||
},
|
},
|
||||||
"perms": {
|
"perms": {
|
||||||
# set network to one the user doesnt have perms to
|
# set network to one the user doesnt have perms to
|
||||||
"ipaddr4": self.get_ip4(),
|
"ipaddr4": self.get_ip4(SHARED["ixlan_rw_ok"]),
|
||||||
"ipaddr6": self.get_ip6(),
|
"ipaddr6": self.get_ip6(SHARED["ixlan_rw_ok"]),
|
||||||
"net_id": SHARED["net_r_ok"].id
|
"net_id": SHARED["net_r_ok"].id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
SHARED["netixlan_id"] = r_data.get("id")
|
SHARED["netixlan_id"] = r_data.get("id")
|
||||||
|
|
||||||
self.assert_update(self.db_org_admin, "netixlan",
|
self.assert_update(self.db_org_admin, "netixlan",
|
||||||
@ -1352,6 +1409,26 @@ class TestJSON(unittest.TestCase):
|
|||||||
test_success=SHARED["netixlan_id"],
|
test_success=SHARED["netixlan_id"],
|
||||||
test_failure=SHARED["netixlan_r_ok"].id)
|
test_failure=SHARED["netixlan_r_ok"].id)
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def test_org_admin_002_POST_PUT_netixlan_validation(self):
|
||||||
|
data = self.make_data_netixlan(net_id=SHARED["net_rw_ok"].id,
|
||||||
|
ixlan_id=SHARED["ixlan_rw_ok"].id)
|
||||||
|
|
||||||
|
test_failures = [
|
||||||
|
# test failure if ip4 not in prefix
|
||||||
|
{"invalid": { "ipaddr4": self.get_ip4(SHARED["ixlan_r_ok"]) }},
|
||||||
|
# test failure if ip6 not in prefix
|
||||||
|
{"invalid": { "ipaddr6": self.get_ip6(SHARED["ixlan_r_ok"]) }},
|
||||||
|
]
|
||||||
|
|
||||||
|
for test_failure in test_failures:
|
||||||
|
self.assert_create(self.db_org_admin, "netixlan", data,
|
||||||
|
test_failures=test_failure, test_success=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def test_org_admin_002_POST_PUT_DELETE_ixfac(self):
|
def test_org_admin_002_POST_PUT_DELETE_ixfac(self):
|
||||||
@ -2719,6 +2796,12 @@ class Command(BaseCommand):
|
|||||||
if tag in ["ix", "net", "fac", "org"]:
|
if tag in ["ix", "net", "fac", "org"]:
|
||||||
data["name"] = name
|
data["name"] = name
|
||||||
|
|
||||||
|
if tag == "ixpfx":
|
||||||
|
if kwargs.get("protocol", 4) == 4:
|
||||||
|
data["prefix"] = PREFIXES_V4[model.objects.all().count()]
|
||||||
|
elif kwargs.get("protocol") == 6:
|
||||||
|
data["prefix"] = PREFIXES_V6[model.objects.all().count()]
|
||||||
|
|
||||||
data.update(**kwargs)
|
data.update(**kwargs)
|
||||||
try:
|
try:
|
||||||
obj = model.objects.get(**data)
|
obj = model.objects.get(**data)
|
||||||
@ -2886,6 +2969,14 @@ class Command(BaseCommand):
|
|||||||
IXLanPrefix,
|
IXLanPrefix,
|
||||||
status=status,
|
status=status,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
|
protocol=4,
|
||||||
|
ixlan_id=SHARED["ixlan_%s_%s" % (prefix, status)].id,
|
||||||
|
)
|
||||||
|
cls.create_entity(
|
||||||
|
IXLanPrefix,
|
||||||
|
status=status,
|
||||||
|
prefix="{}_v6".format(prefix),
|
||||||
|
protocol=6,
|
||||||
ixlan_id=SHARED["ixlan_%s_%s" % (prefix, status)].id,
|
ixlan_id=SHARED["ixlan_%s_%s" % (prefix, status)].id,
|
||||||
)
|
)
|
||||||
cls.create_entity(
|
cls.create_entity(
|
||||||
|
@ -1435,7 +1435,6 @@ class IXLan(pdb_models.IXLanBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.create_revision()
|
@reversion.create_revision()
|
||||||
def add_netixlan(self, netixlan_info, save=True, save_others=True):
|
def add_netixlan(self, netixlan_info, save=True, save_others=True):
|
||||||
"""
|
"""
|
||||||
@ -2286,11 +2285,40 @@ class NetworkIXLan(pdb_models.NetworkIXLanBase):
|
|||||||
conflict_v6 = (self.ipaddr6 and ipv6.exists())
|
conflict_v6 = (self.ipaddr6 and ipv6.exists())
|
||||||
return (conflict_v4, conflict_v6)
|
return (conflict_v4, conflict_v6)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_ipaddr4(self):
|
||||||
|
if self.ipaddr4 and not self.ixlan.test_ipv4_address(self.ipaddr4):
|
||||||
|
raise ValidationError(_("IPv4 address outside of prefix"))
|
||||||
|
|
||||||
|
def validate_ipaddr6(self):
|
||||||
|
if self.ipaddr6 and not self.ixlan.test_ipv6_address(self.ipaddr6):
|
||||||
|
raise ValidationError(_("IPv6 address outside of prefix"))
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
Custom model validation
|
Custom model validation
|
||||||
"""
|
"""
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
|
# check that the ip address can be validated agaisnt
|
||||||
|
# at least one of the prefix on the parent ixlan
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.validate_ipaddr4()
|
||||||
|
except ValidationError as exc:
|
||||||
|
errors["ipaddr4"] = exc.message
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.validate_ipaddr6()
|
||||||
|
except ValidationError as exc:
|
||||||
|
errors["ipaddr6"] = exc.message
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
# make sure this ip address is not claimed anywhere else
|
||||||
|
|
||||||
conflict_v4, conflict_v6 = self.ipaddress_conflict()
|
conflict_v4, conflict_v6 = self.ipaddress_conflict()
|
||||||
if conflict_v4:
|
if conflict_v4:
|
||||||
errors["ipaddr4"] = _("Ip address already exists elsewhere")
|
errors["ipaddr4"] = _("Ip address already exists elsewhere")
|
||||||
|
@ -8,7 +8,7 @@ from django.db.models.query import QuerySet
|
|||||||
from django.db.models import Prefetch, Q, Sum, IntegerField, Case, When
|
from django.db.models import Prefetch, Q, Sum, IntegerField, Case, When
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models.fields.related import ReverseManyToOneDescriptor, ForwardManyToOneDescriptor
|
from django.db.models.fields.related import ReverseManyToOneDescriptor, ForwardManyToOneDescriptor
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError, ValidationError
|
||||||
from rest_framework import serializers, validators
|
from rest_framework import serializers, validators
|
||||||
from rest_framework.exceptions import ValidationError as RestValidationError
|
from rest_framework.exceptions import ValidationError as RestValidationError
|
||||||
# from drf_toolbox import serializers
|
# from drf_toolbox import serializers
|
||||||
@ -1161,6 +1161,20 @@ class NetworkIXLanSerializer(ModelSerializer):
|
|||||||
def get_ix_id(self, inst):
|
def get_ix_id(self, inst):
|
||||||
return inst.ix_id
|
return inst.ix_id
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
netixlan = NetworkIXLan(**data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
netixlan.validate_ipaddr4()
|
||||||
|
except ValidationError as exc:
|
||||||
|
raise serializers.ValidationError({"ipaddr4":exc.message})
|
||||||
|
|
||||||
|
try:
|
||||||
|
netixlan.validate_ipaddr6()
|
||||||
|
except ValidationError as exc:
|
||||||
|
raise serializers.ValidationError({"ipaddr6":exc.message})
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class NetworkFacilitySerializer(ModelSerializer):
|
class NetworkFacilitySerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user