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.project_domain_id = self.auth_ref.project_domain_id
self.auth_url = self.auth_ref.auth_url[0] self.auth_url = self.auth_ref.auth_url[0]
self._management_url = self.auth_ref.management_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 self.trust_id = self.auth_ref.trust_id
if self.auth_ref.has_service_catalog(): if self.auth_ref.has_service_catalog():
self.region_name = self.auth_ref.service_catalog.region_name self.region_name = self.auth_ref.service_catalog.region_name
@@ -223,6 +223,7 @@ class HTTPClient(object):
self._endpoint = endpoint.rstrip('/') self._endpoint = endpoint.rstrip('/')
if region_name: if region_name:
self.region_name = region_name self.region_name = region_name
self._auth_token = None
if not session: if not session:
verify = cacert or True verify = cacert or True
@@ -265,20 +266,28 @@ class HTTPClient(object):
@property @property
def auth_token(self): def auth_token(self):
if self.auth_token_from_user: if self._auth_token:
return self.auth_token_from_user return self._auth_token
if self.auth_ref: if self.auth_ref:
if self.auth_ref.will_expire_soon(self.stale_duration): if self.auth_ref.will_expire_soon(self.stale_duration):
self.authenticate() self.authenticate()
return self.auth_ref.auth_token return self.auth_ref.auth_token
if self.auth_token_from_user:
return self.auth_token_from_user
@auth_token.setter @auth_token.setter
def auth_token(self, value): 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 @auth_token.deleter
def auth_token(self): def auth_token(self):
del self.auth_token_from_user self._auth_token = None
self.auth_token_from_user = None
@property @property
def service_catalog(self): def service_catalog(self):

View File

@@ -18,6 +18,7 @@ import json
import httpretty import httpretty
from keystoneclient import exceptions from keystoneclient import exceptions
from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import timeutils from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v2_0 import utils from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import client from keystoneclient.v2_0 import client
@@ -157,6 +158,27 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog) self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) 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 @httpretty.activate
def test_authenticate_success_token_scoped(self): def test_authenticate_success_token_scoped(self):
del self.TEST_REQUEST_BODY['auth']['passwordCredentials'] del self.TEST_REQUEST_BODY['auth']['passwordCredentials']
@@ -206,3 +228,45 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog) self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) 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): class TestCase(UnauthenticatedTestCase):
TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v2.0"
TEST_SERVICE_CATALOG = [{ TEST_SERVICE_CATALOG = [{
"endpoints": [{ "endpoints": [{
"adminURL": "http://cdn.admin-nets.local:8774/v1.0", "adminURL": "http://cdn.admin-nets.local:8774/v1.0",
@@ -60,7 +62,7 @@ class TestCase(UnauthenticatedTestCase):
"name": "glance" "name": "glance"
}, { }, {
"endpoints": [{ "endpoints": [{
"adminURL": "http://127.0.0.1:35357/v2.0", "adminURL": TEST_ADMIN_IDENTITY_ENDPOINT,
"region": "RegionOne", "region": "RegionOne",
"internalURL": "http://127.0.0.1:5000/v2.0", "internalURL": "http://127.0.0.1:5000/v2.0",
"publicURL": "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 import httpretty
from keystoneclient import exceptions from keystoneclient import exceptions
from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests.v3 import utils from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import client from keystoneclient.v3 import client
@@ -224,6 +225,27 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.assertFalse('catalog' in cs.service_catalog.catalog) self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) 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 @httpretty.activate
def test_authenticate_success_token_domain_scoped(self): def test_authenticate_success_token_domain_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity'] ident = self.TEST_REQUEST_BODY['auth']['identity']
@@ -301,3 +323,45 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertFalse('catalog' in cs.service_catalog.catalog) self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) 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): class TestCase(UnauthenticatedTestCase):
TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v3"
TEST_SERVICE_CATALOG = [{ TEST_SERVICE_CATALOG = [{
"endpoints": [{ "endpoints": [{
"url": "http://cdn.admin-nets.local:8774/v1.0/", "url": "http://cdn.admin-nets.local:8774/v1.0/",
@@ -105,7 +107,7 @@ class TestCase(UnauthenticatedTestCase):
"region": "RegionOne", "region": "RegionOne",
"interface": "internal" "interface": "internal"
}, { }, {
"url": "http://127.0.0.1:35357/v3", "url": TEST_ADMIN_IDENTITY_ENDPOINT,
"region": "RegionOne", "region": "RegionOne",
"interface": "admin" "interface": "admin"
}], }],