summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie Lennox <jamielennox@redhat.com>2014-07-17 13:25:37 +1000
committerJamie Lennox <jamielennox@redhat.com>2014-08-04 12:14:41 +1000
commite2f1845549258b1e01adca0f3979b9f1c0f475bf (patch)
tree5cad279c94682cce54a08e7e03771c7fe847c61d
parentfe0ba8cd446081651f2f891157406a1b0ff1fb1f (diff)
downloadpython-keystoneclient-e2f1845549258b1e01adca0f3979b9f1c0f475bf.tar.gz
Control identity plugin reauthentication
Identity plugins will by default re-authenticate themselves if they are about to expire. This is generally correct however there are times where this re-authentication doesn't make sense and we should be able to prevent it. Closes-Bug: #1352051 Change-Id: I66b50b1e650501e7f076139895473e8d1791ce27
-rw-r--r--keystoneclient/auth/identity/base.py29
-rw-r--r--keystoneclient/auth/identity/v2.py9
-rw-r--r--keystoneclient/auth/identity/v3.py9
-rw-r--r--keystoneclient/tests/auth/test_identity_common.py68
4 files changed, 91 insertions, 24 deletions
diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py
index fb5ed7a..ffdee4d 100644
--- a/keystoneclient/auth/identity/base.py
+++ b/keystoneclient/auth/identity/base.py
@@ -34,12 +34,14 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
username=None,
password=None,
token=None,
- trust_id=None):
+ trust_id=None,
+ reauthenticate=True):
super(BaseIdentityPlugin, self).__init__()
self.auth_url = auth_url
self.auth_ref = None
+ self.reauthenticate = reauthenticate
self._endpoint_cache = {}
@@ -81,6 +83,28 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
"""
return self.get_access(session).auth_token
+ def _needs_reauthenticate(self):
+ """Return if the existing token needs to be re-authenticated.
+
+ The token should be refreshed if it is about to expire.
+
+ :returns: True if the plugin should fetch a new token. False otherwise.
+ """
+ if not self.auth_ref:
+ # authentication was never fetched.
+ return True
+
+ if not self.reauthenticate:
+ # don't re-authenticate if it has been disallowed.
+ return False
+
+ if self.auth_ref.will_expire_soon(self.MIN_TOKEN_LIFE_SECONDS):
+ # if it's about to expire we should re-authenticate now.
+ return True
+
+ # otherwise it's fine and use the existing one.
+ return False
+
def get_access(self, session, **kwargs):
"""Fetch or return a current AccessInfo object.
@@ -91,8 +115,7 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
:returns AccessInfo: Valid AccessInfo
"""
- if (not self.auth_ref or
- self.auth_ref.will_expire_soon(self.MIN_TOKEN_LIFE_SECONDS)):
+ if self._needs_reauthenticate():
self.auth_ref = self.get_auth_ref(session)
return self.auth_ref
diff --git a/keystoneclient/auth/identity/v2.py b/keystoneclient/auth/identity/v2.py
index ad93dba..72f6259 100644
--- a/keystoneclient/auth/identity/v2.py
+++ b/keystoneclient/auth/identity/v2.py
@@ -43,15 +43,20 @@ class Auth(base.BaseIdentityPlugin):
def __init__(self, auth_url,
trust_id=None,
tenant_id=None,
- tenant_name=None):
+ tenant_name=None,
+ reauthenticate=True):
"""Construct an Identity V2 Authentication Plugin.
:param string auth_url: Identity service endpoint for authorization.
:param string trust_id: Trust ID for trust scoping.
:param string tenant_id: Tenant ID for project scoping.
:param string tenant_name: Tenant name for project scoping.
+ :param bool reauthenticate: Allow fetching a new token if the current
+ one is going to expire.
+ (optional) default True
"""
- super(Auth, self).__init__(auth_url=auth_url)
+ super(Auth, self).__init__(auth_url=auth_url,
+ reauthenticate=reauthenticate)
self.trust_id = trust_id
self.tenant_id = tenant_id
diff --git a/keystoneclient/auth/identity/v3.py b/keystoneclient/auth/identity/v3.py
index 010c80d..b385ba6 100644
--- a/keystoneclient/auth/identity/v3.py
+++ b/keystoneclient/auth/identity/v3.py
@@ -34,7 +34,8 @@ class Auth(base.BaseIdentityPlugin):
project_id=None,
project_name=None,
project_domain_id=None,
- project_domain_name=None):
+ project_domain_name=None,
+ reauthenticate=True):
"""Construct an Identity V3 Authentication Plugin.
:param string auth_url: Identity service endpoint for authentication.
@@ -46,9 +47,13 @@ class Auth(base.BaseIdentityPlugin):
:param string project_name: Project name for project scoping.
:param string project_domain_id: Project's domain ID for project.
:param string project_domain_name: Project's domain name for project.
+ :param bool reauthenticate: Allow fetching a new token if the current
+ one is going to expire.
+ (optional) default True
"""
- super(Auth, self).__init__(auth_url=auth_url)
+ super(Auth, self).__init__(auth_url=auth_url,
+ reauthenticate=reauthenticate)
self.auth_methods = auth_methods
self.trust_id = trust_id
diff --git a/keystoneclient/tests/auth/test_identity_common.py b/keystoneclient/tests/auth/test_identity_common.py
index 33bb5c1..8c5499c 100644
--- a/keystoneclient/tests/auth/test_identity_common.py
+++ b/keystoneclient/tests/auth/test_identity_common.py
@@ -11,14 +11,17 @@
# under the License.
import abc
+import datetime
import uuid
import six
+from keystoneclient import access
from keystoneclient.auth import base
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
from keystoneclient import fixture
+from keystoneclient.openstack.common import timeutils
from keystoneclient import session
from keystoneclient.tests import utils
@@ -45,7 +48,7 @@ class CommonIdentityTests(object):
self.stub_auth_data()
@abc.abstractmethod
- def create_auth_plugin(self):
+ def create_auth_plugin(self, **kwargs):
"""Create an auth plugin that makes sense for the auth data.
It doesn't really matter what auth mechanism is used but it should be
@@ -53,13 +56,17 @@ class CommonIdentityTests(object):
"""
@abc.abstractmethod
- def stub_auth_data(self):
- """Stub out authentication data.
+ def get_auth_data(self, **kwargs):
+ """Return fake authentication data.
This should register a valid token response and ensure that the compute
endpoints are set to TEST_COMPUTE_PUBLIC, _INTERNAL and _ADMIN.
"""
+ def stub_auth_data(self, **kwargs):
+ token = self.get_auth_data(**kwargs)
+ self.stub_auth(json=token)
+
@abc.abstractproperty
def version(self):
"""The API version being tested."""
@@ -177,6 +184,31 @@ class CommonIdentityTests(object):
self.assertEqual(self.TEST_URL, auth_url)
+ def _create_expired_auth_plugin(self, **kwargs):
+ expires = timeutils.utcnow() - datetime.timedelta(minutes=20)
+ expired_token = self.get_auth_data(expires=expires)
+ expired_auth_ref = access.AccessInfo.factory(body=expired_token)
+
+ body = 'SUCCESS'
+ self.stub_url('GET', ['path'],
+ base_url=self.TEST_COMPUTE_ADMIN, body=body)
+
+ a = self.create_auth_plugin(**kwargs)
+ a.auth_ref = expired_auth_ref
+ return a
+
+ def test_reauthenticate(self):
+ a = self._create_expired_auth_plugin()
+ expired_auth_ref = a.auth_ref
+ s = session.Session(auth=a)
+ self.assertIsNot(expired_auth_ref, a.get_access(s))
+
+ def test_no_reauthenticate(self):
+ a = self._create_expired_auth_plugin(reauthenticate=False)
+ expired_auth_ref = a.auth_ref
+ s = session.Session(auth=a)
+ self.assertIs(expired_auth_ref, a.get_access(s))
+
class V3(CommonIdentityTests, utils.TestCase):
@@ -184,8 +216,8 @@ class V3(CommonIdentityTests, utils.TestCase):
def version(self):
return 'v3'
- def stub_auth_data(self):
- token = fixture.V3Token()
+ def get_auth_data(self, **kwargs):
+ token = fixture.V3Token(**kwargs)
region = 'RegionOne'
svc = token.add_service('identity')
@@ -197,7 +229,7 @@ class V3(CommonIdentityTests, utils.TestCase):
internal=self.TEST_COMPUTE_INTERNAL,
region=region)
- self.stub_auth(json=token)
+ return token
def stub_auth(self, subject_token=None, **kwargs):
if not subject_token:
@@ -206,10 +238,11 @@ class V3(CommonIdentityTests, utils.TestCase):
kwargs.setdefault('headers', {})['X-Subject-Token'] = subject_token
self.stub_url('POST', ['auth', 'tokens'], **kwargs)
- def create_auth_plugin(self):
- return v3.Password(self.TEST_URL,
- username=self.TEST_USER,
- password=self.TEST_PASS)
+ def create_auth_plugin(self, **kwargs):
+ kwargs.setdefault('auth_url', self.TEST_URL)
+ kwargs.setdefault('username', self.TEST_USER)
+ kwargs.setdefault('password', self.TEST_PASS)
+ return v3.Password(**kwargs)
class V2(CommonIdentityTests, utils.TestCase):
@@ -218,13 +251,14 @@ class V2(CommonIdentityTests, utils.TestCase):
def version(self):
return 'v2.0'
- def create_auth_plugin(self):
- return v2.Password(self.TEST_URL,
- username=self.TEST_USER,
- password=self.TEST_PASS)
+ def create_auth_plugin(self, **kwargs):
+ kwargs.setdefault('auth_url', self.TEST_URL)
+ kwargs.setdefault('username', self.TEST_USER)
+ kwargs.setdefault('password', self.TEST_PASS)
+ return v2.Password(**kwargs)
- def stub_auth_data(self):
- token = fixture.V2Token()
+ def get_auth_data(self, **kwargs):
+ token = fixture.V2Token(**kwargs)
region = 'RegionOne'
svc = token.add_service('identity')
@@ -236,7 +270,7 @@ class V2(CommonIdentityTests, utils.TestCase):
admin=self.TEST_COMPUTE_ADMIN,
region=region)
- self.stub_auth(json=token)
+ return token
def stub_auth(self, **kwargs):
self.stub_url('POST', ['tokens'], **kwargs)