diff --git a/mainsite/settings/__init__.py b/mainsite/settings/__init__.py index f6cba5b4..8e6db0d3 100644 --- a/mainsite/settings/__init__.py +++ b/mainsite/settings/__init__.py @@ -163,6 +163,9 @@ set_option("DATA_QUALITY_MIN_PREFIXLEN_V6", 64) # maximum value to allow for prefix length on a v6 prefix set_option("DATA_QUALITY_MAX_PREFIXLEN_V6", 116) +# maximum value to allow for irr set hierarchy depth +set_option("DATA_QUALITY_MAX_IRR_DEPTH", 3) + RATELIMITS = { "request_login_POST": "4/m", "request_translation": "2/m", diff --git a/peeringdb_server/inet.py b/peeringdb_server/inet.py index 7530b253..87e9b2b2 100644 --- a/peeringdb_server/inet.py +++ b/peeringdb_server/inet.py @@ -120,6 +120,7 @@ class RdapLookup(rdap.RdapClient): config = dict( bootstrap_url=settings.RDAP_URL.rstrip("/"), lacnic_apikey=settings.RDAP_LACNIC_APIKEY, + timeout=2.5, ) super(RdapLookup, self).__init__(config) diff --git a/peeringdb_server/validators.py b/peeringdb_server/validators.py index 80d1b39e..f85f4b55 100644 --- a/peeringdb_server/validators.py +++ b/peeringdb_server/validators.py @@ -180,23 +180,62 @@ def validate_irr_as_set(value): for item in value.split(","): item = item.upper() source = None + as_set = None # @ - parts_match = re.match("^(AS|RS)-([\w\d\-]+)@(\w+)$", item) + parts_match = re.match("^([\w\d\-:]+)@(\w+)$", item) if parts_match: - source = parts_match.group(3) + source = parts_match.group(2) + as_set = parts_match.group(1) # :: else: - parts_match = re.match("^(\w+)::(AS|RS)-([\w\d\-]+)$", item) + parts_match = re.match("^(\w+)::([\w\d\-:]+)$", item) if parts_match: source = parts_match.group(1) + as_set = parts_match.group(2) else: raise ValidationError(_("Invalid formatting: {} - should be AS-SET@SOURCE or SOURCE::AS-SET").format(item)) if source not in IRR_SOURCE: raise ValidationError(_("Unknown IRR source: {}").format(source)) + + # validate set name and as hierarchy + as_parts = as_set.split(":") + + if len(as_parts) > settings.DATA_QUALITY_MAX_IRR_DEPTH: + raise ValidationError( + _("Maximum AS-SET hierarchy depth: {}").format( + settings.DATA_QUALITY_MAX_IRR_DEPTH + ) + ) + + set_found = False + typ = None + types = [] + + for part in as_parts: + match_set = re.match("^(AS|RS)-[\w\d\-]+$", part) + match_as = re.match("^(AS)[\d]+$", part) + + # set name found + + if match_set: + set_found = True + types.append(match_set.group(1)) + elif not match_as: + raise ValidationError(_("Invalid formatting: {} - should be RS-SET, AS-SET or AS123").format(part)) + + + if len(list(set(types))) > 1: + raise ValidationError( + _("All parts of an hierarchical name have to be of the same type") + ) + + if not set_found and len(as_parts) > 1: + raise ValidationError(_("At least one component must be an actual set name")) + validated.append(item) return " ".join(validated) diff --git a/tests/django_init.py b/tests/django_init.py index dd9877f5..bcd58afb 100644 --- a/tests/django_init.py +++ b/tests/django_init.py @@ -154,6 +154,7 @@ settings.configure( DATA_QUALITY_MAX_PREFIXLEN_V4=28, DATA_QUALITY_MIN_PREFIXLEN_V6=64, DATA_QUALITY_MAX_PREFIXLEN_V6=116, + DATA_QUALITY_MAX_IRR_DEPTH=3, TUTORIAL_MODE=False, CAPTCHA_TEST_MODE=True, SITE_ID=1, diff --git a/tests/test_validators.py b/tests/test_validators.py index 491ee514..4a4a2985 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -156,11 +156,20 @@ def test_validate_prefix_overlap(): # success validation ("RIPE::AS-FOO", "RIPE::AS-FOO"), ("AS-FOO@RIPE", "AS-FOO@RIPE"), + ("AS-FOO-BAR@RIPE", "AS-FOO-BAR@RIPE"), ("ripe::as-foo", "RIPE::AS-FOO"), ("as-foo@ripe", "AS-FOO@RIPE"), ("as-foo@ripe as-bar@ripe", "AS-FOO@RIPE AS-BAR@RIPE"), ("as-foo@ripe,as-bar@ripe", "AS-FOO@RIPE AS-BAR@RIPE"), ("as-foo@ripe, as-bar@ripe", "AS-FOO@RIPE AS-BAR@RIPE"), + ( + "RIPE::AS12345:AS-FOO RIPE::AS12345:AS-FOO:AS9876", + "RIPE::AS12345:AS-FOO RIPE::AS12345:AS-FOO:AS9876" + ), + ("ripe::as-foo:as123:as345", "RIPE::AS-FOO:AS123:AS345"), + ("RIPE::AS12345", "RIPE::AS12345"), + ("AS12345@RIPE", "AS12345@RIPE"), + ("RIPE::AS123456:RS-FOO", "RIPE::AS123456:RS-FOO"), # fail validation ("AS-FOO", False), @@ -170,6 +179,11 @@ def test_validate_prefix_overlap(): ("UNKNOWN::ASFOO", False), ("AS-FOO RIPE:AS-FOO", False), ("AS-FOO AS-FOO@RIPE", False), + ("RIPE::RS15562:RS-FOO", False), + ("RIPE::AS123456:RS-FOO:AS-FOO", False), + + # > DATA_QUALITY_MAX_IRR_DEPTH + ("ripe::as-foo:as123:as345:as678", False), ]) def test_validate_irr_as_set(value, validated): if not validated: