Allow discovery URLs to have trailing slashes

The _get_discovery_url_choices generator works by taking a starting URL,
splitting it on '/', and working through the parts trying to get a
matching discovery document from it. It makes assumptions about what the
URL might look like: it might have a project ID on the end of it, and a
version before that. If the starting URL has a trailing '/', splitting
the URL results in an empty string at the end of the list of parts,
which is then treated as a version. The real version is left on the URL
while the generator assumes it has already trimmed the URL down to an
unversioned endpoint. If that version does not match the version we're
seeking, the resulting discovery document will be mismatched and the
generator will fail to yield the right endpoint.

This patch normalizes the starting URL by removing the trailing '/', if
there is one. This way every part of the split URL will be meaningful.

Closes-bug: #1709658

Change-Id: I28c48f78d6f07804d6ea228f163dd37b0fcfcd58
This commit is contained in:
Colleen Murphy 2017-08-10 13:45:14 +02:00
parent 9130c4caf6
commit 88827a895f
2 changed files with 40 additions and 1 deletions

View File

@ -983,7 +983,7 @@ class EndpointData(object):
min_version and max_version are already normalized, so will either be
None or a tuple.
"""
url = urllib.parse.urlparse(self.url)
url = urllib.parse.urlparse(self.url.rstrip('/'))
url_parts = url.path.split('/')
# First, check to see if the catalog url ends with a project id

View File

@ -1119,6 +1119,8 @@ class CatalogHackTests(utils.TestCase):
V2_URL = BASE_URL + 'v2.0'
V3_URL = BASE_URL + 'v3'
PROJECT_ID = uuid.uuid4().hex
def test_getting_endpoints(self):
disc = fixture.DiscoveryList(href=self.BASE_URL)
self.stub_url('GET',
@ -1176,6 +1178,43 @@ class CatalogHackTests(utils.TestCase):
self.assertEqual(self.V2_URL, endpoint)
def test_getting_endpoints_project_id_and_trailing_slash_in_disc_url(self):
# Test that when requesting a v3 endpoint and having a project in the
# session but only the v2 endpoint with a trailing slash in the
# catalog, we can still discover the v3 endpoint.
disc = fixture.DiscoveryList(href=self.BASE_URL)
self.stub_url('GET',
['/'],
base_url=self.BASE_URL,
json=disc)
# Create a project-scoped token. This will exercise the flow in the
# discovery URL sequence where a project ID exists in the token but
# there is no project ID in the URL.
token = fixture.V3Token(project_id=self.PROJECT_ID)
# Add only a v2 endpoint with a trailing slash
service = token.add_service(self.IDENTITY)
service.add_endpoint('public', self.V2_URL + '/')
service.add_endpoint('admin', self.V2_URL + '/')
# Auth with v3
kwargs = {'headers': {'X-Subject-Token': self.TEST_TOKEN}}
self.stub_url('POST',
['auth', 'tokens'],
base_url=self.V3_URL,
json=token, **kwargs)
v3_auth = identity.V3Password(self.V3_URL,
username=uuid.uuid4().hex,
password=uuid.uuid4().hex)
sess = session.Session(auth=v3_auth)
# Try to get a v3 endpoint
endpoint = sess.get_endpoint(service_type=self.IDENTITY,
interface='public',
version=(3, 0))
self.assertEqual(self.V3_URL, endpoint)
def test_returns_original_skipping_discovery(self):
token = fixture.V2Token()
service = token.add_service(self.IDENTITY)