Ensure endpoints returned is filtered correctly
Conflicts:
keystone/catalog/controllers.py
keystone/catalog/core.py
keystone/contrib/endpoint_filter/backends/catalog_sql.py
This patch move some logic to manager layer, so that endpoints
filtered by endpoint_group project association will be included
in catalog when issue a project scoped token and using
`endpoint_filter.sql` as catalog's backend driver.
This make sure that call `list_endpoints_for_project` API has
the same endpoints with that in catalog returned for project
scoped token.
The difference between this cherry pick and the patch on the master
branch is massive since the endpoint filter extension has been
consolidated with keystone catalog on master branch, all the change
made in the keystone catalog should be made in keystone endpoint filter
extension instead.
Closes-Bug: #1516469
(cherry picked from commit f86448a311
)
Change-Id: I56f4eb6fc524650677b627295dd4338d55164c39
This commit is contained in:
parent
d100184a15
commit
9262bb8dcb
|
@ -17,7 +17,6 @@ from oslo_config import cfg
|
|||
from keystone.catalog.backends import sql
|
||||
from keystone.catalog import core as catalog_core
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -31,38 +30,33 @@ class EndpointFilterCatalog(sql.Catalog):
|
|||
|
||||
services = {}
|
||||
|
||||
refs = self.endpoint_filter_api.list_endpoints_for_project(project_id)
|
||||
dict_of_endpoint_refs = (self.endpoint_filter_api.
|
||||
list_endpoints_for_project(project_id))
|
||||
|
||||
if (not refs and
|
||||
if (not dict_of_endpoint_refs and
|
||||
CONF.endpoint_filter.return_all_endpoints_if_no_filter):
|
||||
return super(EndpointFilterCatalog, self).get_v3_catalog(
|
||||
user_id, project_id)
|
||||
|
||||
for entry in refs:
|
||||
try:
|
||||
endpoint = self.get_endpoint(entry['endpoint_id'])
|
||||
if not endpoint['enabled']:
|
||||
# Skip disabled endpoints.
|
||||
continue
|
||||
service_id = endpoint['service_id']
|
||||
services.setdefault(
|
||||
service_id,
|
||||
self.get_service(service_id))
|
||||
service = services[service_id]
|
||||
del endpoint['service_id']
|
||||
del endpoint['enabled']
|
||||
del endpoint['legacy_endpoint_id']
|
||||
endpoint['url'] = catalog_core.format_url(
|
||||
endpoint['url'], substitutions)
|
||||
# populate filtered endpoints
|
||||
if 'endpoints' in services[service_id]:
|
||||
service['endpoints'].append(endpoint)
|
||||
else:
|
||||
service['endpoints'] = [endpoint]
|
||||
except exception.EndpointNotFound:
|
||||
# remove bad reference from association
|
||||
self.endpoint_filter_api.remove_endpoint_from_project(
|
||||
entry['endpoint_id'], project_id)
|
||||
for endpoint_id, endpoint in dict_of_endpoint_refs.items():
|
||||
if not endpoint['enabled']:
|
||||
# Skip disabled endpoints.
|
||||
continue
|
||||
service_id = endpoint['service_id']
|
||||
services.setdefault(
|
||||
service_id,
|
||||
self.get_service(service_id))
|
||||
service = services[service_id]
|
||||
del endpoint['service_id']
|
||||
del endpoint['enabled']
|
||||
del endpoint['legacy_endpoint_id']
|
||||
endpoint['url'] = catalog_core.format_url(
|
||||
endpoint['url'], substitutions)
|
||||
# populate filtered endpoints
|
||||
if 'endpoints' in services[service_id]:
|
||||
service['endpoints'].append(endpoint)
|
||||
else:
|
||||
service['endpoints'] = [endpoint]
|
||||
|
||||
# format catalog
|
||||
catalog = []
|
||||
|
|
|
@ -103,22 +103,8 @@ class EndpointFilterV3Controller(_ControllerBase):
|
|||
def list_endpoints_for_project(self, context, project_id):
|
||||
"""List all endpoints currently associated with a given project."""
|
||||
self.resource_api.get_project(project_id)
|
||||
refs = self.endpoint_filter_api.list_endpoints_for_project(project_id)
|
||||
filtered_endpoints = {ref['endpoint_id']:
|
||||
self.catalog_api.get_endpoint(ref['endpoint_id'])
|
||||
for ref in refs}
|
||||
|
||||
# need to recover endpoint_groups associated with project
|
||||
# then for each endpoint group return the endpoints.
|
||||
endpoint_groups = self._get_endpoint_groups_for_project(project_id)
|
||||
for endpoint_group in endpoint_groups:
|
||||
endpoint_refs = self._get_endpoints_filtered_by_endpoint_group(
|
||||
endpoint_group['id'])
|
||||
# now check if any endpoints for current endpoint group are not
|
||||
# contained in the list of filtered endpoints
|
||||
for endpoint_ref in endpoint_refs:
|
||||
if endpoint_ref['id'] not in filtered_endpoints:
|
||||
filtered_endpoints[endpoint_ref['id']] = endpoint_ref
|
||||
filtered_endpoints = (self.endpoint_filter_api.
|
||||
list_endpoints_for_project(project_id))
|
||||
|
||||
return catalog_controllers.EndpointV3.wrap_collection(
|
||||
context, [v for v in six.itervalues(filtered_endpoints)])
|
||||
|
|
|
@ -50,6 +50,7 @@ extension.register_admin_extension(extension_data['alias'], extension_data)
|
|||
|
||||
|
||||
@dependency.provider('endpoint_filter_api')
|
||||
@dependency.requires('catalog_api', 'resource_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Endpoint Filter backend.
|
||||
|
||||
|
@ -63,6 +64,67 @@ class Manager(manager.Manager):
|
|||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.endpoint_filter.driver)
|
||||
|
||||
def _get_endpoint_groups_for_project(self, project_id):
|
||||
# recover the project endpoint group memberships and for each
|
||||
# membership recover the endpoint group
|
||||
self.resource_api.get_project(project_id)
|
||||
try:
|
||||
refs = self.driver.list_endpoint_groups_for_project(
|
||||
project_id)
|
||||
endpoint_groups = [self.driver.get_endpoint_group(
|
||||
ref['endpoint_group_id']) for ref in refs]
|
||||
return endpoint_groups
|
||||
except exception.EndpointGroupNotFound:
|
||||
return []
|
||||
|
||||
def _get_endpoints_filtered_by_endpoint_group(self, endpoint_group_id):
|
||||
endpoints = self.catalog_api.list_endpoints()
|
||||
filters = self.driver.get_endpoint_group(endpoint_group_id)['filters']
|
||||
filtered_endpoints = []
|
||||
|
||||
for endpoint in endpoints:
|
||||
is_candidate = True
|
||||
for key, value in filters.items():
|
||||
if endpoint[key] != value:
|
||||
is_candidate = False
|
||||
break
|
||||
if is_candidate:
|
||||
filtered_endpoints.append(endpoint)
|
||||
return filtered_endpoints
|
||||
|
||||
def list_endpoints_for_project(self, project_id):
|
||||
"""List all endpoints associated with a project.
|
||||
|
||||
:param project_id: project identifier to check
|
||||
:type project_id: string
|
||||
:returns: a list of endpoint ids or an empty list.
|
||||
|
||||
"""
|
||||
refs = self.driver.list_endpoints_for_project(project_id)
|
||||
filtered_endpoints = {}
|
||||
for ref in refs:
|
||||
try:
|
||||
endpoint = self.catalog_api.get_endpoint(ref['endpoint_id'])
|
||||
filtered_endpoints.update({ref['endpoint_id']: endpoint})
|
||||
except exception.EndpointNotFound:
|
||||
# remove bad reference from association
|
||||
self.remove_endpoint_from_project(ref['endpoint_id'],
|
||||
project_id)
|
||||
|
||||
# need to recover endpoint_groups associated with project
|
||||
# then for each endpoint group return the endpoints.
|
||||
endpoint_groups = self._get_endpoint_groups_for_project(project_id)
|
||||
for endpoint_group in endpoint_groups:
|
||||
endpoint_refs = self._get_endpoints_filtered_by_endpoint_group(
|
||||
endpoint_group['id'])
|
||||
# now check if any endpoints for current endpoint group are not
|
||||
# contained in the list of filtered endpoints
|
||||
for endpoint_ref in endpoint_refs:
|
||||
if endpoint_ref['id'] not in filtered_endpoints:
|
||||
filtered_endpoints[endpoint_ref['id']] = endpoint_ref
|
||||
|
||||
return filtered_endpoints
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class EndpointFilterDriverV8(object):
|
||||
|
|
|
@ -959,6 +959,15 @@ class EndpointGroupCRUDTestCase(TestExtensionCase):
|
|||
endpoints = self.assertValidEndpointListResponse(r)
|
||||
self.assertEqual(len(endpoints), 2)
|
||||
|
||||
# Ensure catalog includes the endpoints from endpoint_group project
|
||||
# association, this is needed when a project scoped token is issued
|
||||
# and "endpoint_filter.sql" backend driver is in place.
|
||||
user_id = uuid.uuid4().hex
|
||||
catalog_list = self.catalog_api.get_v3_catalog(
|
||||
user_id,
|
||||
self.default_domain_project_id)
|
||||
self.assertEqual(2, len(catalog_list))
|
||||
|
||||
# Now remove project endpoint group association
|
||||
url = self._get_project_endpoint_group_url(
|
||||
endpoint_group_id, self.default_domain_project_id)
|
||||
|
@ -973,6 +982,11 @@ class EndpointGroupCRUDTestCase(TestExtensionCase):
|
|||
endpoints = self.assertValidEndpointListResponse(r)
|
||||
self.assertEqual(len(endpoints), 1)
|
||||
|
||||
catalog_list = self.catalog_api.get_v3_catalog(
|
||||
user_id,
|
||||
self.default_domain_project_id)
|
||||
self.assertEqual(1, len(catalog_list))
|
||||
|
||||
def test_endpoint_group_project_cleanup_with_project(self):
|
||||
# create endpoint group
|
||||
endpoint_group_id = self._create_valid_endpoint_group(
|
||||
|
|
Loading…
Reference in New Issue