Create Authentication Plugins
Provides the framework for creating authentication plugins and using them from a session object. To allow this system to co-exist with the original client there is a bit of a hack. The client object itself is now also an authentication plugin, that supports the original client pattern. If a client is created without a session object then that session object uses the client as it's authentication plugin. Change-Id: I682c8dcd3705148aaa804a91f4ed48a5b74bdc12 blueprint: auth-plugins
This commit is contained in:
parent
6e21c94bb5
commit
015213d1ee
0
keystoneclient/auth/__init__.py
Normal file
0
keystoneclient/auth/__init__.py
Normal file
38
keystoneclient/auth/base.py
Normal file
38
keystoneclient/auth/base.py
Normal file
@ -0,0 +1,38 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseAuthPlugin(object):
|
||||
"""The basic structure of an authentication plugin."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_token(self, session, **kwargs):
|
||||
"""Obtain a token.
|
||||
|
||||
How the token is obtained is up to the plugin. If it is still valid
|
||||
it may be re-used, retrieved from cache or invoke an authentication
|
||||
request against a server.
|
||||
|
||||
There are no required kwargs. They are passed directly to the auth
|
||||
plugin and they are implementation specific.
|
||||
|
||||
Returning None will indicate that no token was able to be retrieved.
|
||||
|
||||
:param session: A session object so the plugin can make HTTP calls.
|
||||
:return string: A token to use.
|
||||
"""
|
0
keystoneclient/auth/identity/__init__.py
Normal file
0
keystoneclient/auth/identity/__init__.py
Normal file
60
keystoneclient/auth/identity/base.py
Normal file
60
keystoneclient/auth/identity/base.py
Normal file
@ -0,0 +1,60 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import logging
|
||||
import six
|
||||
|
||||
from keystoneclient.auth import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseIdentityPlugin(base.BaseAuthPlugin):
|
||||
|
||||
def __init__(self,
|
||||
auth_url=None,
|
||||
username=None,
|
||||
password=None,
|
||||
token=None,
|
||||
trust_id=None):
|
||||
|
||||
super(BaseIdentityPlugin, self).__init__()
|
||||
|
||||
self.auth_url = auth_url
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.token = token
|
||||
self.trust_id = trust_id
|
||||
|
||||
self.auth_ref = None
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_auth_ref(self, session, **kwargs):
|
||||
"""Obtain a token from an OpenStack Identity Service.
|
||||
|
||||
This method is overridden by the various token version plugins.
|
||||
|
||||
This function should not be called independently and is expected to be
|
||||
invoked via the do_authenticate function.
|
||||
|
||||
:returns AccessInfo: Token access information.
|
||||
"""
|
||||
|
||||
def get_token(self, session, **kwargs):
|
||||
if not self.auth_ref or self.auth_ref.will_expire_soon(1):
|
||||
self.auth_ref = self.get_auth_ref(session, **kwargs)
|
||||
|
||||
return self.auth_ref.auth_token
|
@ -49,3 +49,7 @@ class DiscoveryFailure(ClientException):
|
||||
|
||||
class VersionNotAvailable(DiscoveryFailure):
|
||||
"""Discovery failed as the version you requested is not available."""
|
||||
|
||||
|
||||
class MissingAuthPlugin(ClientException):
|
||||
"""An authenticated request is required but no plugin available."""
|
||||
|
@ -37,14 +37,18 @@ class Session(object):
|
||||
REDIRECT_STATUSES = (301, 302, 303, 305, 307)
|
||||
DEFAULT_REDIRECT_LIMIT = 30
|
||||
|
||||
def __init__(self, session=None, original_ip=None, verify=True, cert=None,
|
||||
timeout=None, user_agent=None,
|
||||
def __init__(self, auth=None, session=None, original_ip=None, verify=True,
|
||||
cert=None, timeout=None, user_agent=None,
|
||||
redirect=DEFAULT_REDIRECT_LIMIT):
|
||||
"""Maintains client communication state and common functionality.
|
||||
|
||||
As much as possible the parameters to this class reflect and are passed
|
||||
directly to the requests library.
|
||||
|
||||
:param auth: An authentication plugin to authenticate the session with.
|
||||
(optional, defaults to None)
|
||||
:param requests.Session session: A requests session object that can be
|
||||
used for issuing requests. (optional)
|
||||
:param string original_ip: The original IP of the requesting user
|
||||
which will be sent to identity service in a
|
||||
'Forwarded' header. (optional)
|
||||
@ -74,6 +78,7 @@ class Session(object):
|
||||
if not session:
|
||||
session = requests.Session()
|
||||
|
||||
self.auth = auth
|
||||
self.session = session
|
||||
self.original_ip = original_ip
|
||||
self.verify = verify
|
||||
@ -89,7 +94,8 @@ class Session(object):
|
||||
self.user_agent = user_agent
|
||||
|
||||
def request(self, url, method, json=None, original_ip=None,
|
||||
user_agent=None, redirect=None, **kwargs):
|
||||
user_agent=None, redirect=None, authenticated=None,
|
||||
**kwargs):
|
||||
"""Send an HTTP request with the specified characteristics.
|
||||
|
||||
Wrapper around `requests.Session.request` to handle tasks such as
|
||||
@ -111,6 +117,10 @@ class Session(object):
|
||||
can be followed by a request. Either an
|
||||
integer for a specific count or True/False
|
||||
for forever/never. (optional)
|
||||
:param bool authenticated: True if a token should be attached to this
|
||||
request, False if not or None for attach if
|
||||
an auth_plugin is available.
|
||||
(optional, defaults to None)
|
||||
:param kwargs: any other parameter that can be passed to
|
||||
requests.Session.request (such as `headers`). Except:
|
||||
'data' will be overwritten by the data in 'json' param.
|
||||
@ -125,6 +135,17 @@ class Session(object):
|
||||
|
||||
headers = kwargs.setdefault('headers', dict())
|
||||
|
||||
if authenticated is None:
|
||||
authenticated = self.auth is not None
|
||||
|
||||
if authenticated:
|
||||
token = self.get_token()
|
||||
|
||||
if not token:
|
||||
raise exceptions.AuthorizationFailure("No token Available")
|
||||
|
||||
headers['X-Auth-Token'] = token
|
||||
|
||||
if self.cert:
|
||||
kwargs.setdefault('cert', self.cert)
|
||||
|
||||
@ -286,3 +307,10 @@ class Session(object):
|
||||
session=kwargs.pop('session', None),
|
||||
original_ip=kwargs.pop('original_ip', None),
|
||||
user_agent=kwargs.pop('user_agent', None))
|
||||
|
||||
def get_token(self):
|
||||
"""Return a token as provided by the auth plugin."""
|
||||
if not self.auth:
|
||||
raise exceptions.MissingAuthPlugin("Token Required")
|
||||
|
||||
return self.auth.get_token(self)
|
||||
|
Loading…
Reference in New Issue
Block a user