summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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'],