Skip to content

Commit

Permalink
Handle auth_token and endpoint_url if passed to the http client const…
Browse files Browse the repository at this point in the history
…ructor

Fixes bug 1152427

Change-Id: Ic7811d928fd00cde0a72f451b5ede8351092a54c
  • Loading branch information
Oleg Bondarev committed Mar 26, 2013
1 parent 661aea1 commit a54dd21
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 30 deletions.
47 changes: 30 additions & 17 deletions quantumclient/client.py
Expand Up @@ -101,7 +101,6 @@ def __init__(self, username=None, tenant_name=None,
self.auth_url = auth_url.rstrip('/') if auth_url else None
self.region_name = region_name
self.auth_token = token
self.token_retrieved = False
self.content_type = 'application/json'
self.endpoint_url = endpoint_url
self.auth_strategy = auth_strategy
Expand Down Expand Up @@ -134,30 +133,27 @@ def _cs_request(self, *args, **kwargs):
return resp, body

def do_request(self, url, method, **kwargs):
if not self.endpoint_url:
if not self.auth_token:
self.authenticate()
elif not self.endpoint_url:
self.endpoint_url = self._get_endpoint_url()

# Perform the request once. If we get a 401 back then it
# might be because the auth token expired, so try to
# re-authenticate and try again. If it still fails, bail.
try:
if self.auth_token:
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(self.endpoint_url + url, method,
**kwargs)
return resp, body
except exceptions.Unauthorized as ex:
if not self.endpoint_url or self.token_retrieved:
self.authenticate()
if self.auth_token:
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(
self.endpoint_url + url, method, **kwargs)
return resp, body
else:
raise ex
except exceptions.Unauthorized:
self.authenticate()
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(
self.endpoint_url + url, method, **kwargs)
return resp, body

def _extract_service_catalog(self, body):
""" Set the client's service catalog from the response data. """
Expand All @@ -167,7 +163,6 @@ def _extract_service_catalog(self, body):
self.auth_token = sc['id']
self.auth_tenant_id = sc.get('tenant_id')
self.auth_user_id = sc.get('user_id')
self.token_retrieved = True
except KeyError:
raise exceptions.Unauthorized()
self.endpoint_url = self.service_catalog.url_for(
Expand Down Expand Up @@ -205,6 +200,24 @@ def authenticate(self):
body = None
self._extract_service_catalog(body)

def _get_endpoint_url(self):
url = self.auth_url + '/tokens/%s/endpoints' % self.auth_token
try:
resp, body = self._cs_request(url, "GET")
except exceptions.Unauthorized:
# rollback to authenticate() to handle case when quantum client
# is initialized just before the token is expired
self.authenticate()
return self.endpoint_url

body = json.loads(body)
for endpoint in body.get('endpoints', []):
if (endpoint['type'] == 'network' and
endpoint.get('region') == self.region_name):
return endpoint['adminURL']

raise exceptions.EndpointNotFound()

def get_status_code(self, response):
"""
Returns the integer status code from the response, which
Expand Down
57 changes: 44 additions & 13 deletions quantumclient/tests/unit/test_auth.py
Expand Up @@ -17,15 +17,13 @@

import httplib2
import json
import unittest
import uuid

import mox
from mox import ContainsKeyValue, IsA, StrContains
import testtools

from quantumclient.client import HTTPClient
from quantumclient.common import exceptions


USERNAME = 'testuser'
Expand Down Expand Up @@ -54,6 +52,17 @@
}
}

ENDPOINTS_RESULT = {
'endpoints': [{
'type': 'network',
'name': 'Quantum Service',
'region': REGION,
'adminURL': ENDPOINT_URL,
'internalURL': ENDPOINT_URL,
'publicURL': ENDPOINT_URL
}]
}


class CLITestAuthKeystone(testtools.TestCase):

Expand Down Expand Up @@ -84,46 +93,68 @@ def test_get_token(self):
self.client.do_request('/resource', 'GET')
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
self.assertEqual(self.client.auth_token, TOKEN)
self.assertEqual(self.client.token_retrieved, True)

def test_already_token_retrieved(self):
def test_refresh_token(self):
self.mox.StubOutWithMock(self.client, "request")

self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
self.client.token_retrieved = True

res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
res401 = self.mox.CreateMock(httplib2.Response)
res401.status = 401

# If a token is expired, quantum server retruns 401
self.client.request(StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=ContainsKeyValue('X-Auth-Token', TOKEN)).\
AndReturn((res401, ''))
self.client.request(AUTH_URL + '/tokens', 'POST',
body=IsA(str), headers=IsA(dict)).\
AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=ContainsKeyValue('X-Auth-Token', TOKEN)).\
AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')

def test_get_endpoint_url(self):
self.mox.StubOutWithMock(self.client, "request")

self.client.auth_token = TOKEN

res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200

self.client.request(StrContains(AUTH_URL +
'/tokens/%s/endpoints' % TOKEN), 'GET',
headers=IsA(dict)). \
AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
self.client.request(StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=ContainsKeyValue('X-Auth-Token', TOKEN)). \
AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')

def test_refresh_token(self):
def test_get_endpoint_url_failed(self):
self.mox.StubOutWithMock(self.client, "request")

self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
self.client.token_retrieved = True

res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
res401 = self.mox.CreateMock(httplib2.Response)
res401.status = 401

# If a token is expired, quantum server retruns 401
self.client.request(StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=ContainsKeyValue('X-Auth-Token', TOKEN)).\
self.client.request(StrContains(AUTH_URL +
'/tokens/%s/endpoints' % TOKEN), 'GET',
headers=IsA(dict)). \
AndReturn((res401, ''))
self.client.request(AUTH_URL + '/tokens', 'POST',
body=IsA(str), headers=IsA(dict)).\
body=IsA(str), headers=IsA(dict)). \
AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=ContainsKeyValue('X-Auth-Token', TOKEN)).\
headers=ContainsKeyValue('X-Auth-Token', TOKEN)). \
AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')

0 comments on commit a54dd21

Please sign in to comment.