Merge "Implements token expiration handling"
This commit is contained in:
@@ -67,7 +67,6 @@ class HTTPClient(object):
|
||||
self.tenant_id = None
|
||||
self.tenant_name = None
|
||||
self.auth_url = None
|
||||
self.auth_token = None
|
||||
self.management_url = None
|
||||
if timeout is not None:
|
||||
self.timeout = float(timeout)
|
||||
@@ -82,7 +81,6 @@ class HTTPClient(object):
|
||||
self.tenant_name = self.auth_ref.tenant_name
|
||||
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
|
||||
# allow override of the auth_ref defaults from explicit
|
||||
# values provided to the client
|
||||
if username:
|
||||
@@ -94,7 +92,9 @@ class HTTPClient(object):
|
||||
if auth_url:
|
||||
self.auth_url = auth_url.rstrip('/')
|
||||
if token:
|
||||
self.auth_token = token
|
||||
self.auth_token_from_user = token
|
||||
else:
|
||||
self.auth_token_from_user = None
|
||||
if endpoint:
|
||||
self.management_url = endpoint.rstrip('/')
|
||||
self.password = password
|
||||
@@ -126,6 +126,23 @@ class HTTPClient(object):
|
||||
self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
|
||||
self.stale_duration = int(self.stale_duration)
|
||||
|
||||
@property
|
||||
def auth_token(self):
|
||||
if self.auth_token_from_user:
|
||||
return self.auth_token_from_user
|
||||
if self.auth_ref:
|
||||
if self.auth_ref.will_expire_soon(self.stale_duration):
|
||||
self.authenticate()
|
||||
return self.auth_ref.auth_token
|
||||
|
||||
@auth_token.setter
|
||||
def auth_token(self, value):
|
||||
self.auth_token_from_user = value
|
||||
|
||||
@auth_token.deleter
|
||||
def auth_token(selef):
|
||||
del self.auth_token_from_user
|
||||
|
||||
def authenticate(self, username=None, password=None, tenant_name=None,
|
||||
tenant_id=None, auth_url=None, token=None):
|
||||
""" Authenticate user.
|
||||
@@ -165,7 +182,12 @@ class HTTPClient(object):
|
||||
password = password or self.password
|
||||
tenant_name = tenant_name or self.tenant_name
|
||||
tenant_id = tenant_id or self.tenant_id
|
||||
token = token or self.auth_token
|
||||
|
||||
if not token:
|
||||
token = self.auth_token_from_user
|
||||
if (not token and self.auth_ref
|
||||
and not self.auth_ref.will_expire_soon(self.stale_duration)):
|
||||
token = self.auth_ref.auth_token
|
||||
|
||||
(keyring_key, auth_ref) = self.get_auth_ref_from_keyring(auth_url,
|
||||
username,
|
||||
@@ -224,7 +246,7 @@ class HTTPClient(object):
|
||||
keyring_key)
|
||||
if auth_ref:
|
||||
auth_ref = pickle.loads(auth_ref)
|
||||
if auth_ref.will_expire_soon(self.stale_duration):
|
||||
if self.auth_ref.will_expire_soon(self.stale_duration):
|
||||
# token has expired, don't use it
|
||||
auth_ref = None
|
||||
except Exception as e:
|
||||
|
@@ -146,7 +146,6 @@ class Client(client.HTTPClient):
|
||||
# if we got a response without a service catalog, set the local
|
||||
# list of tenants for introspection, and leave to client user
|
||||
# to determine what to do. Otherwise, load up the service catalog
|
||||
self.auth_token = self.auth_ref.auth_token
|
||||
if self.auth_ref.scoped:
|
||||
if self.management_url is None and self.auth_ref.management_url:
|
||||
self.management_url = self.auth_ref.management_url[0]
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from keystoneclient.v2_0 import client
|
||||
from keystoneclient import exceptions
|
||||
from keystoneclient.openstack.common import timeutils
|
||||
from tests import utils
|
||||
|
||||
|
||||
@@ -14,7 +16,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
self.TEST_RESPONSE_DICT = {
|
||||
"access": {
|
||||
"token": {
|
||||
"expires": "12345",
|
||||
"expires": "2999-01-01T00:00:10Z",
|
||||
"id": self.TEST_TOKEN,
|
||||
"tenant": {
|
||||
"id": self.TEST_TENANT_ID
|
||||
@@ -40,6 +42,49 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
'User-Agent': 'python-keystoneclient',
|
||||
}
|
||||
|
||||
def test_authenticate_success_expired(self):
|
||||
# Build an expired token
|
||||
self.TEST_RESPONSE_DICT['access']['token']['expires'] = \
|
||||
(timeutils.utcnow() - timedelta(1)).isoformat()
|
||||
resp = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": json.dumps(self.TEST_RESPONSE_DICT),
|
||||
})
|
||||
|
||||
kwargs = copy.copy(self.TEST_REQUEST_BASE)
|
||||
kwargs['headers'] = self.TEST_REQUEST_HEADERS
|
||||
kwargs['data'] = json.dumps(self.TEST_REQUEST_BODY)
|
||||
requests.request('POST',
|
||||
self.TEST_URL + "/tokens",
|
||||
**kwargs).AndReturn((resp))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
cs = client.Client(tenant_id=self.TEST_TENANT_ID,
|
||||
auth_url=self.TEST_URL,
|
||||
username=self.TEST_USER,
|
||||
password=self.TEST_TOKEN)
|
||||
self.assertEqual(cs.management_url,
|
||||
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
|
||||
['endpoints'][0]["adminURL"])
|
||||
|
||||
# Build a new response
|
||||
self.mox.ResetAll()
|
||||
TEST_TOKEN = "abcdef"
|
||||
self.TEST_RESPONSE_DICT['access']['token']['expires'] = \
|
||||
"2999-01-01T00:00:10Z"
|
||||
self.TEST_RESPONSE_DICT['access']['token']['id'] = TEST_TOKEN
|
||||
|
||||
resp = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": json.dumps(self.TEST_RESPONSE_DICT),
|
||||
})
|
||||
requests.request('POST',
|
||||
self.TEST_URL + "/tokens",
|
||||
**kwargs).AndReturn((resp))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.assertEqual(cs.auth_token, TEST_TOKEN)
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
_auth = 'auth'
|
||||
_cred = 'passwordCredentials'
|
||||
|
Reference in New Issue
Block a user