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