summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--doc/source/man/keystone-all.rst2
-rw-r--r--doc/source/man/keystone-manage.rst2
-rw-r--r--keystone/assignment/backends/kvs.py2
-rw-r--r--keystone/assignment/backends/ldap.py2
-rw-r--r--keystone/assignment/backends/sql.py27
-rw-r--r--keystone/assignment/core.py15
-rw-r--r--keystone/common/dependency.py33
-rw-r--r--keystone/common/logging.py4
-rw-r--r--keystone/common/wsgi.py2
-rw-r--r--keystone/identity/backends/ldap.py10
-rw-r--r--keystone/identity/backends/sql.py2
-rw-r--r--keystone/identity/controllers.py10
-rw-r--r--keystone/identity/core.py8
-rw-r--r--keystone/service.py13
-rw-r--r--keystone/test.py8
-rw-r--r--keystone/trust/controllers.py18
-rw-r--r--tests/test_auth.py4
-rw-r--r--tests/test_backend_sql.py2
-rw-r--r--tests/test_injection.py27
-rw-r--r--tests/test_v3_identity.py20
-rw-r--r--tests/test_wsgi.py4
22 files changed, 172 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 5cce17d6..d4915b0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*.pyc
*.swp
+*.egg/
vendor
.ksl-venv
.venv
diff --git a/doc/source/man/keystone-all.rst b/doc/source/man/keystone-all.rst
index 9b0859d9..76d5ab1f 100644
--- a/doc/source/man/keystone-all.rst
+++ b/doc/source/man/keystone-all.rst
@@ -6,7 +6,7 @@ keystone-all
Keystone Startup Command
------------------------
-:Author: openstack@lists.launchpad.net
+:Author: openstack@lists.openstack.org
:Date: 2010-11-16
:Copyright: OpenStack LLC
:Version: 2012.1
diff --git a/doc/source/man/keystone-manage.rst b/doc/source/man/keystone-manage.rst
index 84a3ec9f..1da4b40a 100644
--- a/doc/source/man/keystone-manage.rst
+++ b/doc/source/man/keystone-manage.rst
@@ -6,7 +6,7 @@ keystone-manage
Keystone Management Utility
---------------------------
-:Author: openstack@lists.launchpad.net
+:Author: openstack@lists.openstack.org
:Date: 2010-11-16
:Copyright: OpenStack LLC
:Version: 2012.1
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..cd2a0a5c 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):
@@ -657,9 +656,10 @@ class Domain(sql.ModelBase, sql.DictBase):
__tablename__ = 'domain'
attributes = ['id', 'name', 'enabled']
id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), unique=True, nullable=False)
- enabled = sql.Column(sql.Boolean, default=True)
+ name = sql.Column(sql.String(64), nullable=False)
+ enabled = sql.Column(sql.Boolean, default=True, nullable=False)
extra = sql.Column(sql.JsonBlob())
+ __table_args__ = (sql.UniqueConstraint('name'), {})
class Project(sql.ModelBase, sql.DictBase):
@@ -681,8 +681,9 @@ class Role(sql.ModelBase, sql.DictBase):
__tablename__ = 'role'
attributes = ['id', 'name']
id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), unique=True, nullable=False)
+ name = sql.Column(sql.String(255), nullable=False)
extra = sql.Column(sql.JsonBlob())
+ __table_args__ = (sql.UniqueConstraint('name'), {})
class BaseGrant(sql.DictBase):
@@ -720,9 +721,8 @@ class BaseGrant(sql.DictBase):
class UserProjectGrant(sql.ModelBase, BaseGrant):
__tablename__ = 'user_project_metadata'
- user_id = sql.Column(sql.String(64),
- primary_key=True)
- project_id = sql.Column(sql.String(64),
+ user_id = sql.Column(sql.String(64), primary_key=True)
+ project_id = sql.Column(sql.String(64), sql.ForeignKey('project.id'),
primary_key=True)
data = sql.Column(sql.JsonBlob())
@@ -730,19 +730,22 @@ class UserProjectGrant(sql.ModelBase, BaseGrant):
class UserDomainGrant(sql.ModelBase, BaseGrant):
__tablename__ = 'user_domain_metadata'
user_id = sql.Column(sql.String(64), primary_key=True)
- domain_id = sql.Column(sql.String(64), primary_key=True)
+ domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
+ primary_key=True)
data = sql.Column(sql.JsonBlob())
class GroupProjectGrant(sql.ModelBase, BaseGrant):
__tablename__ = 'group_project_metadata'
group_id = sql.Column(sql.String(64), primary_key=True)
- project_id = sql.Column(sql.String(64), primary_key=True)
+ project_id = sql.Column(sql.String(64), sql.ForeignKey('project.id'),
+ primary_key=True)
data = sql.Column(sql.JsonBlob())
class GroupDomainGrant(sql.ModelBase, BaseGrant):
__tablename__ = 'group_domain_metadata'
group_id = sql.Column(sql.String(64), primary_key=True)
- domain_id = sql.Column(sql.String(64), primary_key=True)
+ domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
+ primary_key=True)
data = sql.Column(sql.JsonBlob())
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/dependency.py b/keystone/common/dependency.py
index 3ed261cc..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):
@@ -67,6 +77,26 @@ def requires(*dependencies):
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.
@@ -75,3 +105,4 @@ def reset():
"""
REGISTRY.clear()
+ _future_dependencies.clear()
diff --git a/keystone/common/logging.py b/keystone/common/logging.py
index 8c036f02..d221edb7 100644
--- a/keystone/common/logging.py
+++ b/keystone/common/logging.py
@@ -23,7 +23,6 @@ import logging
import logging.config
import logging.handlers
import pprint
-import traceback
# A list of things we want to replicate from logging.
@@ -78,8 +77,7 @@ def fail_gracefully(f):
try:
return f(*args, **kw)
except Exception as e:
- # tracebacks are kept in the debug log
- logging.debug(traceback.format_exc(e))
+ logging.debug(e, exc_info=True)
# exception message is printed to all logs
logging.critical(e)
diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py
index 381f1ff0..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')
diff --git a/keystone/identity/backends/ldap.py b/keystone/identity/backends/ldap.py
index f9e546a9..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__()
@@ -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/sql.py b/keystone/identity/backends/sql.py
index ba97758c..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"
diff --git a/keystone/identity/controllers.py b/keystone/identity/controllers.py
index 12fb8145..7ca1f8bf 100644
--- a/keystone/identity/controllers.py
+++ b/keystone/identity/controllers.py
@@ -403,6 +403,8 @@ class DomainV3(controller.V3Controller):
@controller.protected
def create_domain(self, context, domain):
+ self._require_attribute(domain, 'name')
+
ref = self._assign_unique_id(self._normalize_dict(domain))
ref = self.identity_api.create_domain(ref['id'], ref)
return DomainV3.wrap_member(context, ref)
@@ -544,6 +546,8 @@ class ProjectV3(controller.V3Controller):
@controller.protected
def create_project(self, context, project):
+ self._require_attribute(project, 'name')
+
ref = self._assign_unique_id(self._normalize_dict(project))
ref = self._normalize_domain_id(context, ref)
ref = self.identity_api.create_project(ref['id'], ref)
@@ -592,6 +596,8 @@ class UserV3(controller.V3Controller):
@controller.protected
def create_user(self, context, user):
+ self._require_attribute(user, 'name')
+
ref = self._assign_unique_id(self._normalize_dict(user))
ref = self._normalize_domain_id(context, ref)
ref = self.identity_api.create_user(ref['id'], ref)
@@ -663,6 +669,8 @@ class GroupV3(controller.V3Controller):
@controller.protected
def create_group(self, context, group):
+ self._require_attribute(group, 'name')
+
ref = self._assign_unique_id(self._normalize_dict(group))
ref = self._normalize_domain_id(context, ref)
ref = self.identity_api.create_group(ref['id'], ref)
@@ -713,6 +721,8 @@ class RoleV3(controller.V3Controller):
@controller.protected
def create_role(self, context, role):
+ self._require_attribute(role, 'name')
+
ref = self._assign_unique_id(self._normalize_dict(role))
ref = self.identity_api.create_role(ref['id'], ref)
return RoleV3.wrap_member(context, ref)
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index 3d9bcf62..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()
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 cbe51fb2..9118b2ea 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -266,7 +266,11 @@ class TestCase(NoModule, unittest.TestCase):
# only call load_backends once.
dependency.reset()
- for manager in [assignment, catalog, credential, ec2, identity, policy,
+ # 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__ is like keystone.xxx[.yyy],
# converted to xxx[_yyy]
@@ -276,6 +280,8 @@ class TestCase(NoModule, unittest.TestCase):
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/trust/controllers.py b/keystone/trust/controllers.py
index 7be6e8fd..7a94fe29 100644
--- a/keystone/trust/controllers.py
+++ b/keystone/trust/controllers.py
@@ -35,6 +35,21 @@ class TrustV3(controller.V3Controller):
collection_name = "trusts"
member_name = "trust"
+ @classmethod
+ def base_url(cls, path=None):
+ endpoint = CONF.public_endpoint % CONF
+
+ # allow a missing trailing slash in the config
+ if endpoint[-1] != '/':
+ endpoint += '/'
+
+ url = endpoint + 'v3/OS-TRUST'
+
+ if path:
+ return url + path
+ else:
+ return url + '/' + cls.collection_name
+
def _get_user_id(self, context):
if 'token_id' in context:
token_id = context['token_id']
@@ -78,8 +93,7 @@ class TrustV3(controller.V3Controller):
trust_full_roles.append(full_role)
trust['roles'] = trust_full_roles
trust['roles_links'] = {
- 'self': (CONF.public_endpoint % CONF +
- "trusts/%s/roles" % trust['id']),
+ 'self': (self.base_url() + "/%s/roles" % trust['id']),
'next': None,
'previous': None}
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 1416269b..db5314be 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -618,6 +618,10 @@ class AuthWithTrust(AuthTest):
role_ids = [self.role_browser['id'], self.role_member['id']]
self.assertTrue(timeutils.parse_strtime(self.new_trust['expires_at'],
fmt=TIME_FORMAT))
+ self.assertIn('http://localhost:5000/v3/OS-TRUST/',
+ self.new_trust['links']['self'])
+ self.assertIn('http://localhost:5000/v3/OS-TRUST/',
+ self.new_trust['roles_links']['self'])
for role in self.new_trust['roles']:
self.assertIn(role['id'], role_ids)
diff --git a/tests/test_backend_sql.py b/tests/test_backend_sql.py
index 38eddaa4..89276e86 100644
--- a/tests/test_backend_sql.py
+++ b/tests/test_backend_sql.py
@@ -114,7 +114,7 @@ class SqlModels(SqlTests):
def test_role_model(self):
cols = (('id', sql.String, 64),
- ('name', sql.String, 64))
+ ('name', sql.String, 255))
self.assertExpectedSchema('role', cols)
def test_user_project_metadata_model(self):
diff --git a/tests/test_injection.py b/tests/test_injection.py
index 08ccd7c7..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,6 +169,29 @@ 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.
diff --git a/tests/test_v3_identity.py b/tests/test_v3_identity.py
index 5eaf9085..891f0c6a 100644
--- a/tests/test_v3_identity.py
+++ b/tests/test_v3_identity.py
@@ -105,6 +105,10 @@ class IdentityTestCase(test_v3.RestfulTestCase):
body={'domain': ref})
return self.assertValidDomainResponse(r, ref)
+ def test_create_domain_400(self):
+ """Call ``POST /domains``."""
+ self.post('/domains', body={'domain': {}}, expected_status=400)
+
def test_list_domains(self):
"""Call ``GET /domains``."""
r = self.get('/domains')
@@ -313,6 +317,10 @@ class IdentityTestCase(test_v3.RestfulTestCase):
body={'project': ref})
self.assertValidProjectResponse(r, ref)
+ def test_create_project_400(self):
+ """Call ``POST /projects``."""
+ self.post('/projects', body={'project': {}}, expected_status=400)
+
def test_get_project(self):
"""Call ``GET /projects/{project_id}``."""
r = self.get(
@@ -376,6 +384,10 @@ class IdentityTestCase(test_v3.RestfulTestCase):
body={'user': ref})
return self.assertValidUserResponse(r, ref)
+ def test_create_user_400(self):
+ """Call ``POST /users``."""
+ self.post('/users', body={'user': {}}, expected_status=400)
+
def test_list_users(self):
"""Call ``GET /users``."""
r = self.get('/users')
@@ -529,6 +541,10 @@ class IdentityTestCase(test_v3.RestfulTestCase):
body={'group': ref})
return self.assertValidGroupResponse(r, ref)
+ def test_create_group_400(self):
+ """Call ``POST /groups``."""
+ self.post('/groups', body={'group': {}}, expected_status=400)
+
def test_list_groups(self):
"""Call ``GET /groups``."""
r = self.get('/groups')
@@ -569,6 +585,10 @@ class IdentityTestCase(test_v3.RestfulTestCase):
body={'role': ref})
return self.assertValidRoleResponse(r, ref)
+ def test_create_role_400(self):
+ """Call ``POST /roles``."""
+ self.post('/roles', body={'role': {}}, expected_status=400)
+
def test_list_roles(self):
"""Call ``GET /roles``."""
r = self.get('/roles')
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index 369dd952..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