diff --git a/app/api/network.py b/app/api/network.py index 0373fa05cb3bf7ed81099f5c8615ec274173c548..73b9d109ebceca854e2dd52c7b16532fc42ca6b2 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 631c124cba7301b7e9b786abca9ff53f5819f272..d4052180ebb2ec36d098c8e7b547f0475cf8e935 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 a33542f92b930cc3b90980a87f6dc724dc2cd094..d4276e46d2c0d007fe78c8754231c1637b32287f 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 0b081608f585591162704ce7f4574597bf0246f3..dcee73e8b858d860ed60d131c4a9b692b737f45d 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