Fix: Policy for new location APIs

Currently, in the new location APIs, we use ``role:service`` as
the check for the existence of service role or not.
However, in a request with service token enabled, we get the
following context object:

'roles': ['member', 'manager', 'admin', 'reader'],
'service_user_name': 'cinder',
'service_project_name': 'service',
'service_roles': ['creator', 'service']

This issue is not seen while adopting the add location API because
of two reasons:
1. The ``add_image_location`` policy validates SERVICE_OR_PROJECT_MEMBER
where the project member check (({PROJECT_MEMBER} and project_id:%(owner)s))
passes the policy check
2. We include the ``service_roles`` from the context before the if check[1]

From the above, we can see that there is a bug in the new location APIs
policy checks which is only visible when we start consuming the GET location
API.
This patch fixes it to correct the check from ``role:service`` to
``service_roles:service`` as already did in this patch[2].

[1] fda4fba5e9/glance/api/v2/images.py (L1160-L1161)
[2] https://review.opendev.org/c/openstack/glance/+/942153

Closes-Bug: #2120737
Change-Id: Iab40d4cf1d6fb836a370c8b3ebfbb84182265d7a
Signed-off-by: Rajat Dhasmana <rajatdhasmana@gmail.com>
This commit is contained in:
Rajat Dhasmana
2025-08-16 01:57:10 +05:30
parent fda4fba5e9
commit e40759c15b
2 changed files with 7 additions and 8 deletions

View File

@@ -89,9 +89,8 @@ SERVICE_OR_PROJECT_MEMBER = (
)
SERVICE = 'rule:service_api'
SERVICE_ROLE = 'service_roles:service'
ADMIN_OR_SERVICE_ROLE = f'{ADMIN} or {SERVICE_ROLE}'
ADMIN_OR_SERVICE_ROLE = f'{ADMIN} or {SERVICE}'
rules = [
policy.RuleDefault(name='default', check_str='',
@@ -114,7 +113,7 @@ rules = [
policy.RuleDefault(name='context_is_admin', check_str='role:admin',
description='Defines the rule for the is_admin:True '
'check.'),
policy.RuleDefault(name='service_api', check_str='role:service',
policy.RuleDefault(name='service_api', check_str='service_roles:service',
description='Default rule for the service-to-service '
'API.'),
]

View File

@@ -2465,7 +2465,7 @@ class TestImagesSingleStore(functional.SynchronousAPIBase):
)
# Get locations of `queued` image
headers = self._headers({'X-Roles': 'service'})
headers = self._headers({'X-Service-Roles': 'service'})
path = '/v2/images/%s/locations' % image_id
response = self.api_get(path, headers=headers)
self.assertEqual(200, response.status_code, response.text)
@@ -2500,7 +2500,7 @@ class TestImagesSingleStore(functional.SynchronousAPIBase):
self.assertEqual(http.FORBIDDEN, response.status_code, response.text)
# Get Locations allowed only for service user
headers = self._headers({'X-Roles': 'service'})
headers = self._headers({'X-Service-Roles': 'service'})
path = '/v2/images/%s/locations' % image_id
response = self.api_get(path, headers=headers)
self.assertEqual(200, response.status_code, response.text)
@@ -2523,7 +2523,7 @@ class TestImagesSingleStore(functional.SynchronousAPIBase):
self.assertEqual(http.FORBIDDEN, response.status_code, response.text)
# Get Locations allowed only for service user
headers = self._headers({'X-Roles': 'service'})
headers = self._headers({'X-Service-Roles': 'service'})
path = '/v2/images/%s/locations' % image_id
response = self.api_get(path, headers=headers)
self.assertEqual(200, response.status_code, response.text)
@@ -4918,7 +4918,7 @@ class TestMultipleBackendsLocationApi(functional.SynchronousAPIBase):
self.assertEqual('queued', image['status'])
# Get location of `queued` image
headers = self._headers({'X-Roles': 'service'})
headers = self._headers({'X-Service-Roles': 'service'})
path = '/v2/images/%s/locations' % image_id
response = self.api_get(path, headers=headers)
self.assertEqual(200, response.status_code, response.text)
@@ -4953,7 +4953,7 @@ class TestMultipleBackendsLocationApi(functional.SynchronousAPIBase):
self.assertEqual(http.FORBIDDEN, response.status_code, response.text)
# Get Locations allowed only for service user
headers = self._headers({'X-Roles': 'service'})
headers = self._headers({'X-Service-Roles': 'service'})
path = '/v2/images/%s/locations' % image_id
response = self.api_get(path, headers=headers)
self.assertEqual(200, response.status_code, response.text)