From 0b4a061d2b0c1591eb83a5c5657dff7996be1f94 Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@ess.eu>
Date: Tue, 14 Apr 2020 17:39:31 +0200
Subject: [PATCH] Allow /network/networks API for normal users

As for the view networks page, only networks the user has access to are
returned.

JIRA INFRA-2013 #action In Progress
---
 app/api/network.py           | 15 +++++++--
 tests/functional/test_api.py | 65 ++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/app/api/network.py b/app/api/network.py
index 503f575..0e7d582 100644
--- a/app/api/network.py
+++ b/app/api/network.py
@@ -65,13 +65,24 @@ def delete_scope(scope_id):
 
 
 @bp.route("/networks")
-@login_groups_accepted("admin")
+@login_groups_accepted("admin", "network")
 def get_networks():
     """Return networks
 
     .. :quickref: Network; Get networks
     """
-    return utils.get_generic_model(models.Network, order_by=models.Network.address)
+    query = models.Network.query
+    if not current_user.is_admin:
+        query = (
+            query.filter(models.Network.sensitive.is_(False))
+            .filter(models.Network.admin_only.is_(False))
+            .join(models.Network.scope)
+            .filter(models.NetworkScope.name.in_(current_user.csentry_network_scopes))
+            .distinct(models.Network.id)
+            .from_self()
+        )
+    query = query.order_by(models.Network.address)
+    return utils.get_generic_model(model=models.Network, base_query=query)
 
 
 @bp.route("/networks", methods=["POST"])
diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py
index c89617f..a2cec3e 100644
--- a/tests/functional/test_api.py
+++ b/tests/functional/test_api.py
@@ -720,6 +720,71 @@ def test_get_networks(client, network_scope_factory, network_factory, admin_toke
     check_input_is_subset_of_response(response, (network2.to_dict(),))
 
 
+def test_get_networks_normal_user(
+    client, network_scope_factory, network_factory, user_token
+):
+    # ProdNetworks scope not available to user
+    prod_scope = network_scope_factory(name="ProdNetworks", supernet="172.16.0.0/16")
+    network_factory(
+        vlan_name="network-prod",
+        address="172.16.3.0/24",
+        first_ip="172.16.3.10",
+        last_ip="172.16.3.250",
+        scope=prod_scope,
+    )
+    # User has access to networks on FooNetworks scopes (expect admin and sensitive)
+    foo_scope = network_scope_factory(name="FooNetworks", supernet="192.168.0.0/16")
+    network_factory(
+        vlan_name="admin-network",
+        address="192.168.1.0/24",
+        first_ip="192.168.1.10",
+        last_ip="192.168.1.250",
+        admin_only=True,
+        scope=foo_scope,
+    )
+    network_factory(
+        vlan_name="sensitive-network",
+        address="192.168.2.0/24",
+        first_ip="192.168.2.10",
+        last_ip="192.168.2.250",
+        admin_only=False,
+        sensitive=True,
+        scope=foo_scope,
+    )
+    # Create 30 networks
+    for n in range(3, 33):
+        network_factory(
+            vlan_name=f"network{n}",
+            address=f"192.168.{n}.0/24",
+            first_ip=f"192.168.{n}.10",
+            last_ip=f"192.168.{n}.250",
+            scope=foo_scope,
+        )
+    url = f"{API_URL}/network/networks"
+    response = get(client, url, token=user_token)
+    assert response.status_code == 200
+    assert response.headers["x-total-count"] == "30"
+    networks1 = response.get_json()
+    assert len(networks1) == 20
+    assert (
+        f'{url}?per_page=20&page=2&recursive=False>; rel="next",'
+        in response.headers["link"]
+    )
+    # Get second page
+    response = get(
+        client, f"{url}?per_page=20&page=2&recursive=False", token=user_token,
+    )
+    assert response.status_code == 200
+    networks2 = response.get_json()
+    assert len(networks2) == 10
+    # Check that admin/sensitive/prod networks aren't part of the result
+    assert [
+        network
+        for network in networks1 + networks2
+        if network["vlan_name"].startswith("network")
+    ] == networks1 + networks2
+
+
 def test_create_network_auth_fail(client, session, user_token):
     # admin is required to create networks
     response = post(client, f"{API_URL}/network/networks", data={}, token=user_token)
-- 
GitLab