From 16268c465c8d814b6449167468a3469b88d1e546 Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand <benjamin.bertrand@esss.se> Date: Thu, 19 Dec 2019 15:53:36 +0100 Subject: [PATCH] Prevent supernet overlapping JIRA INFRA-1627 --- app/models.py | 12 ++++++++++++ app/network/views.py | 22 ++++++++++++++-------- tests/functional/test_models.py | 12 ++++++++++++ tests/functional/test_web.py | 16 ++++++++++++++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/app/models.py b/app/models.py index f2b78d9..f5a2601 100644 --- a/app/models.py +++ b/app/models.py @@ -1615,6 +1615,18 @@ class NetworkScope(CreatedMixin, db.Model): def __str__(self): return str(self.name) + @validates("supernet") + def validate_supernet(self, key, supernet): + """Ensure the supernet doesn't overlap existing supernets""" + supernet_address = ipaddress.ip_network(supernet) + existing_scopes = NetworkScope.query.all() + for existing_scope in existing_scopes: + if supernet_address.overlaps(existing_scope.supernet_ip): + raise ValidationError( + f"{supernet} overlaps {existing_scope} ({existing_scope.supernet_ip})" + ) + return supernet + @validates("networks") def validate_networks(self, key, network): """Ensure the network is included in the supernet and doesn't overlap diff --git a/app/network/views.py b/app/network/views.py index f16ecf7..d8cf18c 100644 --- a/app/network/views.py +++ b/app/network/views.py @@ -588,14 +588,20 @@ def view_scope(name): def create_scope(): form = NetworkScopeForm() if form.validate_on_submit(): - scope = models.NetworkScope( - name=form.name.data, - description=form.description.data or None, - first_vlan=form.first_vlan.data, - last_vlan=form.last_vlan.data, - supernet=form.supernet.data, - domain=models.Domain.query.get(form.domain_id.data), - ) + try: + scope = models.NetworkScope( + name=form.name.data, + description=form.description.data or None, + first_vlan=form.first_vlan.data, + last_vlan=form.last_vlan.data, + supernet=form.supernet.data, + domain=models.Domain.query.get(form.domain_id.data), + ) + except ValidationError as e: + # Check for error raised by model validation (not implemented in form vaildation) + current_app.logger.warning(f"{e}") + flash(f"{e}", "error") + return render_template("network/create_scope.html", form=form) current_app.logger.debug(f"Trying to create: {scope!r}") db.session.add(scope) try: diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py index fe5daff..31bf839 100644 --- a/tests/functional/test_models.py +++ b/tests/functional/test_models.py @@ -919,3 +919,15 @@ def test_scope_available_subnets(network_scope_factory, network_factory): network_factory(vlan_id=3802, address="172.30.238.64/26", scope=scope) expected3 = [subnet for subnet in expected2 if subnet != "172.30.238.0/24"] assert scope.available_subnets(24) == expected3 + + +@pytest.mark.parametrize("address", ("172.30.0.0/22", "172.30.244.0/22")) +def test_network_scope_overlapping(address, network_scope_factory): + scope = network_scope_factory( + first_vlan=3800, last_vlan=4000, supernet="172.30.0.0/16" + ) + with pytest.raises(ValidationError) as excinfo: + network_scope_factory(first_vlan=2000, last_vlan=2200, supernet=address) + assert f"{address} overlaps {scope.name} ({scope.supernet_ip})" in str( + excinfo.value + ) diff --git a/tests/functional/test_web.py b/tests/functional/test_web.py index 5966191..0b08160 100644 --- a/tests/functional/test_web.py +++ b/tests/functional/test_web.py @@ -788,3 +788,19 @@ def test_create_network_overlapping( response = logged_admin_client.post("/network/networks/create", data=form) assert response.status_code == 200 assert b"172.30.1.0/24 overlaps network1 (172.30.0.0/23)" in response.data + + +def test_create_network_scope_overlapping(network_scope_factory, logged_admin_client): + scope1 = network_scope_factory( + name="scope1", first_vlan=3800, last_vlan=4000, supernet="172.30.0.0/16" + ) + form = { + "name": "scope2", + "first_vlan": 200, + "last_vlan": 500, + "supernet": "172.30.200.0/22", + "domain_id": scope1.domain_id, + } + response = logged_admin_client.post("/network/scopes/create", data=form) + assert response.status_code == 200 + assert b"172.30.200.0/22 overlaps scope1 (172.30.0.0/16)" in response.data -- GitLab