Merge "Control identity plugin reauthentication"
This commit is contained in:
commit
a505ffbf17
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue