1
0
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:
Stefan Pratter
2019-02-07 08:00:22 +00:00
parent a62a546a2c
commit a14556f07b
8 changed files with 180 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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",

View File

@ -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()

View File

@ -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")

View File

@ -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}

View File

@ -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)