summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie Lennox <jamielennox@redhat.com>2014-07-16 10:31:21 +1000
committerJamie Lennox <jamielennox@redhat.com>2014-08-26 10:11:39 +1000
commitd26c81df440c0d1306c1aaf1c001fa6f7df37115 (patch)
treea643a0c4971e06f8b276ee4101f30430be3151d3
parente3a3dff00ff40d2cd161d0cd50b1549ae675b0f2 (diff)
downloadkeystonemiddleware-d26c81df440c0d1306c1aaf1c001fa6f7df37115.tar.gz
Create an Auth Plugin to pass to users
We pass the service catalog and token data through to consumers via headers, with the auth plugin work we can create clients that are authenticated with the incoming token and service catalog. DocImpact: A new environment option is passed out of auth_token middleware called `token_auth` which is an authentication plugin that can be used with a session in a service. Implements: blueprint create-auth-plugin Change-Id: I549a421fc73f00126f6ac5f4b5911de2e32de825
-rw-r--r--doc/source/conf.py5
-rw-r--r--keystonemiddleware/auth_token.py45
-rw-r--r--keystonemiddleware/tests/test_auth_token_middleware.py20
3 files changed, 65 insertions, 5 deletions
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 19c81d6..069382b 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -231,6 +231,7 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
+keystoneclient = 'http://docs.openstack.org/developer/python-keystoneclient/'
-# Example configuration for intersphinx: refer to the Python standard library.
-#intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
+intersphinx_mapping = {'keystoneclient': (keystoneclient, None),
+ }
diff --git a/keystonemiddleware/auth_token.py b/keystonemiddleware/auth_token.py
index a9625e7..908b923 100644
--- a/keystonemiddleware/auth_token.py
+++ b/keystonemiddleware/auth_token.py
@@ -143,6 +143,11 @@ keystone.token_info
Keystone token validation call, as well as basic information about
the tenant and user.
+keystone.token_auth
+ A keystoneclient auth plugin that may be used with a
+ :py:class:`keystoneclient.session.Session`. This plugin will load the
+ authentication data provided to auth_token middleware.
+
"""
import contextlib
@@ -154,6 +159,7 @@ import tempfile
import time
from keystoneclient import access
+from keystoneclient.auth.identity import base as base_identity
from keystoneclient.auth.identity import v2
from keystoneclient.auth import token_endpoint
from keystoneclient.common import cms
@@ -453,6 +459,38 @@ class _MiniResp(object):
self.headers.append(('Content-type', 'text/plain'))
+class _UserAuthPlugin(base_identity.BaseIdentityPlugin):
+ """The incoming authentication credentials.
+
+ A plugin that represents the incoming user credentials. This can be
+ consumed by applications.
+
+ This object is not expected to be constructed directly by users. It is
+ created and passed by auth_token middleware and then can be used as the
+ authentication plugin when communicating via a session.
+ """
+
+ def __init__(self, user_token, auth_ref):
+ # FIXME(jamielennox): set reauthenticate=False here when keystoneclient
+ # 0.11 is released to prevent trying to refetch authentication.
+ super(_UserAuthPlugin, self).__init__()
+ self._user_token = user_token
+ self._stored_auth_ref = auth_ref
+
+ def get_token(self, session, **kwargs):
+ # NOTE(jamielennox): This is needed partially because the AccessInfo
+ # factory is so bad that we don't always get the correct token data.
+ # Override and always return the token that was provided in the req.
+ return self._user_token
+
+ def get_auth_ref(self, session, **kwargs):
+ # NOTE(jamielennox): We can't go out and fetch this auth_ref, we've
+ # got it already so always return it. In the event it tries to
+ # re-authenticate it will get the same old auth_ref which is not
+ # perfect, but the best we can do for now.
+ return self._stored_auth_ref
+
+
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls."""
@@ -559,8 +597,10 @@ class AuthProtocol(object):
self._remove_auth_headers(env)
user_token = self._get_user_token_from_header(env)
token_info = self._validate_user_token(user_token, env)
+ auth_ref = access.AccessInfo.factory(body=token_info)
env['keystone.token_info'] = token_info
- user_headers = self._build_user_headers(token_info)
+ env['keystone.token_auth'] = _UserAuthPlugin(user_token, auth_ref)
+ user_headers = self._build_user_headers(auth_ref, token_info)
self._add_headers(env, user_headers)
return self._app(env, start_response)
@@ -696,7 +736,7 @@ class AuthProtocol(object):
self._LOG.warn('Authorization failed for token')
raise InvalidUserToken('Token authorization failed')
- def _build_user_headers(self, token_info):
+ def _build_user_headers(self, auth_ref, token_info):
"""Convert token object into headers.
Build headers that represent authenticated user - see main
@@ -706,7 +746,6 @@ class AuthProtocol(object):
:raise InvalidUserToken when unable to parse token object
"""
- auth_ref = access.AccessInfo.factory(body=token_info)
roles = ','.join(auth_ref.role_names)
if _token_is_v2(token_info) and not auth_ref.project_id:
diff --git a/keystonemiddleware/tests/test_auth_token_middleware.py b/keystonemiddleware/tests/test_auth_token_middleware.py
index bf64995..e353313 100644
--- a/keystonemiddleware/tests/test_auth_token_middleware.py
+++ b/keystonemiddleware/tests/test_auth_token_middleware.py
@@ -29,6 +29,7 @@ from keystoneclient import access
from keystoneclient.common import cms
from keystoneclient import exceptions
from keystoneclient import fixture
+from keystoneclient import session
import mock
import testresources
import testtools
@@ -1256,6 +1257,25 @@ class CommonAuthTokenMiddlewareTest(object):
# again.
self.assertThat(2, matchers.Equals(cache.set.call_count))
+ def test_auth_plugin(self):
+ httpretty.register_uri(httpretty.GET,
+ self.examples.SERVICE_URL,
+ body=VERSION_LIST_v3,
+ status_code=300)
+
+ req = webob.Request.blank('/')
+ req.headers['X-Auth-Token'] = self.token_dict['uuid_token_default']
+ body = self.middleware(req.environ, self.start_fake_response)
+ self.assertEqual(200, self.response_status)
+ self.assertEqual([FakeApp.SUCCESS], body)
+
+ token_auth = req.environ['keystone.token_auth']
+ endpoint_filter = {'service_type': self.examples.SERVICE_TYPE,
+ 'version': 3}
+
+ url = token_auth.get_endpoint(session.Session(), **endpoint_filter)
+ self.assertEqual('%s/v3' % BASE_URI, url)
+
class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest,
testresources.ResourcedTestCase):