summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuang Yee <guang.yee@hp.com>2013-06-24 23:37:59 -0700
committerGuang Yee <guang.yee@hp.com>2013-07-15 12:53:27 -0700
commitee27d6eef62d201c99694d0f788ea2a96c6669a4 (patch)
treeaed60962e83e6f98d24b7f2f763c2a8bc1c62c57
parentc238ace30981877e5991874c5b193ea7d5107419 (diff)
downloadkeystone-ee27d6eef62d201c99694d0f788ea2a96c6669a4.tar.gz
keystone-ee27d6eef62d201c99694d0f788ea2a96c6669a4.tar.xz
keystone-ee27d6eef62d201c99694d0f788ea2a96c6669a4.zip
Implements Pluggable V2 Token Provider
This patch implemented V2 token provider. Abstract token provider backend to make token provider pluggable. It enables deployers to customize token management to add their own capabilities. Token provider is responsible for issuing, checking, validating, and revoking tokens. Note the distinction between token 'driver' and 'provider'. Token 'driver' simply provides token CRUD. It does not issue or interpret tokens. Token provider is specified by the 'provider' property in the '[token]' section of the Keystone configuration file. Change-Id: Ic418ec433bd9e3f2f70fa31c90e570e32c1ca687
-rw-r--r--keystone/common/controller.py2
-rw-r--r--keystone/contrib/ec2/core.py23
-rw-r--r--keystone/token/controllers.py214
-rw-r--r--keystone/token/provider.py3
-rw-r--r--keystone/token/providers/uuid.py250
-rw-r--r--tests/test_keystoneclient.py24
6 files changed, 287 insertions, 229 deletions
diff --git a/keystone/common/controller.py b/keystone/common/controller.py
index 0ad1efa3..0fb91cf1 100644
--- a/keystone/common/controller.py
+++ b/keystone/common/controller.py
@@ -26,7 +26,7 @@ def _build_policy_check_credentials(self, action, context, kwargs):
raise exception.Unauthorized()
creds = {}
- if 'token_data' in token_ref:
+ if 'token_data' in token_ref and 'token' in token_ref['token_data']:
#V3 Tokens
token_data = token_ref['token_data']['token']
try:
diff --git a/keystone/contrib/ec2/core.py b/keystone/contrib/ec2/core.py
index 5254b53f..fed7ee08 100644
--- a/keystone/contrib/ec2/core.py
+++ b/keystone/contrib/ec2/core.py
@@ -97,7 +97,7 @@ class Ec2Extension(wsgi.ExtensionRouter):
conditions=dict(method=['DELETE']))
-@dependency.requires('catalog_api', 'ec2_api')
+@dependency.requires('catalog_api', 'ec2_api', 'token_provider_api')
class Ec2Controller(controller.V2Controller):
def check_signature(self, creds_ref, credentials):
signer = ec2_utils.Ec2Signer(creds_ref['secret'])
@@ -172,17 +172,16 @@ class Ec2Controller(controller.V2Controller):
tenant_id=tenant_ref['id'],
metadata=metadata_ref)
- token_ref = self.token_api.create_token(
- token_id, dict(id=token_id,
- user=user_ref,
- tenant=tenant_ref,
- metadata=metadata_ref))
-
- # TODO(termie): i don't think the ec2 middleware currently expects a
- # full return, but it contains a note saying that it
- # would be better to expect a full return
- return token.controllers.Auth.format_authenticate(
- token_ref, roles_ref, catalog_ref)
+ auth_token_data = dict(user=user_ref,
+ tenant=tenant_ref,
+ metadata=metadata_ref,
+ id='placeholder')
+ (token_id, token_data) = self.token_provider_api.issue_token(
+ version=token.provider.V2,
+ token_ref=auth_token_data,
+ roles_ref=roles_ref,
+ catalog_ref=catalog_ref)
+ return token_data
def create_credential(self, context, user_id, tenant_id):
"""Create a secret/access pair for use with ec2 style auth.
diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py
index 1ada05ea..4914d305 100644
--- a/keystone/token/controllers.py
+++ b/keystone/token/controllers.py
@@ -1,16 +1,16 @@
import json
-import sys
-import uuid
from keystone.common import cms
from keystone.common import controller
-from keystone.common import environment
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import utils
from keystone import config
from keystone import exception
from keystone.openstack.common import timeutils
from keystone.token import core
+from keystone.token import provider as token_provider
+
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -22,6 +22,7 @@ class ExternalAuthNotApplicable(Exception):
pass
+@dependency.requires('token_provider_api')
class Auth(controller.V2Controller):
def ca_cert(self, context, auth=None):
ca_file = open(CONF.signing.ca_certs, 'r')
@@ -79,7 +80,6 @@ class Auth(controller.V2Controller):
user_ref, tenant_ref, metadata_ref, expiry = auth_info
core.validate_auth_info(self, user_ref, tenant_ref)
- trust_id = metadata_ref.get('trust_id')
user_ref = self._filter_domain_id(user_ref)
if tenant_ref:
tenant_ref = self._filter_domain_id(tenant_ref)
@@ -103,46 +103,11 @@ class Auth(controller.V2Controller):
role_ref = self.identity_api.get_role(role_id)
roles_ref.append(dict(name=role_ref['name']))
- token_data = Auth.format_token(auth_token_data, roles_ref)
-
- service_catalog = Auth.format_catalog(catalog_ref)
- token_data['access']['serviceCatalog'] = service_catalog
-
- if CONF.signing.token_format == 'UUID':
- token_id = uuid.uuid4().hex
- elif CONF.signing.token_format == 'PKI':
- try:
- token_id = cms.cms_sign_token(json.dumps(token_data),
- CONF.signing.certfile,
- CONF.signing.keyfile)
- except environment.subprocess.CalledProcessError:
- raise exception.UnexpectedError(_(
- 'Unable to sign token.'))
- else:
- raise exception.UnexpectedError(_(
- 'Invalid value for token_format: %s.'
- ' Allowed values are PKI or UUID.') %
- CONF.signing.token_format)
- try:
- self.token_api.create_token(
- token_id, dict(key=token_id,
- id=token_id,
- expires=auth_token_data['expires'],
- user=user_ref,
- tenant=tenant_ref,
- metadata=metadata_ref,
- trust_id=trust_id))
- except Exception:
- exc_info = sys.exc_info()
- # an identical token may have been created already.
- # if so, return the token_data as it is also identical
- try:
- self.token_api.get_token(token_id)
- except exception.TokenNotFound:
- raise exc_info[0], exc_info[1], exc_info[2]
-
- token_data['access']['token']['id'] = token_id
-
+ (token_id, token_data) = self.token_provider_api.issue_token(
+ version=token_provider.V2,
+ token_ref=auth_token_data,
+ roles_ref=roles_ref,
+ catalog_ref=catalog_ref)
return token_data
def _authenticate_token(self, context, auth):
@@ -416,45 +381,6 @@ class Auth(controller.V2Controller):
_('Token does not belong to specified tenant.'))
return data
- def _assert_default_domain(self, token_ref):
- """Make sure we are operating on default domain only."""
- if token_ref.get('token_data'):
- # this is a V3 token
- msg = _('Non-default domain is not supported')
- # user in a non-default is prohibited
- if (token_ref['token_data']['token']['user']['domain']['id'] !=
- DEFAULT_DOMAIN_ID):
- raise exception.Unauthorized(msg)
- # domain scoping is prohibited
- if token_ref['token_data']['token'].get('domain'):
- raise exception.Unauthorized(
- _('Domain scoped token is not supported'))
- # project in non-default domain is prohibited
- if token_ref['token_data']['token'].get('project'):
- project = token_ref['token_data']['token']['project']
- project_domain_id = project['domain']['id']
- # scoped to project in non-default domain is prohibited
- if project_domain_id != DEFAULT_DOMAIN_ID:
- raise exception.Unauthorized(msg)
- # if token is scoped to trust, both trustor and trustee must
- # be in the default domain. Furthermore, the delegated project
- # must also be in the default domain
- metadata_ref = token_ref['metadata']
- if CONF.trust.enabled and 'trust_id' in metadata_ref:
- trust_ref = self.trust_api.get_trust(metadata_ref['trust_id'])
- trustee_user_ref = self.identity_api.get_user(
- trust_ref['trustee_user_id'])
- if trustee_user_ref['domain_id'] != DEFAULT_DOMAIN_ID:
- raise exception.Unauthorized(msg)
- trustor_user_ref = self.identity_api.get_user(
- trust_ref['trustor_user_id'])
- if trustor_user_ref['domain_id'] != DEFAULT_DOMAIN_ID:
- raise exception.Unauthorized(msg)
- project_ref = self.identity_api.get_project(
- trust_ref['project_id'])
- if project_ref['domain_id'] != DEFAULT_DOMAIN_ID:
- raise exception.Unauthorized(msg)
-
@controller.protected
def validate_token_head(self, context, token_id):
"""Check that a token is valid.
@@ -465,9 +391,9 @@ class Auth(controller.V2Controller):
"""
belongs_to = context['query_string'].get('belongsTo')
- token_ref = self._get_token_ref(token_id, belongs_to)
- assert token_ref
- self._assert_default_domain(token_ref)
+ self.token_provider_api.check_token(token_id,
+ belongs_to=belongs_to,
+ version=token_provider.V2)
@controller.protected
def validate_token(self, context, token_id):
@@ -479,26 +405,9 @@ class Auth(controller.V2Controller):
"""
belongs_to = context['query_string'].get('belongsTo')
- token_ref = self._get_token_ref(token_id, belongs_to)
- self._assert_default_domain(token_ref)
-
- # TODO(termie): optimize this call at some point and put it into the
- # the return for metadata
- # fill out the roles in the metadata
- metadata_ref = token_ref['metadata']
- roles_ref = []
- for role_id in metadata_ref.get('roles', []):
- roles_ref.append(self.identity_api.get_role(role_id))
-
- # Get a service catalog if possible
- # This is needed for on-behalf-of requests
- catalog_ref = None
- if token_ref.get('tenant'):
- catalog_ref = self.catalog_api.get_catalog(
- user_id=token_ref['user']['id'],
- tenant_id=token_ref['tenant']['id'],
- metadata=metadata_ref)
- return Auth.format_token(token_ref, roles_ref, catalog_ref)
+ return self.token_provider_api.validate_token(
+ token_id, belongs_to=belongs_to,
+ version=token_provider.V2)
def delete_token(self, context, token_id):
"""Delete a token, effectively invalidating it for authz."""
@@ -538,99 +447,6 @@ class Auth(controller.V2Controller):
return Auth.format_endpoint_list(catalog_ref)
@classmethod
- def format_authenticate(cls, token_ref, roles_ref, catalog_ref):
- o = Auth.format_token(token_ref, roles_ref)
- o['access']['serviceCatalog'] = Auth.format_catalog(catalog_ref)
- return o
-
- @classmethod
- def format_token(cls, token_ref, roles_ref, catalog_ref=None):
- user_ref = token_ref['user']
- metadata_ref = token_ref['metadata']
- expires = token_ref['expires']
- if expires is not None:
- if not isinstance(expires, unicode):
- expires = timeutils.isotime(expires)
- o = {'access': {'token': {'id': token_ref['id'],
- 'expires': expires,
- 'issued_at': timeutils.strtime()
- },
- 'user': {'id': user_ref['id'],
- 'name': user_ref['name'],
- 'username': user_ref['name'],
- 'roles': roles_ref,
- 'roles_links': metadata_ref.get('roles_links',
- [])
- }
- }
- }
- if 'tenant' in token_ref and token_ref['tenant']:
- token_ref['tenant']['enabled'] = True
- o['access']['token']['tenant'] = token_ref['tenant']
- if catalog_ref is not None:
- o['access']['serviceCatalog'] = Auth.format_catalog(catalog_ref)
- if metadata_ref:
- if 'is_admin' in metadata_ref:
- o['access']['metadata'] = {'is_admin':
- metadata_ref['is_admin']}
- else:
- o['access']['metadata'] = {'is_admin': 0}
- if 'roles' in metadata_ref:
- o['access']['metadata']['roles'] = metadata_ref['roles']
- if CONF.trust.enabled and 'trust_id' in metadata_ref:
- o['access']['trust'] = {'trustee_user_id':
- metadata_ref['trustee_user_id'],
- 'id': metadata_ref['trust_id']
- }
- return o
-
- @classmethod
- def format_catalog(cls, catalog_ref):
- """Munge catalogs from internal to output format
- Internal catalogs look like:
-
- {$REGION: {
- {$SERVICE: {
- $key1: $value1,
- ...
- }
- }
- }
-
- The legacy api wants them to look like
-
- [{'name': $SERVICE[name],
- 'type': $SERVICE,
- 'endpoints': [{
- 'tenantId': $tenant_id,
- ...
- 'region': $REGION,
- }],
- 'endpoints_links': [],
- }]
-
- """
- if not catalog_ref:
- return []
-
- services = {}
- for region, region_ref in catalog_ref.iteritems():
- for service, service_ref in region_ref.iteritems():
- new_service_ref = services.get(service, {})
- new_service_ref['name'] = service_ref.pop('name')
- new_service_ref['type'] = service
- new_service_ref['endpoints_links'] = []
- service_ref['region'] = region
-
- endpoints_ref = new_service_ref.get('endpoints', [])
- endpoints_ref.append(service_ref)
-
- new_service_ref['endpoints'] = endpoints_ref
- services[service] = new_service_ref
-
- return services.values()
-
- @classmethod
def format_endpoint_list(cls, catalog_ref):
"""Formats a list of endpoints according to Identity API v2.
diff --git a/keystone/token/provider.py b/keystone/token/provider.py
index b5ad72fa..3bb14e01 100644
--- a/keystone/token/provider.py
+++ b/keystone/token/provider.py
@@ -81,6 +81,9 @@ class Provider(object):
domain-scoped token; and 'auth_context' from the authentication
plugins.
+ For V2 tokens, 'token_ref' must be present in kwargs.
+ Optionally, kwargs may contain 'roles_ref' and 'catalog_ref'.
+
:param context: request context
:type context: dictionary
:param version: version of the token to be issued
diff --git a/keystone/token/providers/uuid.py b/keystone/token/providers/uuid.py
index e1bd0b3b..413c7479 100644
--- a/keystone/token/providers/uuid.py
+++ b/keystone/token/providers/uuid.py
@@ -27,7 +27,6 @@ from keystone import config
from keystone import exception
from keystone.openstack.common import timeutils
from keystone import token
-from keystone.token import provider as token_provider
from keystone import trust
@@ -37,6 +36,108 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
@dependency.requires('catalog_api', 'identity_api')
+class V2TokenDataHelper(object):
+ """Creates V2 token data."""
+ @classmethod
+ def format_token(cls, token_ref, roles_ref, catalog_ref=None):
+ user_ref = token_ref['user']
+ metadata_ref = token_ref['metadata']
+ expires = token_ref.get('expires', token.default_expire_time())
+ if expires is not None:
+ if not isinstance(expires, unicode):
+ expires = timeutils.isotime(expires)
+ o = {'access': {'token': {'id': token_ref['id'],
+ 'expires': expires,
+ 'issued_at': timeutils.strtime()
+ },
+ 'user': {'id': user_ref['id'],
+ 'name': user_ref['name'],
+ 'username': user_ref['name'],
+ 'roles': roles_ref,
+ 'roles_links': metadata_ref.get('roles_links',
+ [])
+ }
+ }
+ }
+ if 'tenant' in token_ref and token_ref['tenant']:
+ token_ref['tenant']['enabled'] = True
+ o['access']['token']['tenant'] = token_ref['tenant']
+ if catalog_ref is not None:
+ o['access']['serviceCatalog'] = V2TokenDataHelper.format_catalog(
+ catalog_ref)
+ if metadata_ref:
+ if 'is_admin' in metadata_ref:
+ o['access']['metadata'] = {'is_admin':
+ metadata_ref['is_admin']}
+ else:
+ o['access']['metadata'] = {'is_admin': 0}
+ if 'roles' in metadata_ref:
+ o['access']['metadata']['roles'] = metadata_ref['roles']
+ if CONF.trust.enabled and 'trust_id' in metadata_ref:
+ o['access']['trust'] = {'trustee_user_id':
+ metadata_ref['trustee_user_id'],
+ 'id': metadata_ref['trust_id']
+ }
+ return o
+
+ @classmethod
+ def format_catalog(cls, catalog_ref):
+ """Munge catalogs from internal to output format
+ Internal catalogs look like:
+
+ {$REGION: {
+ {$SERVICE: {
+ $key1: $value1,
+ ...
+ }
+ }
+ }
+
+ The legacy api wants them to look like
+
+ [{'name': $SERVICE[name],
+ 'type': $SERVICE,
+ 'endpoints': [{
+ 'tenantId': $tenant_id,
+ ...
+ 'region': $REGION,
+ }],
+ 'endpoints_links': [],
+ }]
+
+ """
+ if not catalog_ref:
+ return []
+
+ services = {}
+ for region, region_ref in catalog_ref.iteritems():
+ for service, service_ref in region_ref.iteritems():
+ new_service_ref = services.get(service, {})
+ new_service_ref['name'] = service_ref.pop('name')
+ new_service_ref['type'] = service
+ new_service_ref['endpoints_links'] = []
+ service_ref['region'] = region
+
+ endpoints_ref = new_service_ref.get('endpoints', [])
+ endpoints_ref.append(service_ref)
+
+ new_service_ref['endpoints'] = endpoints_ref
+ services[service] = new_service_ref
+
+ return services.values()
+
+ @classmethod
+ def get_token_data(cls, **kwargs):
+ if 'token_ref' not in kwargs:
+ raise ValueError('Require token_ref to create V2 token data')
+ token_ref = kwargs.get('token_ref')
+ roles_ref = kwargs.get('roles_ref', [])
+ catalog_ref = kwargs.get('catalog_ref')
+ return V2TokenDataHelper.format_token(
+ token_ref, roles_ref, catalog_ref)
+
+
+@dependency.requires('catalog_api', 'identity_api')
class V3TokenDataHelper(object):
"""Token data helper."""
def __init__(self):
@@ -207,25 +308,56 @@ class V3TokenDataHelper(object):
return {'token': token_data}
-@dependency.requires('token_api', 'identity_api')
-class Provider(token_provider.Provider):
+@dependency.requires('token_api', 'identity_api', 'catalog_api')
+class Provider(token.provider.Provider):
def __init__(self, *args, **kwargs):
super(Provider, self).__init__(*args, **kwargs)
if CONF.trust.enabled:
self.trust_api = trust.Manager()
self.v3_token_data_helper = V3TokenDataHelper()
+ self.v2_token_data_helper = V2TokenDataHelper()
def get_token_version(self, token_data):
if token_data and isinstance(token_data, dict):
if 'access' in token_data:
- return token_provider.V2
+ return token.provider.V2
if 'token' in token_data and 'methods' in token_data['token']:
- return token_provider.V3
- raise token_provider.UnsupportedTokenVersionException()
+ return token.provider.V3
+ raise token.provider.UnsupportedTokenVersionException()
def _get_token_id(self, token_data):
return uuid.uuid4().hex
+ def _issue_v2_token(self, **kwargs):
+ token_data = self.v2_token_data_helper.get_token_data(**kwargs)
+ token_id = self._get_token_id(token_data)
+ token_data['access']['token']['id'] = token_id
+ try:
+ expiry = token_data['access']['token']['expires']
+ token_ref = kwargs.get('token_ref')
+ if isinstance(expiry, basestring):
+ expiry = timeutils.normalize_time(
+ timeutils.parse_isotime(expiry))
+ data = dict(key=token_id,
+ id=token_id,
+ expires=expiry,
+ user=token_ref['user'],
+ tenant=token_ref['tenant'],
+ metadata=token_ref['metadata'],
+ token_data=token_data,
+ trust_id=token_ref['metadata'].get('trust_id'))
+ self.token_api.create_token(token_id, data)
+ except Exception:
+ exc_info = sys.exc_info()
+ # an identical token may have been created already.
+ # if so, return the token_data as it is also identical
+ try:
+ self.token_api.get_token(token_id)
+ except exception.TokenNotFound:
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ return (token_id, token_data)
+
def _issue_v3_token(self, **kwargs):
user_id = kwargs.get('user_id')
method_names = kwargs.get('method_names')
@@ -287,21 +419,104 @@ class Provider(token_provider.Provider):
return (token_id, token_data)
def issue_token(self, version='v3.0', **kwargs):
- if version == token_provider.V3:
+ if version == token.provider.V3:
return self._issue_v3_token(**kwargs)
- raise token_provider.UnsupportedTokenVersionException
+ elif version == token.provider.V2:
+ return self._issue_v2_token(**kwargs)
+ raise token.provider.UnsupportedTokenVersionException
def _verify_token(self, token_id, belongs_to=None):
"""Verify the given token and return the token_ref."""
token_ref = self.token_api.get_token(token_id=token_id)
assert token_ref
if belongs_to:
- assert token_ref['tenant']['id'] == belongs_to
+ assert (token_ref['tenant'] and
+ token_ref['tenant']['id'] == belongs_to)
return token_ref
def revoke_token(self, token_id):
self.token_api.delete_token(token_id=token_id)
+ def _assert_default_domain(self, token_ref):
+ """Make sure we are operating on default domain only."""
+ if (token_ref.get('token_data') and
+ self.get_token_version(token_ref.get('token_data')) ==
+ token.provider.V3):
+ # this is a V3 token
+ msg = _('Non-default domain is not supported')
+ # user in a non-default is prohibited
+ if (token_ref['token_data']['token']['user']['domain']['id'] !=
+ DEFAULT_DOMAIN_ID):
+ raise exception.Unauthorized(msg)
+ # domain scoping is prohibited
+ if token_ref['token_data']['token'].get('domain'):
+ raise exception.Unauthorized(
+ _('Domain scoped token is not supported'))
+ # project in non-default domain is prohibited
+ if token_ref['token_data']['token'].get('project'):
+ project = token_ref['token_data']['token']['project']
+ project_domain_id = project['domain']['id']
+ # scoped to project in non-default domain is prohibited
+ if project_domain_id != DEFAULT_DOMAIN_ID:
+ raise exception.Unauthorized(msg)
+ # if token is scoped to trust, both trustor and trustee must
+ # be in the default domain. Furthermore, the delegated project
+ # must also be in the default domain
+ metadata_ref = token_ref['metadata']
+ if CONF.trust.enabled and 'trust_id' in metadata_ref:
+ trust_ref = self.trust_api.get_trust(metadata_ref['trust_id'])
+ trustee_user_ref = self.identity_api.get_user(
+ trust_ref['trustee_user_id'])
+ if trustee_user_ref['domain_id'] != DEFAULT_DOMAIN_ID:
+ raise exception.Unauthorized(msg)
+ trustor_user_ref = self.identity_api.get_user(
+ trust_ref['trustor_user_id'])
+ if trustor_user_ref['domain_id'] != DEFAULT_DOMAIN_ID:
+ raise exception.Unauthorized(msg)
+ project_ref = self.identity_api.get_project(
+ trust_ref['project_id'])
+ if project_ref['domain_id'] != DEFAULT_DOMAIN_ID:
+ raise exception.Unauthorized(msg)
+
+ def _validate_v2_token(self, token_id, belongs_to=None, **kwargs):
+ try:
+ token_ref = self._verify_token(token_id, belongs_to=belongs_to)
+ self._assert_default_domain(token_ref)
+ # FIXME(gyee): performance or correctness? Should we return the
+ # cached token or reconstruct it? Obviously if we are going with
+ # the cached token, any role, project, or domain name changes
+ # will not be reflected. One may argue that with PKI tokens,
+ # we are essentially doing cached token validation anyway.
+ # Lets go with the cached token strategy. Since token
+ # management layer is now pluggable, one can always provide
+ # their own implementation to suit their needs.
+ token_data = token_ref.get('token_data')
+ if (not token_data or
+ self.get_token_version(token_data) !=
+ token.provider.V2):
+ # token is created by old v2 logic
+ metadata_ref = token_ref['metadata']
+ role_refs = []
+ for role_id in metadata_ref.get('roles', []):
+ role_refs.append(self.identity_api.get_role(role_id))
+
+ # Get a service catalog if possible
+ # This is needed for on-behalf-of requests
+ catalog_ref = None
+ if token_ref.get('tenant'):
+ catalog_ref = self.catalog_api.get_catalog(
+ token_ref['user']['id'],
+ token_ref['tenant']['id'],
+ metadata=metadata_ref)
+ token_data = self.v2_token_data_helper.get_token_data(
+ token_ref=token_ref,
+ roles_ref=role_refs,
+ catalog_ref=catalog_ref)
+ return token_data
+ except AssertionError as e:
+ LOG.exception(_('Failed to validate token'))
+ raise exception.Unauthorized(e)
+
def _validate_v3_token(self, token_id):
token_ref = self._verify_token(token_id)
# FIXME(gyee): performance or correctness? Should we return the
@@ -327,12 +542,14 @@ class Provider(token_provider.Provider):
expires=token_ref['expires'])
return token_data
- def validate_token(self, token_id, belongs_to=None,
- version='v3.0'):
+ def validate_token(self, token_id, belongs_to=None, version='v3.0'):
try:
- if version == token_provider.V3:
+ if version == token.provider.V3:
return self._validate_v3_token(token_id)
- raise token_provider.UnsupportedTokenVersionException()
+ elif version == token.provider.V2:
+ return self._validate_v2_token(token_id,
+ belongs_to=belongs_to)
+ raise token.provider.UnsupportedTokenVersionException()
except exception.TokenNotFound as e:
LOG.exception(_('Failed to verify token'))
raise exception.Unauthorized(e)
@@ -340,10 +557,9 @@ class Provider(token_provider.Provider):
def check_token(self, token_id, belongs_to=None,
version='v3.0', **kwargs):
try:
- if version == token_provider.V3:
- self._verify_token(token_id)
- else:
- raise token_provider.UnsupportedTokenVersionException()
+ token_ref = self._verify_token(token_id, belongs_to=belongs_to)
+ if version == token.provider.V2:
+ self._assert_default_domain(token_ref)
except exception.TokenNotFound as e:
LOG.exception(_('Failed to verify token'))
raise exception.Unauthorized(e)
diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py
index d78e885a..5c0d2f5b 100644
--- a/tests/test_keystoneclient.py
+++ b/tests/test_keystoneclient.py
@@ -20,6 +20,7 @@ import webob
import nose.exc
from keystone import test
+from keystone import token
from keystone import config
from keystone.openstack.common import jsonutils
@@ -42,6 +43,7 @@ class CompatTestCase(test.TestCase):
self.clear_module('keystoneclient')
self.load_backends()
+ self.token_provider_api = token.provider.Manager()
self.load_fixtures(default_fixtures)
self.public_server = self.serveapp('keystone', name='main')
@@ -839,6 +841,28 @@ class KcMasterTestCase(CompatTestCase, KeystoneClientTests):
def get_checkout(self):
return KEYSTONECLIENT_REPO, 'master'
+ def test_ec2_auth(self):
+ client = self.get_client()
+ cred = client.ec2.create(user_id=self.user_foo['id'],
+ tenant_id=self.tenant_bar['id'])
+
+ from keystoneclient.contrib.ec2 import utils as ec2_utils
+ signer = ec2_utils.Ec2Signer(cred.secret)
+ credentials = {'params': {'SignatureVersion': '2'},
+ 'access': cred.access,
+ 'verb': 'GET',
+ 'host': 'localhost',
+ 'path': '/thisisgoingtowork'}
+ signature = signer.generate(credentials)
+ credentials['signature'] = signature
+ url = '%s/ec2tokens' % (client.auth_url)
+ (resp, token) = client.request(url=url,
+ method='POST',
+ body={'credentials': credentials})
+ # make sure we have a v2 token
+ self.assertEqual(resp.status_code, 200)
+ self.assertIn('access', token)
+
def test_tenant_add_and_remove_user(self):
client = self.get_client(admin=True)
client.roles.add_user_role(tenant=self.tenant_bar['id'],