From 162a4dd1c5055d344581871303cb9ac41a0a07c0 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Fri, 4 Jul 2025 16:32:56 +0200 Subject: [PATCH] Add default value for the "context_with_global_access" policy This API policy rule can be used to specify who besides admin and service role can have access to the resources from all projects. It is added to the neutron_lib.context.Context with patch [1]. This patch also adds unit tests to check basic GET network(s) calls to the core plugin to make sure that it can return from the database resources which belongs to different projects if `context.has_global_access` is set to `True`. Those unit tests are not testing API policies are policy module is "skipped" in those unit tests completely. Additionally this patch adds documentation which describes how to use this rule in the custom policy file. [1] https://review.opendev.org/c/openstack/neutron-lib/+/954054 Depends-On: https://review.opendev.org/c/openstack/neutron-lib/+/954054 Related-bug: #2115184 Change-Id: Id68170ef7ed12ddca51610d53e6ef936d84577be Signed-off-by: Slawek Kaplonski --- .../configuration/custom_policy_roles.rst | 33 +++++++++++++++++++ doc/source/configuration/index.rst | 1 + neutron/conf/policies/base.py | 4 +++ .../tests/common/test_db_base_plugin_v2.py | 26 +++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 doc/source/configuration/custom_policy_roles.rst diff --git a/doc/source/configuration/custom_policy_roles.rst b/doc/source/configuration/custom_policy_roles.rst new file mode 100644 index 00000000000..64711433b51 --- /dev/null +++ b/doc/source/configuration/custom_policy_roles.rst @@ -0,0 +1,33 @@ +.. _Custom Policy Roles: + +=================== +Custom Policy Roles +=================== + +Besides the :ref:`default policy roles `, Neutron also +supports using custom roles. Using custom roles with for example read only +access to all of the resources requires to configure the policy rule which +allows ``global access`` to the resources. + +To grant the ``auditor`` role access to fetch all of the resources from the +database, following rule should be added to the ``policy.yaml`` file: + +.. code-block:: yaml + + "context_with_global_access": "role:auditor" + + +This will make all SQL queries made by neutron with the ``auditor`` role in the +context to not be scoped by the project ID. +This however don't grant the ``auditor`` role to receive all of the resources +from the Neutron API yet. To grant such permissions for example for the +``get_network`` action, following rule should be added to the ``policy.yaml`` +file: + +.. code-block:: yaml + + "get_network": "role:admin_only or (role:reader and project_id:%(project_id)s) or rule:shared or rule:external or rule:context_is_advsvc or role:auditor" + + +With those 2 rules in place, the ``auditor`` role will be able to fetch all of +the networks from the Neutron API. diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst index 6d6d7f88c73..0a939810453 100644 --- a/doc/source/configuration/index.rst +++ b/doc/source/configuration/index.rst @@ -9,3 +9,4 @@ Configuration Guide config policy + custom_policy_roles diff --git a/neutron/conf/policies/base.py b/neutron/conf/policies/base.py index 4947230a784..dfead740702 100644 --- a/neutron/conf/policies/base.py +++ b/neutron/conf/policies/base.py @@ -102,6 +102,10 @@ rules = [ 'context_is_admin', 'role:admin', description='Rule for cloud admin access'), + policy.RuleDefault( + 'context_with_global_access', + '!', + description='Rule for context with global access to the resources'), policy.RuleDefault( "service_api", "role:service", diff --git a/neutron/tests/common/test_db_base_plugin_v2.py b/neutron/tests/common/test_db_base_plugin_v2.py index 49e2ea4dc95..45246c57ce2 100644 --- a/neutron/tests/common/test_db_base_plugin_v2.py +++ b/neutron/tests/common/test_db_base_plugin_v2.py @@ -1061,6 +1061,32 @@ class TestBasicGet(NeutronDbPluginV2TestCase): n = plugin._get_network(ctx, net_id) self.assertEqual(net_id, n.id) + def test_list_with_context_with_global_access(self): + plugin = neutron.db.db_base_plugin_v2.NeutronDbPluginV2() + ctx = context.Context( + user_id="auditor", project_id="auditor project", + is_admin=False, has_global_access=True) + with self.network(project_id='some project') as net1, \ + self.network(project_id='other project') as net2, \ + self.network(project_id='auditor project') as own_net: + networks = plugin.get_networks(ctx) + net_ids = [n['id'] for n in networks] + self.assertIn(own_net['network']['id'], net_ids) + self.assertIn(net1['network']['id'], net_ids) + self.assertIn(net2['network']['id'], net_ids) + + def test_single_get_with_context_with_global_access(self): + plugin = neutron.db.db_base_plugin_v2.NeutronDbPluginV2() + ctx = context.Context( + user_id="auditor", project_id="auditor project", + is_admin=False, has_global_access=True) + with self.network(project_id='some project') as net1, \ + self.network(project_id='other project') as net2, \ + self.network(project_id='auditor project') as own_net: + for net in [net1, net2, own_net]: + network = plugin._get_network(ctx, net['network']['id']) + self.assertEqual(net['network']['id'], network['id']) + class TestV2HTTPResponse(NeutronDbPluginV2TestCase): def test_create_returns_201(self):