mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
handle bogon asns (#419)
This commit is contained in:
@ -5,9 +5,38 @@ import rdap
|
|||||||
from rdap import RdapAsn
|
from rdap import RdapAsn
|
||||||
from rdap.exceptions import RdapException, RdapHTTPError, RdapNotFoundError
|
from rdap.exceptions import RdapException, RdapHTTPError, RdapNotFoundError
|
||||||
import requests
|
import requests
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from peeringdb_server import settings
|
from peeringdb_server import settings
|
||||||
|
|
||||||
|
BOGON_ASN_RANGES = [
|
||||||
|
# RFC 5398 - documentation 16-bit
|
||||||
|
(64496, 64511),
|
||||||
|
# RFC 5398 - documentation 32-bit
|
||||||
|
(65536, 65551),
|
||||||
|
# RFC 6996 - private 16-bit
|
||||||
|
(64512, 65534),
|
||||||
|
# RFC 6996 - private 32-bit
|
||||||
|
(4200000000, 4294967294),
|
||||||
|
]
|
||||||
|
|
||||||
|
class BogonAsn(rdap.RdapAsn):
|
||||||
|
|
||||||
|
"""
|
||||||
|
On tutorial mode environments we will return an instance
|
||||||
|
of this to provide an rdapasn result for asns in the
|
||||||
|
private and documentation ranges
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, asn):
|
||||||
|
name = "AS{}".format(asn)
|
||||||
|
self._parsed = {
|
||||||
|
"name":name,
|
||||||
|
"org_name":name,
|
||||||
|
"org_address":None,
|
||||||
|
"emails":[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RdapLookup(rdap.RdapClient):
|
class RdapLookup(rdap.RdapClient):
|
||||||
"""
|
"""
|
||||||
@ -23,6 +52,42 @@ class RdapLookup(rdap.RdapClient):
|
|||||||
super(RdapLookup, self).__init__(config)
|
super(RdapLookup, self).__init__(config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_asn(self, asn):
|
||||||
|
"""
|
||||||
|
We handle asns that fall into the private/documentation ranges
|
||||||
|
manually - others are processed normally through rdap lookup
|
||||||
|
"""
|
||||||
|
|
||||||
|
if asn_is_bogon(asn):
|
||||||
|
if settings.TUTORIAL_MODE:
|
||||||
|
return BogonAsn(asn)
|
||||||
|
else:
|
||||||
|
raise RdapException(_("ASNs for documentation/private purposes " \
|
||||||
|
"are not allowed in this environment"))
|
||||||
|
return super(RdapLookup, self).get_asn(asn)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def asn_is_bogon(asn):
|
||||||
|
"""
|
||||||
|
Test if an asn is bogon by being either in the documentation
|
||||||
|
or private asn ranges
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- asn<int>
|
||||||
|
|
||||||
|
Return:
|
||||||
|
- bool: True if in bogon range
|
||||||
|
"""
|
||||||
|
|
||||||
|
asn = int(asn)
|
||||||
|
for as_range in BOGON_ASN_RANGES:
|
||||||
|
if asn >= as_range[0] and asn <= as_range[1]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def network_is_bogon(network):
|
def network_is_bogon(network):
|
||||||
"""
|
"""
|
||||||
Returns if the passed ipaddress network is a bogon
|
Returns if the passed ipaddress network is a bogon
|
||||||
|
@ -27,6 +27,7 @@ from peeringdb_server.models import (
|
|||||||
IXLanPrefix, InternetExchangeFacility)
|
IXLanPrefix, InternetExchangeFacility)
|
||||||
|
|
||||||
from peeringdb_server.serializers import REFTAG_MAP as REFTAG_MAP_SLZ
|
from peeringdb_server.serializers import REFTAG_MAP as REFTAG_MAP_SLZ
|
||||||
|
from peeringdb_server import inet, settings as pdb_settings
|
||||||
|
|
||||||
START_TIMESTAMP = time.time()
|
START_TIMESTAMP = time.time()
|
||||||
|
|
||||||
@ -1033,6 +1034,32 @@ class TestJSON(unittest.TestCase):
|
|||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
def test_org_admin_002_POST_net_bogon_asn(self):
|
||||||
|
|
||||||
|
# Test bogon asn failure
|
||||||
|
|
||||||
|
data = self.make_data_net()
|
||||||
|
for bogon_asn in inet.BOGON_ASN_RANGES:
|
||||||
|
r_data = self.assert_create(
|
||||||
|
self.db_org_admin, "net", data,
|
||||||
|
test_failures={"invalid": {
|
||||||
|
"asn": bogon_asn[0]
|
||||||
|
}}, test_success=False)
|
||||||
|
|
||||||
|
# server running in tutorial mode should be allowed
|
||||||
|
# to create networks with bogon asns, so we test that
|
||||||
|
# as well
|
||||||
|
|
||||||
|
pdb_settings.TUTORIAL_MODE = True
|
||||||
|
|
||||||
|
for bogon_asn in inet.BOGON_ASN_RANGES:
|
||||||
|
data = self.make_data_net(asn=bogon_asn[0])
|
||||||
|
r_data = self.assert_create(self.db_org_admin, "net", data)
|
||||||
|
|
||||||
|
pdb_settings.TUTORIAL_MODE = False
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
def test_org_admin_002_PUT_net_write_only_fields(self):
|
def test_org_admin_002_PUT_net_write_only_fields(self):
|
||||||
"""
|
"""
|
||||||
with this we check that certain fields that are allowed to be
|
with this we check that certain fields that are allowed to be
|
||||||
|
@ -199,9 +199,8 @@ def uoar_creation(sender, instance, created=False, **kwargs):
|
|||||||
rdap_lookup = rdap = RdapLookup().get_asn(instance.asn)
|
rdap_lookup = rdap = RdapLookup().get_asn(instance.asn)
|
||||||
ok = rdap_lookup.emails
|
ok = rdap_lookup.emails
|
||||||
except RdapException, inst:
|
except RdapException, inst:
|
||||||
if not pdb_settings.AUTO_APPROVE_AFFILIATION:
|
instance.deny()
|
||||||
instance.deny()
|
raise
|
||||||
raise
|
|
||||||
|
|
||||||
# create organization
|
# create organization
|
||||||
instance.org, org_created = Organization.create_from_rdap(
|
instance.org, org_created = Organization.create_from_rdap(
|
||||||
|
@ -156,7 +156,7 @@ settings.configure(
|
|||||||
DATA_QUALITY_MAX_PREFIX_V6_LIMIT=500000,
|
DATA_QUALITY_MAX_PREFIX_V6_LIMIT=500000,
|
||||||
TUTORIAL_MODE=False,
|
TUTORIAL_MODE=False,
|
||||||
RATELIMITS={
|
RATELIMITS={
|
||||||
"view_affiliate_to_org_POST": "3/m",
|
"view_affiliate_to_org_POST": "100/m",
|
||||||
"resend_confirmation_mail": "2/m",
|
"resend_confirmation_mail": "2/m",
|
||||||
"view_request_ownership_GET": "3/m",
|
"view_request_ownership_GET": "3/m",
|
||||||
"view_username_retrieve_initiate": "2/m",
|
"view_username_retrieve_initiate": "2/m",
|
||||||
|
@ -41,6 +41,8 @@ def setup_module(module):
|
|||||||
def get_asn(self, asn):
|
def get_asn(self, asn):
|
||||||
if asn in ASN_RANGE_OVERRIDE:
|
if asn in ASN_RANGE_OVERRIDE:
|
||||||
return pdbinet.RdapAsn(self.override_result)
|
return pdbinet.RdapAsn(self.override_result)
|
||||||
|
elif pdbinet.asn_is_bogon(asn):
|
||||||
|
return RdapLookup_get_asn(self, asn)
|
||||||
else:
|
else:
|
||||||
raise pdbinet.RdapNotFoundError()
|
raise pdbinet.RdapNotFoundError()
|
||||||
|
|
||||||
|
@ -3,12 +3,16 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
import peeringdb_server.models as models
|
import peeringdb_server.models as models
|
||||||
import peeringdb_server.views as pdbviews
|
import peeringdb_server.views as pdbviews
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.test import TestCase, Client, RequestFactory
|
from django.test import TestCase, Client, RequestFactory
|
||||||
|
|
||||||
import peeringdb_server.inet as pdbinet
|
import peeringdb_server.inet as pdbinet
|
||||||
|
from util import SettingsCase
|
||||||
|
|
||||||
ERR_COULD_NOT_GET_RIR_ENTRY = "RDAP Lookup Error: Test Not Found"
|
ERR_COULD_NOT_GET_RIR_ENTRY = "RDAP Lookup Error: Test Not Found"
|
||||||
|
ERR_BOGON_ASN = "RDAP Lookup Error: ASNs for documentation/private purposes " \
|
||||||
|
"are not allowed in this environment"
|
||||||
|
|
||||||
RdapLookup_get_asn = pdbinet.RdapLookup.get_asn
|
RdapLookup_get_asn = pdbinet.RdapLookup.get_asn
|
||||||
|
|
||||||
@ -39,6 +43,8 @@ def setup_module(module):
|
|||||||
r._parsed["name"] = "AS%d" % asn
|
r._parsed["name"] = "AS%d" % asn
|
||||||
r._parsed["org_name"] = "ORG AS%d" % asn
|
r._parsed["org_name"] = "ORG AS%d" % asn
|
||||||
return r
|
return r
|
||||||
|
elif pdbinet.asn_is_bogon(asn):
|
||||||
|
return RdapLookup_get_asn(self, asn)
|
||||||
else:
|
else:
|
||||||
raise pdbinet.RdapNotFoundError("Test Not Found")
|
raise pdbinet.RdapNotFoundError("Test Not Found")
|
||||||
|
|
||||||
@ -204,6 +210,24 @@ class AsnAutomationTestCase(TestCase):
|
|||||||
self.assertEqual(net.status, "ok")
|
self.assertEqual(net.status, "ok")
|
||||||
self.assertEqual(net.org.status, "ok")
|
self.assertEqual(net.org.status, "ok")
|
||||||
|
|
||||||
|
def test_affiliate_to_bogon_asn(self):
|
||||||
|
"""
|
||||||
|
tests affiliation with non-existant asn
|
||||||
|
"""
|
||||||
|
asns = []
|
||||||
|
for a,b in pdbinet.BOGON_ASN_RANGES:
|
||||||
|
asns.extend([a,b])
|
||||||
|
|
||||||
|
for asn in asns:
|
||||||
|
request = self.factory.post("/affiliate-to-org", data={
|
||||||
|
"asn": asn})
|
||||||
|
|
||||||
|
request.user = self.user_a
|
||||||
|
request._dont_enforce_csrf_checks = True
|
||||||
|
resp = json.loads(pdbviews.view_affiliate_to_org(request).content)
|
||||||
|
self.assertEqual(resp.get("asn"), ERR_BOGON_ASN)
|
||||||
|
|
||||||
|
|
||||||
def test_claim_ownership(self):
|
def test_claim_ownership(self):
|
||||||
"""
|
"""
|
||||||
tests ownership to org via asn RiR validation
|
tests ownership to org via asn RiR validation
|
||||||
@ -243,3 +267,31 @@ class AsnAutomationTestCase(TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.user_b.groups.filter(name=org.admin_usergroup.name).exists(),
|
self.user_b.groups.filter(name=org.admin_usergroup.name).exists(),
|
||||||
False)
|
False)
|
||||||
|
|
||||||
|
class TestTutorialMode(SettingsCase):
|
||||||
|
settings = {"TUTORIAL_MODE":True}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTutorialMode, self).setUp()
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
def test_affiliate_to_bogon_asn(self):
|
||||||
|
"""
|
||||||
|
tests affiliation with non-existant bogon asn
|
||||||
|
with tutorial mode enabled those should be allowed
|
||||||
|
"""
|
||||||
|
user = get_user_model().objects.create_user("user_a", "user_a@localhost", "user_a")
|
||||||
|
asns = []
|
||||||
|
for a,b in pdbinet.BOGON_ASN_RANGES:
|
||||||
|
asns.extend([a,b])
|
||||||
|
|
||||||
|
for asn in asns:
|
||||||
|
request = self.factory.post("/affiliate-to-org", data={
|
||||||
|
"asn": asn})
|
||||||
|
|
||||||
|
request.user = user
|
||||||
|
request._dont_enforce_csrf_checks = True
|
||||||
|
resp = json.loads(pdbviews.view_affiliate_to_org(request).content)
|
||||||
|
self.assertEqual(resp.get("status"), "ok")
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,36 +3,11 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
from util import ClientCase
|
from util import SettingsCase
|
||||||
from peeringdb_server import signals, models, serializers
|
from peeringdb_server import signals, models, serializers
|
||||||
from peeringdb_server import settings as pdb_settings
|
from peeringdb_server import settings as pdb_settings
|
||||||
|
|
||||||
|
|
||||||
class SettingsCase(ClientCase):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Since we read settings from peeringdb_server.settings
|
|
||||||
we can't use the `settings` fixture from pytest-django
|
|
||||||
|
|
||||||
This class instead does something similar for peeringdb_server.settings,
|
|
||||||
where it will override settings specified and then reset after test case
|
|
||||||
is finished
|
|
||||||
"""
|
|
||||||
|
|
||||||
settings = {}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUp(cls):
|
|
||||||
cls._restore = {}
|
|
||||||
for k,v in cls.settings.items():
|
|
||||||
cls._restore[k] = getattr(pdb_settings, k)
|
|
||||||
setattr(pdb_settings, k, v)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDown(cls):
|
|
||||||
for k,v in cls._restore.items():
|
|
||||||
setattr(pdb_settings, k, v)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAutoVerifyUser(SettingsCase):
|
class TestAutoVerifyUser(SettingsCase):
|
||||||
settings = {"AUTO_VERIFY_USERS":True}
|
settings = {"AUTO_VERIFY_USERS":True}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth.models import Group, AnonymousUser
|
from django.contrib.auth.models import Group, AnonymousUser
|
||||||
|
from django.conf import settings
|
||||||
import peeringdb_server.models as models
|
import peeringdb_server.models as models
|
||||||
import django_namespace_perms as nsp
|
import django_namespace_perms as nsp
|
||||||
|
from peeringdb_server import settings as pdb_settings
|
||||||
|
|
||||||
|
|
||||||
class ClientCase(TestCase):
|
class ClientCase(TestCase):
|
||||||
@ -33,3 +35,31 @@ class ClientCase(TestCase):
|
|||||||
group=guest_group,
|
group=guest_group,
|
||||||
namespace="peeringdb.organization.*.network.*.poc_set.public",
|
namespace="peeringdb.organization.*.network.*.poc_set.public",
|
||||||
permissions=0x01)
|
permissions=0x01)
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsCase(ClientCase):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Since we read settings from peeringdb_server.settings
|
||||||
|
we can't use the `settings` fixture from pytest-django
|
||||||
|
|
||||||
|
This class instead does something similar for peeringdb_server.settings,
|
||||||
|
where it will override settings specified and then reset after test case
|
||||||
|
is finished
|
||||||
|
"""
|
||||||
|
|
||||||
|
settings = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUp(cls):
|
||||||
|
cls._restore = {}
|
||||||
|
for k,v in cls.settings.items():
|
||||||
|
cls._restore[k] = getattr(pdb_settings, k)
|
||||||
|
setattr(pdb_settings, k, v)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDown(cls):
|
||||||
|
for k,v in cls._restore.items():
|
||||||
|
setattr(pdb_settings, k, v)
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user