Correctly handle auth_url/token authentication

Previously the client assumed that if a user passed a token then this
token should be used for everything. This assumption is correct for the
endpoint/token case but not in the auth_url/token case where you will
want to fetch a new token. This is needed in the case where you want to
use an existing token to fetch a token that is re-scoped or activate a
trust.

There are still problems such as if you use auth_url/token
authentication then when the token expires it will try to refresh it,
but authenticating with a token will not extend the token expiry.

Closes-Bug: #1257541
Change-Id: I1c35600ca5437da44071dcea5361bfb42f6b72a3
This commit is contained in:
Jamie Lennox
2013-11-22 11:14:49 +10:00
parent 6ad5f2f4cc
commit 35aed518a9
5 changed files with 148 additions and 7 deletions

View File

@@ -161,7 +161,7 @@ class HTTPClient(object):
self.project_domain_id = self.auth_ref.project_domain_id
self.auth_url = self.auth_ref.auth_url[0]
self._management_url = self.auth_ref.management_url[0]
self.auth_token = self.auth_ref.auth_token
self.auth_token_from_user = self.auth_ref.auth_token
self.trust_id = self.auth_ref.trust_id
if self.auth_ref.has_service_catalog():
self.region_name = self.auth_ref.service_catalog.region_name
@@ -223,6 +223,7 @@ class HTTPClient(object):
self._endpoint = endpoint.rstrip('/')
if region_name:
self.region_name = region_name
self._auth_token = None
if not session:
verify = cacert or True
@@ -265,20 +266,28 @@ class HTTPClient(object):
@property
def auth_token(self):
if self.auth_token_from_user:
return self.auth_token_from_user
if self._auth_token:
return self._auth_token
if self.auth_ref:
if self.auth_ref.will_expire_soon(self.stale_duration):
self.authenticate()
return self.auth_ref.auth_token
if self.auth_token_from_user:
return self.auth_token_from_user
@auth_token.setter
def auth_token(self, value):
self.auth_token_from_user = value
"""Override the auth_token.
If an application sets auth_token explicitly then it will always be
used and override any past or future retrieved token.
"""
self._auth_token = value
@auth_token.deleter
def auth_token(self):
del self.auth_token_from_user
self._auth_token = None
self.auth_token_from_user = None
@property
def service_catalog(self):

View File

@@ -18,6 +18,7 @@ import json
import httpretty
from keystoneclient import exceptions
from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import client
@@ -157,6 +158,27 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_auth_url_token_authentication(self):
fake_token = 'fake_token'
fake_url = '/fake-url'
fake_resp = {'result': True}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
self.stub_url('GET', [fake_url], json=fake_resp,
base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT)
cl = client.Client(auth_url=self.TEST_URL,
token=fake_token)
body = jsonutils.loads(httpretty.last_request().body)
self.assertEqual(body['auth']['token']['id'], fake_token)
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)
@httpretty.activate
def test_authenticate_success_token_scoped(self):
del self.TEST_REQUEST_BODY['auth']['passwordCredentials']
@@ -206,3 +228,45 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_allow_override_of_auth_token(self):
fake_url = '/fake-url'
fake_token = 'fake_token'
fake_resp = {'result': True}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
self.stub_url('GET', [fake_url], json=fake_resp,
base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT)
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
auth_url=self.TEST_URL)
self.assertEqual(cl.auth_token, self.TEST_TOKEN)
# the token returned from the authentication will be used
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)
# then override that token and the new token shall be used
cl.auth_token = fake_token
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
fake_token)
# if we clear that overriden token then we fall back to the original
del cl.auth_token
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)

View File

@@ -31,6 +31,8 @@ class UnauthenticatedTestCase(utils.TestCase):
class TestCase(UnauthenticatedTestCase):
TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v2.0"
TEST_SERVICE_CATALOG = [{
"endpoints": [{
"adminURL": "http://cdn.admin-nets.local:8774/v1.0",
@@ -60,7 +62,7 @@ class TestCase(UnauthenticatedTestCase):
"name": "glance"
}, {
"endpoints": [{
"adminURL": "http://127.0.0.1:35357/v2.0",
"adminURL": TEST_ADMIN_IDENTITY_ENDPOINT,
"region": "RegionOne",
"internalURL": "http://127.0.0.1:5000/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"

View File

@@ -15,6 +15,7 @@
import httpretty
from keystoneclient import exceptions
from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import client
@@ -224,6 +225,27 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_auth_url_token_authentication(self):
fake_token = 'fake_token'
fake_url = '/fake-url'
fake_resp = {'result': True}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
self.stub_url('GET', [fake_url], json=fake_resp,
base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT)
cl = client.Client(auth_url=self.TEST_URL,
token=fake_token)
body = jsonutils.loads(httpretty.last_request().body)
self.assertEqual(body['auth']['identity']['token']['id'], fake_token)
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)
@httpretty.activate
def test_authenticate_success_token_domain_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
@@ -301,3 +323,45 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_allow_override_of_auth_token(self):
fake_url = '/fake-url'
fake_token = 'fake_token'
fake_resp = {'result': True}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
self.stub_url('GET', [fake_url], json=fake_resp,
base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT)
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
auth_url=self.TEST_URL)
self.assertEqual(cl.auth_token, self.TEST_TOKEN)
# the token returned from the authentication will be used
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)
# then override that token and the new token shall be used
cl.auth_token = fake_token
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
fake_token)
# if we clear that overriden token then we fall back to the original
del cl.auth_token
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'),
self.TEST_TOKEN)

View File

@@ -49,6 +49,8 @@ class UnauthenticatedTestCase(utils.TestCase):
class TestCase(UnauthenticatedTestCase):
TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v3"
TEST_SERVICE_CATALOG = [{
"endpoints": [{
"url": "http://cdn.admin-nets.local:8774/v1.0/",
@@ -105,7 +107,7 @@ class TestCase(UnauthenticatedTestCase):
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://127.0.0.1:35357/v3",
"url": TEST_ADMIN_IDENTITY_ENDPOINT,
"region": "RegionOne",
"interface": "admin"
}],