Merge "[S-RBAC] Change tenant_id to project_id in the API policies"

This commit is contained in:
Zuul
2025-10-14 22:29:26 +00:00
committed by Gerrit Code Review
15 changed files with 57 additions and 59 deletions

View File

@@ -88,7 +88,7 @@ ADMIN_OR_NET_OWNER_READER = (
# above. We should probably deprecate SG_OWNER rules and use PARENT_OWNER
# instead but this can be done later
# TODO(slaweq): Deprecate SG_OWNER rules and replace them with PARENT_OWNER
# rules but for that, 'ext_parent_owner:tenant_id' needs to be added to the SG
# rules but for that, 'ext_parent_owner:project_id' needs to be added to the SG
# rule target dict
SG_OWNER_MEMBER = 'role:member and ' + RULE_SG_OWNER
SG_OWNER_READER = 'role:reader and ' + RULE_SG_OWNER
@@ -112,7 +112,7 @@ rules = [
description="Default rule for the service-to-service APIs."),
policy.RuleDefault(
'owner',
'tenant_id:%(tenant_id)s',
'project_id:%(project_id)s',
description='Rule for resource owner access'),
policy.RuleDefault(
'admin_or_owner',
@@ -129,7 +129,7 @@ rules = [
policy.RuleDefault(
'admin_or_network_owner',
neutron_policy.policy_or('rule:context_is_admin',
'tenant_id:%(network:tenant_id)s'),
'project_id:%(network:project_id)s'),
description='Rule for admin or network owner access'),
policy.RuleDefault(
'admin_owner_or_network_owner',
@@ -139,7 +139,7 @@ rules = [
'admin or network owner access')),
policy.RuleDefault(
'network_owner',
'tenant_id:%(network:tenant_id)s',
'project_id:%(network:project_id)s',
description='Rule for network owner access'),
policy.RuleDefault(
'admin_only',
@@ -161,15 +161,15 @@ rules = [
policy.RuleDefault(
'admin_or_ext_parent_owner',
neutron_policy.policy_or('rule:context_is_admin',
'tenant_id:%(ext_parent:tenant_id)s'),
'project_id:%(ext_parent:project_id)s'),
description='Rule for common parent owner check'),
policy.RuleDefault(
'ext_parent_owner',
'tenant_id:%(ext_parent:tenant_id)s',
'project_id:%(ext_parent:project_id)s',
description='Rule for common parent owner check'),
policy.RuleDefault(
name='sg_owner',
check_str='tenant_id:%(security_group:tenant_id)s',
check_str='project_id:%(security_group:project_id)s',
description='Rule for security group owner access'),
]

View File

@@ -55,7 +55,7 @@ rules = [
name='admin_or_sg_owner',
check_str=neutron_policy.policy_or(
'rule:context_is_admin',
'tenant_id:%(security_group:tenant_id)s'),
'project_id:%(security_group:project_id)s'),
description='Rule for admin or security group owner access'),
policy.RuleDefault(
name='admin_owner_or_sg_owner',

View File

@@ -322,9 +322,12 @@ class DbBasePluginCommon:
def _make_network_dict(self, network, fields=None,
process_extensions=True, context=None):
# TODO(slaweq): Remove 'tenant_id' in the 2027.1 cycle, when it will
# not be registered for OwnerCheck anymore.
res = {'id': network['id'],
'name': network['name'],
'tenant_id': network['tenant_id'],
'project_id': network['project_id'],
'admin_state_up': network['admin_state_up'],
'mtu': network.get('mtu', constants.DEFAULT_NETWORK_MTU),
'status': network['status'],

View File

@@ -1242,8 +1242,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
if floatingip.floating_ip_address else None)
fixed_ip_address = (str(floatingip.fixed_ip_address)
if floatingip.fixed_ip_address else None)
# TODO(slaweq): Remove 'tenant_id' in the 2027.1 cycle, when it will
# not be registered for OwnerCheck anymore.
res = {'id': floatingip.id,
'tenant_id': floatingip.project_id,
'project_id': floatingip.project_id,
'floating_ip_address': floating_ip_address,
'floating_network_id': floatingip.floating_network_id,
'router_id': floatingip.router_id,

View File

@@ -345,10 +345,13 @@ class SecurityGroupDbMixin(
rbac_entries = security_group['rbac_entries']
shared = rbac_db_obj.RbacNeutronDbObjectMixin.is_network_shared(
context, rbac_entries)
# TODO(slaweq): Remove 'tenant_id' in the 2027.1 cycle, when it will
# not be registered for OwnerCheck anymore.
res = {'id': security_group['id'],
'name': security_group['name'],
'stateful': security_group['stateful'],
'tenant_id': security_group['tenant_id'],
'project_id': security_group['project_id'],
'description': security_group['description'],
'standard_attr_id': security_group.standard_attr_id,
'shared': shared,

View File

@@ -253,6 +253,9 @@ def _build_match_rule(action, target, pluralized):
# This will prevent us from having to handling backward compatibility
# for policy.yaml
# TODO(salv-orlando): Reinstate GenericCheck for simple tenant_id checks
@policy.register('project_id')
# TODO(slaweq): Remove registering of the 'tenant_id' for OwnerCheck in
# the 2027.1 cycle
@policy.register('tenant_id')
class OwnerCheck(policy.Check):
"""Resource ownership check.
@@ -264,6 +267,11 @@ class OwnerCheck(policy.Check):
resource and perform the check.
"""
def __init__(self, kind, match):
if kind == 'tenant_id':
LOG.warning(
"Using 'tenant_id' in the API policy rules is deprecated "
"since 2026.1 release and will be removed in "
"the 2027.1. Please use 'project_id' instead.")
self._orig_kind = kind
self._orig_match = match

View File

@@ -145,22 +145,22 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'is_visible': True, 'default': ''},
'restricted_attr': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string':
db_const.PROJECT_ID_FIELD_SIZE},
'is_visible': True}
'project_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string':
db_const.PROJECT_ID_FIELD_SIZE},
'is_visible': True}
},
'admin_mehs': {
'id': {'allow_post': False, 'allow_put': False,
'is_visible': True, 'primary_key': True},
'foo': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string':
db_const.PROJECT_ID_FIELD_SIZE},
'is_visible': True}
'project_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string':
db_const.PROJECT_ID_FIELD_SIZE},
'is_visible': True}
}
}
@@ -211,7 +211,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
response = self.app.post_json('/v2.0/mehs.json',
params={'meh': {'attr': 'meh'}},
headers={'X-Project-Id': 'tenid'})
@@ -227,7 +227,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
# The policy engine should trigger an exception in 'before', and the
# plugin method should not be called at all
response = self.app.put_json('/v2.0/mehs/xxx.json',
@@ -246,7 +246,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'yyy',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
response = self.app.put_json('/v2.0/mehs/yyy.json',
params={'meh': {'attr': 'meh'}},
headers={'X-Project-Id': 'tenid'},
@@ -263,7 +263,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
# The policy engine should trigger an exception in 'before', and the
# plugin method should not be called
response = self.app.delete_json('/v2.0/mehs/xxx.json',
@@ -281,7 +281,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'yyy',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
# The policy engine should trigger an exception in 'after', and the
# plugin method should be called
response = self.app.get('/v2.0/mehs/yyy.json',
@@ -295,7 +295,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}
'project_id': 'tenid'}
response = self.app.get('/v2.0/mehs/xxx.json',
headers={'X-Project-Id': 'tenid'})
self.assertEqual(200, response.status_int)
@@ -307,7 +307,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}]
'project_id': 'tenid'}]
response = self.app.get('/v2.0/mehs',
headers={'X-Project-Id': 'tenid'})
self.assertEqual(200, response.status_int)
@@ -319,7 +319,7 @@ class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest):
'id': 'xxx',
'attr': 'meh',
'restricted_attr': '',
'tenant_id': 'tenid'}]
'project_id': 'tenid'}]
policy.reset()
response = self.app.get('/v2.0/mehs',
headers={'X-Project-Id': 'tenid'})

View File

@@ -28,11 +28,9 @@ class FloatingipPortForwardingAPITestCase(base.PolicyBaseTestCase):
super().setUp()
self.fip = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_fip = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.target = {

View File

@@ -28,11 +28,9 @@ class L3ConntrackHelperAPITestCase(base.PolicyBaseTestCase):
super().setUp()
self.router = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_router = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.target = {

View File

@@ -28,11 +28,9 @@ class LocalIPAssociationAPITestCase(base.PolicyBaseTestCase):
super().setUp()
self.local_ip = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_local_ip = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.target = {

View File

@@ -29,19 +29,15 @@ class PortAPITestCase(base.PolicyBaseTestCase):
self.network = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_network = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.target = {
'tenant_id': self.project_id,
'project_id': self.project_id,
'network_id': self.network['id'],
'ext_parent_network_id': self.network['id']}
self.alt_target = {
'tenant_id': self.project_id,
'project_id': self.alt_project_id,
'network_id': self.alt_network['id'],
'ext_parent_network_id': self.alt_network['id']}

View File

@@ -455,11 +455,9 @@ class QosRulesAPITestCase(base.PolicyBaseTestCase):
super().setUp()
self.qos_policy = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_qos_policy = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.target = {
'policy_id': self.qos_policy['id'],

View File

@@ -389,28 +389,24 @@ class SecurityGroupRuleAPITestCase(base.PolicyBaseTestCase):
super().setUp()
self.sg = {
'id': uuidutils.generate_uuid(),
'project_id': self.project_id,
'tenant_id': self.project_id}
'project_id': self.project_id}
self.alt_sg = {
'id': uuidutils.generate_uuid(),
'project_id': self.alt_project_id,
'tenant_id': self.alt_project_id}
'project_id': self.alt_project_id}
self.target = {
'project_id': self.project_id,
'tenant_id': self.project_id,
'security_group_id': self.sg['id'],
'ext_parent:tenant_id': self.sg['id'],
'ext_parent:project_id': self.sg['id'],
'ext_parent_security_group_id': self.sg['id']}
self.alt_target = {
'project_id': self.alt_project_id,
'tenant_id': self.alt_project_id,
'security_group_id': self.alt_sg['id'],
'ext_parent:tenant_id': self.alt_sg['id'],
'ext_parent:project_id': self.alt_sg['id'],
'ext_parent_security_group_id': self.alt_sg['id']}
def get_security_group_mock(context, id,
fields=None, tenant_id=None):
fields=None, project_id=None):
if id == self.alt_sg['id']:
return self.alt_sg
return self.sg
@@ -559,7 +555,7 @@ class ProjectManagerSecurityGroupRuleTests(AdminSecurityGroupRuleTests):
# Test for the SG_OWNER different then current user case:
target = copy.copy(self.target)
target['security_group_id'] = self.alt_sg['id']
target['ext_parent:tenant_id'] = self.alt_sg['tenant_id']
target['ext_parent:project_id'] = self.alt_sg['project_id']
target['ext_parent_security_group_id'] = self.alt_sg['id']
self.plugin_mock.get_security_group.return_value = self.alt_sg
self.assertRaises(
@@ -600,7 +596,7 @@ class ProjectManagerSecurityGroupRuleTests(AdminSecurityGroupRuleTests):
# Test for the SG_OWNER different then current user case:
target = copy.copy(self.target)
target['security_group_id'] = self.alt_sg['id']
target['ext_parent:tenant_id'] = self.alt_sg['tenant_id']
target['ext_parent:project_id'] = self.alt_sg['project_id']
target['ext_parent_security_group_id'] = self.alt_sg['id']
self.plugin_mock.get_security_group.return_value = self.alt_sg
self.assertRaises(
@@ -646,7 +642,7 @@ class ProjectReaderSecurityGroupRuleTests(ProjectMemberSecurityGroupRuleTests):
# Test for the SG_OWNER different then current user case:
target = copy.copy(self.target)
target['security_group_id'] = self.alt_sg['id']
target['ext_parent:tenant_id'] = self.alt_sg['tenant_id']
target['ext_parent:project_id'] = self.alt_sg['project_id']
target['ext_parent_security_group_id'] = self.alt_sg['id']
self.plugin_mock.get_security_group.return_value = self.alt_sg
self.assertRaises(
@@ -666,7 +662,7 @@ class ProjectReaderSecurityGroupRuleTests(ProjectMemberSecurityGroupRuleTests):
# Test for the SG_OWNER different then current user case:
target = copy.copy(self.target)
target['security_group_id'] = self.alt_sg['id']
target['ext_parent:tenant_id'] = self.alt_sg['tenant_id']
target['ext_parent:project_id'] = self.alt_sg['project_id']
target['ext_parent_security_group_id'] = self.alt_sg['id']
self.plugin_mock.get_security_group.return_value = self.alt_sg
self.assertRaises(

View File

@@ -37,15 +37,12 @@ class SubnetAPITestCase(base.PolicyBaseTestCase):
self.network = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.project_id,
'project_id': self.project_id}
self.alt_network = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
self.ext_alt_network = {
'id': uuidutils.generate_uuid(),
'tenant_id': self.alt_project_id,
'project_id': self.alt_project_id}
networks = {
@@ -56,26 +53,22 @@ class SubnetAPITestCase(base.PolicyBaseTestCase):
self.target = {
'project_id': self.project_id,
'tenant_id': self.project_id,
'network_id': self.network['id'],
'ext_parent_network_id': self.network['id']}
# This subnet belongs to "project_id", but not the network that
# belongs to "alt_project_id".
self.target_net_alt_target = {
'project_id': self.project_id,
'tenant_id': self.project_id,
'network_id': self.alt_network['id'],
'ext_parent_network_id': self.alt_network['id']}
self.alt_target = {
'project_id': self.alt_project_id,
'tenant_id': self.alt_project_id,
'network_id': self.alt_network['id'],
'ext_parent_network_id': self.alt_network['id']}
# Both the subnet and the network belongs to "alt_project_id" and the
# network is external.
self.target_net_ext_alt_target = {
'project_id': self.alt_project_id,
'tenant_id': self.alt_project_id,
'network_id': self.ext_alt_network['id'],
'ext_parent_network_id': self.ext_alt_network['id'],
'router:external': True}
@@ -83,7 +76,6 @@ class SubnetAPITestCase(base.PolicyBaseTestCase):
# the subnet.
self.alt_target_own_net = {
'project_id': self.alt_project_id,
'tenant_id': self.alt_project_id,
'network_id': self.network['id'],
'ext_parent_network_id': self.network['id']}

View File

@@ -0,0 +1,5 @@
---
deprecations:
- |
Usage of ``tenant_id`` in the API policy rules is deprecated and will be
removed in the 2027.1 release. Please use ``project_id`` instead.