summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/keystone.conf.sample6
-rw-r--r--keystone/assignment/backends/kvs.py2
-rw-r--r--keystone/assignment/backends/ldap.py2
-rw-r--r--keystone/assignment/backends/sql.py5
-rw-r--r--keystone/assignment/core.py15
-rw-r--r--keystone/common/config.py2
-rw-r--r--keystone/common/dependency.py43
-rw-r--r--keystone/common/sql/core.py11
-rw-r--r--keystone/common/wsgi.py4
-rw-r--r--keystone/exception.py128
-rw-r--r--keystone/identity/backends/kvs.py2
-rw-r--r--keystone/identity/backends/ldap.py12
-rw-r--r--keystone/identity/backends/pam.py2
-rw-r--r--keystone/identity/backends/sql.py4
-rw-r--r--keystone/identity/core.py10
-rw-r--r--keystone/service.py13
-rw-r--r--keystone/test.py24
-rw-r--r--keystone/token/provider.py4
-rw-r--r--tests/test_exception.py32
-rw-r--r--tests/test_injection.py44
-rw-r--r--tests/test_keystoneclient.py15
-rw-r--r--tests/test_pki_token_provider.conf3
-rw-r--r--tests/test_sql_core.py8
-rw-r--r--tests/test_token_provider.py9
-rw-r--r--tests/test_uuid_token_provider.conf3
-rw-r--r--tests/test_wsgi.py9
26 files changed, 276 insertions, 136 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample
index 4c0327cf..a49a9a5e 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -128,7 +128,8 @@
# driver = keystone.token.backends.sql.Token
# Controls the token construction, validation, and revocation operations.
-# provider = keystone.token.providers.pki.Provider
+# Core providers are keystone.token.providers.[pki|uuid].Provider
+# provider =
# Amount of time a token should remain valid (in seconds)
# expiration = 86400
@@ -165,7 +166,8 @@
[signing]
# Deprecated in favor of provider in the [token] section
-#token_format = PKI
+# Allowed values are PKI or UUID
+#token_format =
#certfile = /etc/keystone/pki/certs/signing_cert.pem
#keyfile = /etc/keystone/pki/private/signing_key.pem
diff --git a/keystone/assignment/backends/kvs.py b/keystone/assignment/backends/kvs.py
index 4dfd908f..30d7b2eb 100644
--- a/keystone/assignment/backends/kvs.py
+++ b/keystone/assignment/backends/kvs.py
@@ -16,11 +16,13 @@
from keystone import assignment
from keystone import clean
+from keystone.common import dependency
from keystone.common import kvs
from keystone import exception
from keystone import identity
+@dependency.requires('identity_api')
class Assignment(kvs.Base, assignment.Driver):
def __init__(self):
super(Assignment, self).__init__()
diff --git a/keystone/assignment/backends/ldap.py b/keystone/assignment/backends/ldap.py
index f8c81eae..9b273e40 100644
--- a/keystone/assignment/backends/ldap.py
+++ b/keystone/assignment/backends/ldap.py
@@ -21,6 +21,7 @@ import ldap as ldap
from keystone import assignment
from keystone import clean
+from keystone.common import dependency
from keystone.common import ldap as common_ldap
from keystone.common import logging
from keystone.common import models
@@ -39,6 +40,7 @@ DEFAULT_DOMAIN = {
}
+@dependency.requires('identity_api')
class Assignment(assignment.Driver):
def __init__(self):
super(Assignment, self).__init__()
diff --git a/keystone/assignment/backends/sql.py b/keystone/assignment/backends/sql.py
index 5ec435ff..024a291a 100644
--- a/keystone/assignment/backends/sql.py
+++ b/keystone/assignment/backends/sql.py
@@ -16,15 +16,14 @@
from keystone import assignment
from keystone import clean
+from keystone.common import dependency
from keystone.common import sql
from keystone.common.sql import migration
from keystone import exception
+@dependency.requires('identity_api')
class Assignment(sql.Base, assignment.Driver):
- def __init__(self):
- super(Assignment, self).__init__()
- self.identity_api = None
# Internal interface to manage the database
def db_sync(self, version=None):
diff --git a/keystone/assignment/core.py b/keystone/assignment/core.py
index b71e2a18..64edb3fa 100644
--- a/keystone/assignment/core.py
+++ b/keystone/assignment/core.py
@@ -35,6 +35,7 @@ DEFAULT_DOMAIN = {'description':
@dependency.provider('assignment_api')
+@dependency.requires('identity_api')
class Manager(manager.Manager):
"""Default pivot point for the Assignment backend.
@@ -45,18 +46,14 @@ class Manager(manager.Manager):
api object by both managers.
"""
- def __init__(self, identity_api=None):
- if identity_api is None:
- from keystone import identity
- identity_api = identity.Manager(self)
-
+ def __init__(self):
assignment_driver = CONF.assignment.driver
+
if assignment_driver is None:
- assignment_driver = identity_api.default_assignment_driver()
+ identity_driver = dependency.REGISTRY['identity_api'].driver
+ assignment_driver = identity_driver.default_assignment_driver()
+
super(Manager, self).__init__(assignment_driver)
- self.driver.identity_api = identity_api
- self.identity_api = identity_api
- self.identity_api.assignment_api = self
def get_roles_for_user_and_project(self, user_id, tenant_id):
"""Get the roles associated with a user within given project.
diff --git a/keystone/common/config.py b/keystone/common/config.py
index b0a534f8..10c47a35 100644
--- a/keystone/common/config.py
+++ b/keystone/common/config.py
@@ -240,7 +240,7 @@ def configure():
# signing
register_str(
- 'token_format', group='signing', default="PKI")
+ 'token_format', group='signing', default=None)
register_str(
'certfile',
group='signing',
diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py
index dc3e4ac4..a640031d 100644
--- a/keystone/common/dependency.py
+++ b/keystone/common/dependency.py
@@ -16,6 +16,8 @@
REGISTRY = {}
+_future_dependencies = {}
+
class UnresolvableDependencyException(Exception):
def __init__(self, name):
@@ -32,6 +34,8 @@ def provider(name):
init(self, *args, **kwargs)
REGISTRY[name] = self
+ resolve_future_dependencies(name)
+
return __wrapped_init__
cls.__init__ = wrapped(cls.__init__)
@@ -48,7 +52,13 @@ def requires(*dependencies):
for dependency in self._dependencies:
if dependency not in REGISTRY:
- raise UnresolvableDependencyException(dependency)
+ if dependency in _future_dependencies:
+ _future_dependencies[dependency] += [self]
+ else:
+ _future_dependencies[dependency] = [self]
+
+ continue
+
setattr(self, dependency, REGISTRY[dependency])
def wrapped(cls):
@@ -65,3 +75,34 @@ def requires(*dependencies):
return cls
return wrapped
+
+
+def resolve_future_dependencies(provider_name=None):
+ if provider_name:
+ targets = _future_dependencies.pop(provider_name, [])
+
+ for target in targets:
+ setattr(target, provider_name, REGISTRY[provider_name])
+
+ return
+
+ try:
+ for dependency, targets in _future_dependencies.iteritems():
+ if dependency not in REGISTRY:
+ raise UnresolvableDependencyException(dependency)
+
+ for target in targets:
+ setattr(target, dependency, REGISTRY[dependency])
+ finally:
+ _future_dependencies.clear()
+
+
+def reset():
+ """Reset the registry of providers.
+
+ This is useful for unit testing to ensure that tests don't use providers
+ from previous tests.
+ """
+
+ REGISTRY.clear()
+ _future_dependencies.clear()
diff --git a/keystone/common/sql/core.py b/keystone/common/sql/core.py
index 7978fcc5..2d3114f2 100644
--- a/keystone/common/sql/core.py
+++ b/keystone/common/sql/core.py
@@ -243,9 +243,10 @@ class Base(object):
def get_session(self, autocommit=True, expire_on_commit=False):
"""Return a SQLAlchemy session."""
- self._engine = self._engine or self.get_engine()
- self._sessionmaker = self._sessionmaker or self.get_sessionmaker(
- self._engine)
+ if not self._engine:
+ self._engine = self.get_engine()
+ self._sessionmaker = self.get_sessionmaker(self._engine)
+ register_global_engine_callback(self.clear_engine)
return self._sessionmaker(autocommit=autocommit,
expire_on_commit=expire_on_commit)
@@ -300,6 +301,10 @@ class Base(object):
autocommit=autocommit,
expire_on_commit=expire_on_commit)
+ def clear_engine(self):
+ self._engine = None
+ self._sessionmaker = None
+
def handle_conflicts(type='object'):
"""Converts IntegrityError into HTTP 409 Conflict."""
diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py
index c7e30576..87636dbe 100644
--- a/keystone/common/wsgi.py
+++ b/keystone/common/wsgi.py
@@ -203,7 +203,7 @@ class BaseApplication(object):
class Application(BaseApplication):
- @webob.dec.wsgify
+ @webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
arg_dict = req.environ['wsgiorg.routing_args'][1]
action = arg_dict.pop('action')
@@ -583,7 +583,7 @@ def render_exception(error):
body = {'error': {
'code': error.code,
'title': error.title,
- 'message': str(error)
+ 'message': unicode(error)
}}
if isinstance(error, exception.AuthPluginException):
body['error']['identity'] = error.authentication
diff --git a/keystone/exception.py b/keystone/exception.py
index db5f5005..5e1defba 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -13,10 +13,10 @@
# 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 re
from keystone.common import config
from keystone.common import logging
+from keystone.openstack.common.gettextutils import _ # noqa
CONF = config.CONF
@@ -29,15 +29,15 @@ _FATAL_EXCEPTION_FORMAT_ERRORS = False
class Error(StandardError):
"""Base error class.
- Child classes should define an HTTP status code, title, and a doc string.
+ Child classes should define an HTTP status code, title, and a
+ message_format.
"""
code = None
title = None
+ message_format = None
def __init__(self, message=None, **kwargs):
- """Use the doc string as the error message by default."""
-
try:
message = self._build_message(message, **kwargs)
except KeyError:
@@ -45,8 +45,8 @@ class Error(StandardError):
if _FATAL_EXCEPTION_FORMAT_ERRORS:
raise
else:
- LOG.warning('missing exception kwargs (programmer error)')
- message = self.__doc__
+ LOG.warning(_('missing exception kwargs (programmer error)'))
+ message = self.message_format
super(Error, self).__init__(message)
@@ -57,42 +57,31 @@ class Error(StandardError):
"""
if not message:
- message = re.sub('[ \n]+', ' ', self.__doc__ % kwargs)
- message = message.strip()
+ message = self.message_format % kwargs
return message
class ValidationError(Error):
- """Expecting to find %(attribute)s in %(target)s.
-
- The server could not comply with the request since it is either malformed
- or otherwise incorrect.
-
- The client is assumed to be in error.
-
- """
+ message_format = _("Expecting to find %(attribute)s in %(target)s."
+ " The server could not comply with the request"
+ " since it is either malformed or otherwise"
+ " incorrect. The client is assumed to be in error.")
code = 400
title = 'Bad Request'
class StringLengthExceeded(ValidationError):
- """String length exceeded.
-
- The length of string "%(string)s" exceeded the limit of column
- %(type)s(CHAR(%(length)d)).
-
- """
+ message_format = _("String length exceeded.The length of"
+ " string '%(string)s' exceeded the limit"
+ " of column %(type)s(CHAR(%(length)d)).")
class ValidationSizeError(Error):
- """Request attribute %(attribute)s must be less than or equal to %(size)i.
-
- The server could not comply with the request because the attribute
- size is invalid (too large).
-
- The client is assumed to be in error.
-
- """
+ message_format = _("Request attribute %(attribute)s must be"
+ " less than or equal to %(size)i. The server"
+ " could not comply with the request because"
+ " the attribute size is invalid (too large)."
+ " The client is assumed to be in error.")
code = 400
title = 'Bad Request'
@@ -103,19 +92,19 @@ class SecurityError(Error):
def _build_message(self, message, **kwargs):
"""Only returns detailed messages in debug mode."""
if CONF.debug:
- return message or self.__doc__ % kwargs
+ return message or self.message_format % kwargs
else:
- return self.__doc__ % kwargs
+ return self.message_format % kwargs
class Unauthorized(SecurityError):
- """The request you have made requires authentication."""
+ message_format = _("The request you have made requires authentication.")
code = 401
title = 'Unauthorized'
class AuthPluginException(Unauthorized):
- """Authentication plugin error."""
+ message_format = _("Authentication plugin error.")
def __init__(self, *args, **kwargs):
super(AuthPluginException, self).__init__(*args, **kwargs)
@@ -123,7 +112,7 @@ class AuthPluginException(Unauthorized):
class AuthMethodNotSupported(AuthPluginException):
- """Attempted to authenticate with an unsupported method."""
+ message_format = _("Attempted to authenticate with an unsupported method.")
def __init__(self, *args, **kwargs):
super(AuthMethodNotSupported, self).__init__(*args, **kwargs)
@@ -131,7 +120,7 @@ class AuthMethodNotSupported(AuthPluginException):
class AdditionalAuthRequired(AuthPluginException):
- """Additional authentications steps required."""
+ message_format = _("Additional authentications steps required.")
def __init__(self, auth_response=None, **kwargs):
super(AdditionalAuthRequired, self).__init__(message=None, **kwargs)
@@ -139,112 +128,111 @@ class AdditionalAuthRequired(AuthPluginException):
class Forbidden(SecurityError):
- """You are not authorized to perform the requested action."""
+ message_format = _("You are not authorized to perform the"
+ " requested action.")
code = 403
title = 'Forbidden'
class ForbiddenAction(Forbidden):
- """You are not authorized to perform the requested action, %(action)s."""
+ message_format = _("You are not authorized to perform the"
+ " requested action, %(action)s.")
class NotFound(Error):
- """Could not find, %(target)s."""
+ message_format = _("Could not find, %(target)s.")
code = 404
title = 'Not Found'
class EndpointNotFound(NotFound):
- """Could not find endpoint, %(endpoint_id)s."""
+ message_format = _("Could not find endpoint, %(endpoint_id)s.")
class MetadataNotFound(NotFound):
- """An unhandled exception has occurred: Could not find metadata."""
- # (dolph): metadata is not a user-facing concept,
- # so this exception should not be exposed
+ """(dolph): metadata is not a user-facing concept,
+ so this exception should not be exposed
+ """
+ message_format = _("An unhandled exception has occurred:"
+ " Could not find metadata.")
class PolicyNotFound(NotFound):
- """Could not find policy, %(policy_id)s."""
+ message_format = _("Could not find policy, %(policy_id)s.")
class RoleNotFound(NotFound):
- """Could not find role, %(role_id)s."""
+ message_format = _("Could not find role, %(role_id)s.")
class ServiceNotFound(NotFound):
- """Could not find service, %(service_id)s."""
+ message_format = _("Could not find service, %(service_id)s.")
class DomainNotFound(NotFound):
- """Could not find domain, %(domain_id)s."""
+ message_format = _("Could not find domain, %(domain_id)s.")
class ProjectNotFound(NotFound):
- """Could not find project, %(project_id)s."""
+ message_format = _("Could not find project, %(project_id)s.")
class TokenNotFound(NotFound):
- """Could not find token, %(token_id)s."""
+ message_format = _("Could not find token, %(token_id)s.")
class UserNotFound(NotFound):
- """Could not find user, %(user_id)s."""
+ message_format = _("Could not find user, %(user_id)s.")
class GroupNotFound(NotFound):
- """Could not find group, %(group_id)s."""
+ message_format = _("Could not find group, %(group_id)s.")
class TrustNotFound(NotFound):
- """Could not find trust, %(trust_id)s."""
+ message_format = _("Could not find trust, %(trust_id)s.")
class CredentialNotFound(NotFound):
- """Could not find credential, %(credential_id)s."""
+ message_format = _("Could not find credential, %(credential_id)s.")
class VersionNotFound(NotFound):
- """Could not find version, %(version)s."""
+ message_format = _("Could not find version, %(version)s.")
class Conflict(Error):
- """Conflict occurred attempting to store %(type)s.
-
- %(details)s
-
- """
+ message_format = _("Conflict occurred attempting to store %(type)s."
+ " %(details)s")
code = 409
title = 'Conflict'
class RequestTooLarge(Error):
- """Request is too large."""
+ message_format = _("Request is too large.")
code = 413
title = 'Request is too large.'
class UnexpectedError(Error):
- """An unexpected error prevented the server from fulfilling your request.
-
- %(exception)s
-
- """
+ message_format = _("An unexpected error prevented the server"
+ " from fulfilling your request. %(exception)s")
code = 500
title = 'Internal Server Error'
class MalformedEndpoint(UnexpectedError):
- """Malformed endpoint URL (%(endpoint)s), see ERROR log for details."""
+ message_format = _("Malformed endpoint URL (%(endpoint)s),"
+ " see ERROR log for details.")
class NotImplemented(Error):
- """The action you have requested has not been implemented."""
+ message_format = _("The action you have requested has not"
+ " been implemented.")
code = 501
title = 'Not Implemented'
class PasteConfigNotFound(UnexpectedError):
- """The Keystone paste configuration file %(config_file)s could not be
- found.
- """
+ message_format = _("The Keystone paste configuration file"
+ " %(config_file)s could not be found.")
diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py
index 83535108..0323d3d0 100644
--- a/keystone/identity/backends/kvs.py
+++ b/keystone/identity/backends/kvs.py
@@ -28,7 +28,7 @@ class Identity(kvs.Base, identity.Driver):
return "keystone.assignment.backends.kvs.Assignment"
# Public interface
- def authenticate(self, user_id=None, password=None):
+ def authenticate(self, user_id, password):
user_ref = None
try:
user_ref = self._get_user(user_id)
diff --git a/keystone/identity/backends/ldap.py b/keystone/identity/backends/ldap.py
index 7f5cedc3..91ea1e41 100644
--- a/keystone/identity/backends/ldap.py
+++ b/keystone/identity/backends/ldap.py
@@ -19,6 +19,7 @@ import uuid
import ldap
from keystone import clean
+from keystone.common import dependency
from keystone.common import ldap as common_ldap
from keystone.common import logging
from keystone.common import models
@@ -38,6 +39,7 @@ DEFAULT_DOMAIN = {
}
+@dependency.requires('assignment_api')
class Identity(identity.Driver):
def __init__(self):
super(Identity, self).__init__()
@@ -52,7 +54,7 @@ class Identity(identity.Driver):
def create_project(self, project_id, project):
return self.assignment_api.create_project(project_id, project)
- def authenticate(self, user_id=None, password=None):
+ def authenticate(self, user_id, password):
try:
user_ref = self._get_user(user_id)
except exception.UserNotFound:
@@ -77,7 +79,8 @@ class Identity(identity.Driver):
return self.assignment_api._set_default_domain(ref)
def list_users(self):
- return self.assignment_api._set_default_domain(self.user.get_all())
+ return (self.assignment_api._set_default_domain
+ (self.user.get_all_filtered()))
def get_user_by_name(self, user_name, domain_id):
self.assignment_api._validate_default_domain_id(domain_id)
@@ -181,7 +184,7 @@ class Identity(identity.Driver):
for user_dn in self.group.list_group_users(group_id):
user_id = self.user._dn_to_id(user_dn)
try:
- users.append(self.user.get(user_id))
+ users.append(self.user.get_filtered(user_id))
except exception.UserNotFound:
LOG.debug(_("Group member '%(user_dn)s' not found in"
" '%(group_id)s'. The user should be removed"
@@ -264,6 +267,9 @@ class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
user = self.get(user_id)
return identity.filter_user(user)
+ def get_all_filtered(self):
+ return [identity.filter_user(user) for user in self.get_all()]
+
class GroupApi(common_ldap.BaseLdap):
DEFAULT_OU = 'ou=UserGroups'
diff --git a/keystone/identity/backends/pam.py b/keystone/identity/backends/pam.py
index 5cfa5b16..2a6ee621 100644
--- a/keystone/identity/backends/pam.py
+++ b/keystone/identity/backends/pam.py
@@ -58,7 +58,7 @@ class PamIdentity(identity.Driver):
Tenant is always the same as User, root user has admin role.
"""
- def authenticate(self, user_id=None, password=None):
+ def authenticate(self, user_id, password):
auth = pam.authenticate if pam else PAM_authenticate
if not auth(user_id, password):
raise AssertionError('Invalid user / password')
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py
index 2c00088e..bff41106 100644
--- a/keystone/identity/backends/sql.py
+++ b/keystone/identity/backends/sql.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from keystone.common import dependency
from keystone.common import sql
from keystone.common.sql import migration
from keystone.common import utils
@@ -61,6 +62,7 @@ class UserGroupMembership(sql.ModelBase, sql.DictBase):
primary_key=True)
+@dependency.requires('assignment_api')
class Identity(sql.Base, identity.Driver):
def default_assignment_driver(self):
return "keystone.assignment.backends.sql.Assignment"
@@ -84,7 +86,7 @@ class Identity(sql.Base, identity.Driver):
return utils.check_password(password, user_ref.password)
# Identity interface
- def authenticate(self, user_id=None, password=None):
+ def authenticate(self, user_id, password):
session = self.get_session()
user_ref = None
try:
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index b3efc0a7..b2b3eaf0 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -16,7 +16,6 @@
"""Main entry point into the Identity service."""
-from keystone import assignment
from keystone import clean
from keystone.common import dependency
from keystone.common import logging
@@ -53,6 +52,7 @@ def filter_user(user_ref):
@dependency.provider('identity_api')
+@dependency.requires('assignment_api')
class Manager(manager.Manager):
"""Default pivot point for the Identity backend.
@@ -61,12 +61,8 @@ class Manager(manager.Manager):
"""
- def __init__(self, assignment_api=None):
+ def __init__(self):
super(Manager, self).__init__(CONF.identity.driver)
- if assignment_api is None:
- assignment_api = assignment.Manager(self)
- self.assignment_api = assignment_api
- self.driver.assignment_api = assignment_api
def create_user(self, user_id, user_ref):
user = user_ref.copy()
@@ -216,7 +212,7 @@ class Manager(manager.Manager):
class Driver(object):
"""Interface description for an Identity driver."""
- def authenticate_user(self, user_id, password):
+ def authenticate(self, user_id, password):
"""Authenticate a given user and password.
:returns: user_ref
:raises: AssertionError
diff --git a/keystone/service.py b/keystone/service.py
index 6b0c3708..ce64aba8 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -16,8 +16,10 @@
import routes
+from keystone import assignment
from keystone import auth
from keystone import catalog
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import wsgi
from keystone import config
@@ -34,16 +36,25 @@ from keystone import trust
CONF = config.CONF
LOG = logging.getLogger(__name__)
+
+# Ensure that the identity driver is created before the assignment manager.
+# The default assignment driver is determined by the identity driver, so the
+# identity driver must be available to the assignment manager.
+_IDENTITY_API = identity.Manager()
+
DRIVERS = dict(
+ assignment_api=assignment.Manager(),
catalog_api=catalog.Manager(),
credentials_api=credential.Manager(),
ec2_api=ec2.Manager(),
- identity_api=identity.Manager(),
+ identity_api=_IDENTITY_API,
policy_api=policy.Manager(),
token_api=token.Manager(),
trust_api=trust.Manager(),
token_provider_api=token.provider.Manager())
+dependency.resolve_future_dependencies()
+
@logging.fail_gracefully
def public_app_factory(global_conf, **local_conf):
diff --git a/keystone/test.py b/keystone/test.py
index 0c51d76d..5d1ad505 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -38,18 +38,21 @@ environment.use_eventlet()
from keystone import assignment
from keystone import catalog
+from keystone.common import dependency
from keystone.common import kvs
from keystone.common import logging
from keystone.common import sql
from keystone.common import utils
from keystone.common import wsgi
from keystone import config
+from keystone.contrib import ec2
from keystone import credential
from keystone import exception
from keystone import identity
from keystone.openstack.common import timeutils
from keystone import policy
from keystone import token
+from keystone.token import provider as token_provider
from keystone import trust
@@ -239,6 +242,11 @@ class TestCase(NoModule, unittest.TestCase):
for path in self._paths:
if path in sys.path:
sys.path.remove(path)
+
+ # Clear the registry of providers so that providers from previous
+ # tests aren't used.
+ dependency.reset()
+
kvs.INMEMDB.clear()
CONF.reset()
@@ -252,11 +260,23 @@ class TestCase(NoModule, unittest.TestCase):
def load_backends(self):
"""Initializes each manager and assigns them to an attribute."""
- for manager in [assignment, catalog, credential, identity, policy,
- token, trust]:
+
+ # TODO(blk-u): Shouldn't need to clear the registry here, but some
+ # tests call load_backends multiple times. These should be fixed to
+ # only call load_backends once.
+ dependency.reset()
+
+ # NOTE(blk-u): identity must be before assignment to ensure that the
+ # identity driver is available to the assignment manager because the
+ # assignment manager gets the default assignment driver from the
+ # identity driver.
+ for manager in [identity, assignment, catalog, credential, ec2, policy,
+ token, token_provider, trust]:
manager_name = '%s_api' % manager.__name__.split('.')[-1]
setattr(self, manager_name, manager.Manager())
+ dependency.resolve_future_dependencies()
+
def load_fixtures(self, fixtures):
"""Hacky basic and naive fixture loading based on a python module.
diff --git a/keystone/token/provider.py b/keystone/token/provider.py
index 2459f843..2864be6f 100644
--- a/keystone/token/provider.py
+++ b/keystone/token/provider.py
@@ -77,6 +77,10 @@ class Manager(manager.Manager):
'conflicts with keystone.conf [token] provider'))
return CONF.token.provider
else:
+ if not CONF.signing.token_format:
+ # No token provider and no format, so use default (PKI)
+ return PKI_PROVIDER
+
msg = _('keystone.conf [signing] token_format is deprecated in '
'favor of keystone.conf [token] provider')
if CONF.signing.token_format == 'PKI':
diff --git a/tests/test_exception.py b/tests/test_exception.py
index 33250835..d442d572 100644
--- a/tests/test_exception.py
+++ b/tests/test_exception.py
@@ -67,14 +67,14 @@ class ExceptionTestCase(test.TestCase):
attribute = uuid.uuid4().hex
e = exception.ValidationError(target=target, attribute=attribute)
self.assertValidJsonRendering(e)
- self.assertIn(target, str(e))
- self.assertIn(attribute, str(e))
+ self.assertIn(target, unicode(e))
+ self.assertIn(attribute, unicode(e))
def test_not_found(self):
target = uuid.uuid4().hex
e = exception.NotFound(target=target)
self.assertValidJsonRendering(e)
- self.assertIn(target, str(e))
+ self.assertIn(target, unicode(e))
def test_403_title(self):
e = exception.Forbidden()
@@ -101,7 +101,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Unauthorized(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
+ self.assertNotIn(risky_info, unicode(e))
def test_unauthorized_exposure_in_debug(self):
self.opt(debug=True)
@@ -109,7 +109,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Unauthorized(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_exposure(self):
self.opt(debug=False)
@@ -117,7 +117,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Forbidden(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
+ self.assertNotIn(risky_info, unicode(e))
def test_forbidden_exposure_in_debug(self):
self.opt(debug=True)
@@ -125,7 +125,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Forbidden(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_action_exposure(self):
self.opt(debug=False)
@@ -134,12 +134,12 @@ class SecurityErrorTestCase(ExceptionTestCase):
action = uuid.uuid4().hex
e = exception.ForbiddenAction(message=risky_info, action=action)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
- self.assertIn(action, str(e))
+ self.assertNotIn(risky_info, unicode(e))
+ self.assertIn(action, unicode(e))
e = exception.ForbiddenAction(action=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_action_exposure_in_debug(self):
self.opt(debug=True)
@@ -148,8 +148,16 @@ class SecurityErrorTestCase(ExceptionTestCase):
e = exception.ForbiddenAction(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
e = exception.ForbiddenAction(action=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
+
+ def test_unicode_argument_message(self):
+ self.opt(debug=False)
+
+ risky_info = u'\u7ee7\u7eed\u884c\u7f29\u8fdb\u6216'
+ e = exception.Forbidden(message=risky_info)
+ self.assertValidJsonRendering(e)
+ self.assertNotIn(risky_info, unicode(e))
diff --git a/tests/test_injection.py b/tests/test_injection.py
index 4b6fc8ba..36cd0126 100644
--- a/tests/test_injection.py
+++ b/tests/test_injection.py
@@ -21,6 +21,10 @@ from keystone.common import dependency
class TestDependencyInjection(unittest.TestCase):
+ def tearDown(self):
+ dependency.reset()
+ super(TestDependencyInjection, self).tearDown()
+
def test_dependency_injection(self):
class Interface(object):
def do_work(self):
@@ -165,3 +169,43 @@ class TestDependencyInjection(unittest.TestCase):
with self.assertRaises(dependency.UnresolvableDependencyException):
Consumer()
+ dependency.resolve_future_dependencies()
+
+ def test_circular_dependency(self):
+ p1_name = uuid.uuid4().hex
+ p2_name = uuid.uuid4().hex
+
+ @dependency.provider(p1_name)
+ @dependency.requires(p2_name)
+ class P1(object):
+ pass
+
+ @dependency.provider(p2_name)
+ @dependency.requires(p1_name)
+ class P2(object):
+ pass
+
+ p1 = P1()
+ p2 = P2()
+
+ dependency.resolve_future_dependencies()
+
+ self.assertIs(getattr(p1, p2_name), p2)
+ self.assertIs(getattr(p2, p1_name), p1)
+
+ def test_reset(self):
+ # Can reset the registry of providers.
+
+ p_id = uuid.uuid4().hex
+
+ @dependency.provider(p_id)
+ class P(object):
+ pass
+
+ p_inst = P()
+
+ self.assertIs(dependency.REGISTRY[p_id], p_inst)
+
+ dependency.reset()
+
+ self.assertFalse(dependency.REGISTRY)
diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py
index ecab6a01..a26d6595 100644
--- a/tests/test_keystoneclient.py
+++ b/tests/test_keystoneclient.py
@@ -36,12 +36,8 @@ class CompatTestCase(test.TestCase):
def setUp(self):
super(CompatTestCase, self).setUp()
- self.public_server = self.serveapp('keystone', name='main')
- self.admin_server = self.serveapp('keystone', name='admin')
-
- revdir = test.checkout_vendor(*self.get_checkout())
- self.add_path(revdir)
- self.clear_module('keystoneclient')
+ # The backends should be loaded and initialized before the servers are
+ # started because the servers use the backends.
self.load_backends()
self.token_provider_api = token.provider.Manager()
@@ -54,6 +50,13 @@ class CompatTestCase(test.TestCase):
self.tenant_bar['id'],
self.role_admin['id'])
+ self.public_server = self.serveapp('keystone', name='main')
+ self.admin_server = self.serveapp('keystone', name='admin')
+
+ revdir = test.checkout_vendor(*self.get_checkout())
+ self.add_path(revdir)
+ self.clear_module('keystoneclient')
+
def tearDown(self):
self.public_server.kill()
self.admin_server.kill()
diff --git a/tests/test_pki_token_provider.conf b/tests/test_pki_token_provider.conf
index ec8df231..255972c3 100644
--- a/tests/test_pki_token_provider.conf
+++ b/tests/test_pki_token_provider.conf
@@ -1,5 +1,2 @@
-[signing]
-token_format = PKI
-
[token]
provider = keystone.token.providers.pki.Provider
diff --git a/tests/test_sql_core.py b/tests/test_sql_core.py
index bb413485..e60005f5 100644
--- a/tests/test_sql_core.py
+++ b/tests/test_sql_core.py
@@ -172,3 +172,11 @@ class TestBase(test.TestCase):
self.assertFalse(session.autocommit)
self.assertTrue(session.expire_on_commit)
+
+ def test_get_session_invalidated(self):
+ # If clear the global engine, a new engine is used for get_session().
+ base = sql.Base()
+ session1 = base.get_session()
+ sql.set_global_engine(None)
+ session2 = base.get_session()
+ self.assertIsNot(session1.bind, session2.bind)
diff --git a/tests/test_token_provider.py b/tests/test_token_provider.py
index 1bcf1a21..ac0b0d6b 100644
--- a/tests/test_token_provider.py
+++ b/tests/test_token_provider.py
@@ -410,11 +410,16 @@ class TestTokenProvider(test.TestCase):
self.assertRaises(exception.UnexpectedError,
token.provider.Manager.get_token_provider)
+ def test_uuid_provider(self):
+ self.opt_in_group('token', provider=token.provider.UUID_PROVIDER)
+ self.assertEqual(token.provider.Manager.get_token_provider(),
+ token.provider.UUID_PROVIDER)
+
def test_provider_override_token_format(self):
self.opt_in_group('token',
provider='keystone.token.providers.pki.Test')
- self.assertRaises(exception.UnexpectedError,
- token.provider.Manager.get_token_provider)
+ self.assertEqual(token.provider.Manager.get_token_provider(),
+ 'keystone.token.providers.pki.Test')
self.opt_in_group('signing', token_format='UUID')
self.opt_in_group('token',
diff --git a/tests/test_uuid_token_provider.conf b/tests/test_uuid_token_provider.conf
index d1ac5fdf..d127ea3b 100644
--- a/tests/test_uuid_token_provider.conf
+++ b/tests/test_uuid_token_provider.conf
@@ -1,5 +1,2 @@
-[signing]
-token_format = UUID
-
[token]
provider = keystone.token.providers.uuid.Provider
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index 8ac594a8..003f7571 100644
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -14,8 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import webob
-
from keystone import test
from keystone.common import wsgi
@@ -34,7 +32,7 @@ class BaseWSGITest(test.TestCase):
super(BaseWSGITest, self).setUp()
def _make_request(self, url='/'):
- req = webob.Request.blank(url)
+ req = wsgi.Request.blank(url)
args = {'action': 'index', 'controller': None}
req.environ['wsgiorg.routing_args'] = [None, args]
return req
@@ -134,6 +132,11 @@ class ApplicationTest(BaseWSGITest):
self.assertIn("testkey", app.kwargs)
self.assertEquals("test", app.kwargs["testkey"])
+ def test_render_exception(self):
+ e = exception.Unauthorized(message=u'\u7f51\u7edc')
+ resp = wsgi.render_exception(e)
+ self.assertEqual(resp.status_int, 401)
+
class ExtensionRouterTest(BaseWSGITest):
def test_extensionrouter_local_config(self):