From 5fc25952caa917c5406a6e623039377dc55c0b50 Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand <benjamin.bertrand@ess.eu> Date: Tue, 28 Jan 2020 15:13:57 +0100 Subject: [PATCH] Restrict view of networks and scopes to admin only JIRA INFRA-1671 #action In Progress --- app/api/network.py | 4 ++-- app/network/views.py | 10 ++++++---- tests/functional/test_api.py | 14 +++++++++----- tests/functional/test_web.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/app/api/network.py b/app/api/network.py index 0373fa0..73b9d10 100644 --- a/app/api/network.py +++ b/app/api/network.py @@ -21,7 +21,7 @@ bp = Blueprint("network_api", __name__) @bp.route("/scopes") -@login_required +@login_groups_accepted("admin") def get_scopes(): """Return network scopes @@ -53,7 +53,7 @@ def create_scope(): @bp.route("/networks") -@login_required +@login_groups_accepted("admin") def get_networks(): """Return networks diff --git a/app/network/views.py b/app/network/views.py index 631c124..d405218 100644 --- a/app/network/views.py +++ b/app/network/views.py @@ -570,14 +570,14 @@ def create_domain(): @bp.route("/scopes") -@login_required +@login_groups_accepted("admin") def list_scopes(): scopes = models.NetworkScope.query.all() return render_template("network/scopes.html", scopes=scopes) @bp.route("/scopes/view/<name>") -@login_required +@login_groups_accepted("admin") def view_scope(name): scope = models.NetworkScope.query.filter_by(name=name).first_or_404() return render_template("network/view_scope.html", scope=scope) @@ -630,16 +630,18 @@ def retrieve_first_available_ip(network_id): @bp.route("/networks") -@login_required +@login_groups_accepted("admin") def list_networks(): networks = models.Network.query.all() return render_template("network/networks.html", networks=networks) @bp.route("/networks/view/<vlan_name>") -@login_required +@login_groups_accepted("admin", "network") def view_network(vlan_name): network = models.Network.query.filter_by(vlan_name=vlan_name).first_or_404() + if not current_user.has_access_to_network(network): + abort(403) return render_template("network/view_network.html", network=network) diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py index a33542f..d4276e4 100644 --- a/tests/functional/test_api.py +++ b/tests/functional/test_api.py @@ -615,7 +615,13 @@ def test_get_items(client, location_factory, item_factory, readonly_token): check_response_message(response, "Invalid query arguments", 422) -def test_get_networks(client, network_scope_factory, network_factory, readonly_token): +@pytest.mark.parametrize("url", ["/network/networks", "/network/scopes"]) +def test_get_restricted_url_as_non_admin(url, client, readonly_token): + response = get(client, f"{API_URL}{url}", token=readonly_token) + assert response.status_code == 403 + + +def test_get_networks(client, network_scope_factory, network_factory, admin_token): # Create some networks scope = network_scope_factory(supernet="172.16.0.0/16") network1 = network_factory( @@ -637,7 +643,7 @@ def test_get_networks(client, network_scope_factory, network_factory, readonly_t scope=scope, ) - response = get(client, f"{API_URL}/network/networks", token=readonly_token) + response = get(client, f"{API_URL}/network/networks", token=admin_token) assert response.status_code == 200 assert len(response.get_json()) == 3 check_input_is_subset_of_response( @@ -646,9 +652,7 @@ def test_get_networks(client, network_scope_factory, network_factory, readonly_t # test filtering by address response = get( - client, - f"{API_URL}/network/networks?address=172.16.20.0/22", - token=readonly_token, + client, f"{API_URL}/network/networks?address=172.16.20.0/22", token=admin_token, ) assert response.status_code == 200 assert len(response.get_json()) == 1 diff --git a/tests/functional/test_web.py b/tests/functional/test_web.py index 0b08160..dcee73e 100644 --- a/tests/functional/test_web.py +++ b/tests/functional/test_web.py @@ -74,7 +74,7 @@ def test_index(logged_client): assert b"User RO" in response.data -@pytest.mark.parametrize("url", ["/", "/inventory/items", "/network/networks"]) +@pytest.mark.parametrize("url", ["/", "/inventory/items", "/network/hosts"]) def test_protected_url_get(url, client): response = client.get(url) assert response.status_code == 302 @@ -84,6 +84,17 @@ def test_protected_url_get(url, client): assert response.status_code == 200 +@pytest.mark.parametrize("url", ["/network/networks", "/network/scopes"]) +def test_admin_protected_url_get(url, client): + login(client, "user_rw", "userrw") + response = client.get(url) + assert response.status_code == 403 + logout(client) + login(client, "admin", "adminpasswd") + response = client.get(url) + assert response.status_code == 200 + + @pytest.mark.parametrize( "url", ["/inventory/_retrieve_items", "/network/_retrieve_hosts"] ) @@ -804,3 +815,22 @@ def test_create_network_scope_overlapping(network_scope_factory, logged_admin_cl 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 + + +def test_view_network_restriction(client, network_scope_factory, network_factory): + scope = network_scope_factory(name="ProdNetworks", supernet="192.168.0.0/16") + network = network_factory( + address="192.168.1.0/24", + first_ip="192.168.1.10", + last_ip="192.168.1.250", + scope=scope, + ) + # user_lab doesn't have the permissions to see that network + login(client, "user_lab", "userlab") + response = client.get(f"/network/networks/view/{network}") + assert response.status_code == 403 + logout(client) + # Success with user_prod user + login(client, "user_prod", "userprod") + response = client.get(f"/network/networks/view/{network}") + assert response.status_code == 200 -- GitLab