From 40ae1ffeaaaa7bbd57e388fc3c0a557248e63602 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Tue, 2 Sep 2014 08:36:28 -0600 Subject: [PATCH] Sync up with latest keystoneclient changes Change-Id: I895534b2d30dec8e38461e07ebf03ea8f466dad9 --- doc/source/session.rst | 2 +- examples/authenticate.py | 2 +- openstack/auth/base.py | 17 +++++++++++- openstack/auth/identity/base.py | 46 ++++++++++++++++++++++++++++++--- openstack/auth/identity/v2.py | 40 +++++++++++++++++++++++----- openstack/auth/identity/v3.py | 10 +++++-- 6 files changed, 102 insertions(+), 15 deletions(-) diff --git a/doc/source/session.rst b/doc/source/session.rst index d184ad90..664c75c3 100644 --- a/doc/source/session.rst +++ b/doc/source/session.rst @@ -18,7 +18,7 @@ Session object provides an HTTP request method. The transport is also to be used by the authenticator if needed. * authenticator - An authenticator derived from - ``openstack.auth.base.BaseAuthenticator`` that provides get_token and + ``openstack.auth.base.BaseAuthPlugin`` that provides get_token and get_endpoint methods for the session. All the other methods of the session accept the following parameters: diff --git a/examples/authenticate.py b/examples/authenticate.py index 68d8e87c..36863edc 100644 --- a/examples/authenticate.py +++ b/examples/authenticate.py @@ -33,7 +33,7 @@ from openstack.auth import base from openstack.auth.identity import authenticator -class TestAuthenticator(base.BaseAuthenticator): +class TestAuthenticator(base.BaseAuthPlugin): def __init__(self, token, endpoint): super(TestAuthenticator, self).__init__() self.token = token diff --git a/openstack/auth/base.py b/openstack/auth/base.py index 2f237900..c90efc98 100644 --- a/openstack/auth/base.py +++ b/openstack/auth/base.py @@ -16,7 +16,7 @@ import six @six.add_metaclass(abc.ABCMeta) -class BaseAuthenticator(object): +class BaseAuthPlugin(object): """The basic structure of an authenticator.""" @abc.abstractmethod @@ -54,3 +54,18 @@ class BaseAuthenticator(object): :returns string: The base URL that will be used to talk to the required service or None if not available. """ + + def invalidate(self): + """Invalidate the current authentication data. + + This should result in fetching a new token on next call. + + A plugin may be invalidated if an Unauthorized HTTP response is + returned to indicate that the token may have been revoked or is + otherwise now invalid. + + :returns bool: True if there was something that the plugin did to + invalidate. This means that it makes sense to try again. + If nothing happens returns False to indicate give up. + """ + return False diff --git a/openstack/auth/identity/base.py b/openstack/auth/identity/base.py index 5573a378..99cc3f7c 100644 --- a/openstack/auth/identity/base.py +++ b/openstack/auth/identity/base.py @@ -18,15 +18,16 @@ from openstack.auth import base @six.add_metaclass(abc.ABCMeta) -class BaseIdentityPlugin(base.BaseAuthenticator): +class BaseIdentityPlugin(base.BaseAuthPlugin): # Consider a token valid if it does not expire for this many seconds BEST_BEFORE_SECONDS = 1 - def __init__(self, auth_url=None): + def __init__(self, auth_url=None, reauthenticate=True): super(BaseIdentityPlugin, self).__init__() self.auth_url = auth_url self.access_info = None + self.reauthenticate = reauthenticate @abc.abstractmethod def authorize(self, transport, **kwargs): @@ -52,6 +53,28 @@ class BaseIdentityPlugin(base.BaseAuthenticator): """ return self.get_access(transport).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.access_info: + # authentication was never fetched. + return True + + if not self.reauthenticate: + # don't re-authenticate if it has been disallowed. + return False + + if self.access_info.will_expire_soon(self.BEST_BEFORE_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, transport): """Fetch or return a current AccessInfo object. @@ -62,12 +85,27 @@ class BaseIdentityPlugin(base.BaseAuthenticator): :returns AccessInfo: Valid AccessInfo """ - if (not self.access_info or - self.access_info.will_expire_soon(self.BEST_BEFORE_SECONDS)): + if self._needs_reauthenticate(): self.access_info = self.authorize(transport) return self.access_info + def invalidate(self): + """Invalidate the current authentication data. + + This should result in fetching a new token on next call. + + A plugin may be invalidated if an Unauthorized HTTP response is + returned to indicate that the token may have been revoked or is + otherwise now invalid. + + :returns bool: True if there was something that the plugin did to + invalidate. This means that it makes sense to try again. + If nothing happens returns False to indicate give up. + """ + self.access_info = None + return True + def get_endpoint(self, transport, service, **kwargs): """Return a valid endpoint for a service. diff --git a/openstack/auth/identity/v2.py b/openstack/auth/identity/v2.py index 9c7ad0e7..c486bb93 100644 --- a/openstack/auth/identity/v2.py +++ b/openstack/auth/identity/v2.py @@ -13,6 +13,7 @@ # under the License. import abc +import logging import six @@ -20,21 +21,29 @@ from openstack.auth import access from openstack.auth.identity import base from openstack import exceptions +_logger = logging.getLogger(__name__) + @six.add_metaclass(abc.ABCMeta) 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 @@ -42,7 +51,7 @@ class Auth(base.BaseIdentityPlugin): def authorize(self, transport, **kwargs): headers = {'Accept': 'application/json'} - url = self.auth_url + '/tokens' + url = self.auth_url.rstrip('/') + '/tokens' params = {'auth': self.get_auth_data(headers)} if self.tenant_id: @@ -52,6 +61,7 @@ class Auth(base.BaseIdentityPlugin): if self.trust_id: params['auth']['trust_id'] = self.trust_id + _logger.debug('Making authentication request to %s', url) resp = transport.post(url, json=params, headers=headers) try: @@ -73,20 +83,38 @@ class Auth(base.BaseIdentityPlugin): class Password(Auth): - def __init__(self, auth_url, username, password, **kwargs): + def __init__(self, auth_url, username=None, password=None, user_id=None, + **kwargs): """A plugin for authenticating with a username and password. + A username or user_id must be provided. + :param string auth_url: Identity service endpoint for authorization. :param string username: Username for authentication. :param string password: Password for authentication. + :param string user_id: User ID for authentication. + + :raises TypeError: if a user_id or username is not provided. """ super(Password, self).__init__(auth_url, **kwargs) + + if not (user_id or username): + msg = 'You need to specify either a username or user_id' + raise TypeError(msg) + + self.user_id = user_id self.username = username self.password = password def get_auth_data(self, headers=None): - return {'passwordCredentials': {'username': self.username, - 'password': self.password}} + auth = {'password': self.password} + + if self.username: + auth['username'] = self.username + elif self.user_id: + auth['userId'] = self.user_id + + return {'passwordCredentials': auth} class Token(Auth): diff --git a/openstack/auth/identity/v3.py b/openstack/auth/identity/v3.py index 86f62490..40a90417 100644 --- a/openstack/auth/identity/v3.py +++ b/openstack/auth/identity/v3.py @@ -33,7 +33,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. @@ -45,9 +46,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 @@ -104,6 +109,7 @@ class Auth(base.BaseIdentityPlugin): elif self.trust_id: body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}} + _logger.debug('Making authentication request to %s', self.token_url) resp = transport.post(self.token_url, json=body, headers=headers) try: