Allow service role to create/update port device_id

The ``device_id`` field on ports is used by other
OpenStack projects to save what resource is using
a port and for these OpenStack projects to support
the Secure RBAC community goal they need to be
able to update this field.

This is required for OpenStack projects such as
Nova that tracks instance UUID in device_id on
a port and Octavia that also uses the device_id
field.

This allows the ``service`` role to update the
device_id field and doesn't touch any existing
policies that already exist for the field.

Related-Bug: #2105502
Closes-Bug: #2107039
Change-Id: I227416a7420412a39e450352915eff5967172c64
This commit is contained in:
Tobias Urdin
2025-04-11 16:13:21 +02:00
parent 2234334611
commit c981cfd658
3 changed files with 123 additions and 0 deletions

View File

@@ -81,6 +81,20 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_port:device_id',
check_str=neutron_policy.policy_or(
base.ADMIN_OR_PROJECT_MEMBER,
base.SERVICE),
scope_types=['project'],
description='Specify ``device_id`` attribute when creating a port',
operations=ACTION_POST,
deprecated_rule=policy.DeprecatedRule(
name='create_port:device_id',
check_str=neutron_policy.RULE_ANY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_port:device_owner',
check_str=neutron_policy.policy_or(
@@ -460,6 +474,20 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='update_port:device_id',
check_str=neutron_policy.policy_or(
base.ADMIN_OR_PROJECT_MEMBER,
base.SERVICE),
scope_types=['project'],
description='Update ``device_id`` attribute of a port',
operations=ACTION_PUT,
deprecated_rule=policy.DeprecatedRule(
name='update_port:device_id',
check_str=neutron_policy.RULE_ANY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='update_port:device_owner',
check_str=neutron_policy.policy_or(

View File

@@ -62,6 +62,16 @@ class SystemAdminTests(PortAPITestCase):
base_policy.InvalidScope,
policy.enforce, self.context, 'create_port', self.alt_target)
def test_create_port_with_device_id(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_port:device_id',
self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_port:device_id',
self.alt_target)
def test_create_port_with_device_owner(self):
self.assertRaises(
base_policy.InvalidScope,
@@ -270,6 +280,16 @@ class SystemAdminTests(PortAPITestCase):
base_policy.InvalidScope,
policy.enforce, self.context, 'update_port', self.alt_target)
def test_update_port_with_device_id(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'update_port:device_id',
self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'update_port:device_id',
self.alt_target)
def test_update_port_with_device_owner(self):
self.assertRaises(
base_policy.InvalidScope,
@@ -441,6 +461,14 @@ class AdminTests(PortAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'create_port', self.alt_target))
def test_create_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'create_port:device_id',
self.target))
self.assertTrue(
policy.enforce(self.context, 'create_port:device_id',
self.alt_target))
def test_create_port_with_device_owner(self):
target = self.target.copy()
target['device_owner'] = 'network:test'
@@ -650,6 +678,14 @@ class AdminTests(PortAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'update_port', self.alt_target))
def test_update_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'update_port:device_id',
self.target))
self.assertTrue(
policy.enforce(self.context, 'update_port:device_id',
self.alt_target))
def test_update_port_with_device_owner(self):
target = self.target.copy()
target['device_owner'] = 'network:test'
@@ -809,6 +845,15 @@ class ProjectManagerTests(AdminTests):
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_port', self.alt_target)
def test_create_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'create_port:device_id',
self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_port:device_id',
self.alt_target)
def test_create_port_with_device_owner(self):
target = self.target.copy()
target['device_owner'] = 'network:test'
@@ -1051,6 +1096,14 @@ class ProjectManagerTests(AdminTests):
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'update_port', self.alt_target)
def test_update_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'update_port:device_id', self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'update_port:device_id',
self.alt_target)
def test_update_port_with_device_owner(self):
target = self.target.copy()
target['device_owner'] = 'network:test'
@@ -1437,6 +1490,16 @@ class ProjectReaderTests(ProjectMemberTests):
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_port', self.alt_target)
def test_create_port_with_device_id(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_port:device_id',
self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_port:device_id',
self.alt_target)
def test_create_port_with_binding_vnic_type(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@@ -1463,6 +1526,16 @@ class ProjectReaderTests(ProjectMemberTests):
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'update_port', self.alt_target)
def test_update_port_with_device_id(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'update_port:device_id',
self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'update_port:device_id',
self.alt_target)
def test_update_port_with_binding_vnic_type(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@@ -1500,6 +1573,14 @@ class ServiceRoleTests(PortAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'create_port', self.target))
def test_create_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'create_port:device_id',
self.target))
self.assertTrue(
policy.enforce(self.context, 'create_port:device_id',
self.alt_target))
def test_create_port_with_device_owner(self):
self.assertTrue(
policy.enforce(
@@ -1608,6 +1689,14 @@ class ServiceRoleTests(PortAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'update_port', self.target))
def test_update_port_with_device_id(self):
self.assertTrue(
policy.enforce(self.context, 'update_port:device_id',
self.target))
self.assertTrue(
policy.enforce(self.context, 'update_port:device_id',
self.alt_target))
def test_update_port_with_device_owner(self):
self.assertTrue(
policy.enforce(

View File

@@ -0,0 +1,6 @@
---
features:
- |
Added ``service`` role to the ``create_port:device_id`` and
``update_port:device_id`` policies to allow service users
for other OpenStack projects to complete Secure RBAC.