Merge "Wrapped unexpected exceptions (bug 955411)"

This commit is contained in:
Jenkins 2012-03-20 22:40:51 +00:00 committed by Gerrit Code Review
commit 5ea232a09f
14 changed files with 123 additions and 57 deletions

View File

@ -125,7 +125,7 @@ class Catalog(sql.Base, catalog.Driver):
endpoint_ref = session.query(Endpoint) endpoint_ref = session.query(Endpoint)
endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first() endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
if not endpoint_ref: if not endpoint_ref:
raise exception.NotFound('Endpoint not found') raise exception.EndpointNotFound(endpoint_id=endpoint_id)
with session.begin(): with session.begin():
session.delete(endpoint_ref) session.delete(endpoint_ref)
session.flush() session.flush()

View File

@ -19,8 +19,6 @@
import uuid import uuid
import webob.exc
from keystone import config from keystone import config
from keystone import exception from keystone import exception
from keystone import identity from keystone import identity
@ -131,7 +129,7 @@ class ServiceController(wsgi.Application):
def get_service(self, context, service_id): def get_service(self, context, service_id):
service_ref = self.catalog_api.get_service(context, service_id) service_ref = self.catalog_api.get_service(context, service_id)
if not service_ref: if not service_ref:
raise webob.exc.HTTPNotFound() raise exception.ServiceNotFound(service_id=service_id)
return {'OS-KSADM:service': service_ref} return {'OS-KSADM:service': service_ref}
def delete_service(self, context, service_id): def delete_service(self, context, service_id):
@ -168,11 +166,8 @@ class EndpointController(wsgi.Application):
endpoint_ref['id'] = endpoint_id endpoint_ref['id'] = endpoint_id
service_id = endpoint_ref['service_id'] service_id = endpoint_ref['service_id']
try: if not self.catalog_api.service_exists(context, service_id):
service = self.catalog_api.get_service(context, service_id) raise exception.ServiceNotFound(service_id=service_id)
except exception.ServiceNotFound:
msg = 'No service exists with id %s' % service_id
raise webob.exc.HTTPBadRequest(msg)
new_endpoint_ref = self.catalog_api.create_endpoint( new_endpoint_ref = self.catalog_api.create_endpoint(
context, endpoint_id, endpoint_ref) context, endpoint_id, endpoint_ref)

View File

@ -16,6 +16,7 @@
import ldap import ldap
from keystone import exception
from keystone.common import logging from keystone.common import logging
from keystone.common.ldap import fakeldap from keystone.common.ldap import fakeldap
@ -140,14 +141,16 @@ class BaseLdap(object):
if values['name'] is not None: if values['name'] is not None:
entity = self.get_by_name(values['name']) entity = self.get_by_name(values['name'])
if entity is not None: if entity is not None:
raise Exception('%s with id %s already exists' raise exception.Conflict(type=self.options_name,
% (self.options_name, values['id'])) details='Duplicate name, %s.' %
values['name'])
if values['id'] is not None: if values['id'] is not None:
entity = self.get(values['id']) entity = self.get(values['id'])
if entity is not None: if entity is not None:
raise Exception('%s with id %s already exists' raise exception.Conflict(type=self.options_name,
% (self.options_name, values['id'])) details='Duplicate ID, %s.' %
values['id'])
def create(self, values): def create(self, values):
conn = self.get_connection() conn = self.get_connection()

View File

@ -43,6 +43,7 @@ Column = sql.Column
String = sql.String String = sql.String
ForeignKey = sql.ForeignKey ForeignKey = sql.ForeignKey
DateTime = sql.DateTime DateTime = sql.DateTime
IntegrityError = sql.exc.IntegrityError
# Special Fields # Special Fields

View File

@ -185,6 +185,9 @@ class Application(BaseApplication):
except exception.Error as e: except exception.Error as e:
LOG.warning(e) LOG.warning(e)
return render_exception(e) return render_exception(e)
except Exception as e:
logging.exception(e)
return render_exception(exception.UnexpectedError(exception=e))
if result is None: if result is None:
return render_response(status=(204, 'No Content')) return render_response(status=(204, 'No Content'))

View File

@ -36,8 +36,6 @@ glance to list images needed to perform the requested task.
import uuid import uuid
import webob.exc
from keystone import catalog from keystone import catalog
from keystone import config from keystone import config
from keystone import exception from keystone import exception
@ -114,12 +112,9 @@ class Ec2Controller(wsgi.Application):
credentials['host'] = hostname credentials['host'] = hostname
signature = signer.generate(credentials) signature = signer.generate(credentials)
if not utils.auth_str_equal(credentials.signature, signature): if not utils.auth_str_equal(credentials.signature, signature):
# TODO(termie): proper exception raise exception.Unauthorized(message='Invalid EC2 signature.')
msg = 'Invalid signature'
raise webob.exc.HTTPUnauthorized(explanation=msg)
else: else:
msg = 'Signature not supplied' raise exception.Unauthorized(message='EC2 signature not supplied.')
raise webob.exc.HTTPUnauthorized(explanation=msg)
def authenticate(self, context, credentials=None, def authenticate(self, context, credentials=None,
ec2Credentials=None): ec2Credentials=None):
@ -152,8 +147,7 @@ class Ec2Controller(wsgi.Application):
creds_ref = self.ec2_api.get_credential(context, creds_ref = self.ec2_api.get_credential(context,
credentials['access']) credentials['access'])
if not creds_ref: if not creds_ref:
msg = 'Access key not found' raise exception.Unauthorized(message='EC2 access key not found.')
raise webob.exc.HTTPUnauthorized(explanation=msg)
self.check_signature(creds_ref, credentials) self.check_signature(creds_ref, credentials)
@ -263,7 +257,7 @@ class Ec2Controller(wsgi.Application):
:param context: standard context :param context: standard context
:param user_id: id of user :param user_id: id of user
:raises webob.exc.HTTPForbidden: when token is invalid :raises exception.Forbidden: when token is invalid
""" """
try: try:
@ -273,7 +267,7 @@ class Ec2Controller(wsgi.Application):
raise exception.Unauthorized() raise exception.Unauthorized()
token_user_id = token_ref['user'].get('id') token_user_id = token_ref['user'].get('id')
if not token_user_id == user_id: if not token_user_id == user_id:
raise webob.exc.HTTPForbidden() raise exception.Forbidden()
def _is_admin(self, context): def _is_admin(self, context):
"""Wrap admin assertion error return statement. """Wrap admin assertion error return statement.
@ -294,9 +288,9 @@ class Ec2Controller(wsgi.Application):
:param context: standard context :param context: standard context
:param user_id: expected credential owner :param user_id: expected credential owner
:param credential_id: id of credential object :param credential_id: id of credential object
:raises webob.exc.HTTPForbidden: on failure :raises exception.Forbidden: on failure
""" """
cred_ref = self.ec2_api.get_credential(context, credential_id) cred_ref = self.ec2_api.get_credential(context, credential_id)
if not user_id == cred_ref['user_id']: if not user_id == cred_ref['user_id']:
raise webob.exc.HTTPForbidden() raise exception.Forbidden()

View File

@ -58,26 +58,66 @@ class Unauthorized(Error):
class Forbidden(Error): class Forbidden(Error):
"""You are not authorized to perform the requested action: %(action)s""" """You are not authorized to perform the requested action."""
code = 403 code = 403
title = 'Not Authorized' title = 'Not Authorized'
class ForbiddenAction(Forbidden):
"""You are not authorized to perform the requested action: %(action)s"""
class NotFound(Error): class NotFound(Error):
"""Could not find: %(target)s""" """Could not find: %(target)s"""
code = 404 code = 404
title = 'Not Found' title = 'Not Found'
class NotImplemented(Error): class EndpointNotFound(NotFound):
"""The action you have requested has not been implemented.""" """Could not find endopint: %(endpoint_id)s"""
code = 501
action = 'Not Implemented'
class RoleNotFound(NotFound):
"""Could not find role: %(role_id)s"""
class ServiceNotFound(NotFound):
"""Could not find service: %(service_id)s"""
class TenantNotFound(NotFound):
"""Could not find tenant: %(tenant_id)s"""
class TokenNotFound(NotFound): class TokenNotFound(NotFound):
"""Could not find token: %(token_id)s""" """Could not find token: %(token_id)s"""
class ServiceNotFound(NotFound): class UserNotFound(NotFound):
"""Could not find service: %(service_id)s""" """Could not find user: %(user_id)s"""
class Conflict(Error):
"""Conflict occurred attempting to store %(type)s.
%(details)s
"""
code = 409
title = 'Conflict'
class NotImplemented(Error):
"""The action you have requested has not been implemented."""
code = 501
action = 'Not Implemented'
class UnexpectedError(Error):
"""An unexpected error prevented the server from fulfilling your request.
%(exception)s
"""
code = 500
title = 'Internal Server Error'

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from keystone import exception
from keystone import identity from keystone import identity
from keystone.common import kvs from keystone.common import kvs
from keystone.common import utils from keystone.common import utils
@ -151,9 +152,11 @@ class Identity(kvs.Base, identity.Driver):
# CRUD # CRUD
def create_user(self, user_id, user): def create_user(self, user_id, user):
if self.get_user(user_id): if self.get_user(user_id):
raise Exception('Duplicate id') msg = 'Duplicate ID, %s.' % user_id
raise exception.Conflict(type='user', details=msg)
if self.get_user_by_name(user['name']): if self.get_user_by_name(user['name']):
raise Exception('Duplicate name') msg = 'Duplicate name, %s.' % user['name']
raise exception.Conflict(type='user', details=msg)
user = _ensure_hashed_password(user) user = _ensure_hashed_password(user)
self.db.set('user-%s' % user_id, user) self.db.set('user-%s' % user_id, user)
self.db.set('user_name-%s' % user['name'], user) self.db.set('user_name-%s' % user['name'], user)
@ -166,7 +169,8 @@ class Identity(kvs.Base, identity.Driver):
if 'name' in user: if 'name' in user:
existing = self.db.get('user_name-%s' % user['name']) existing = self.db.get('user_name-%s' % user['name'])
if existing and user_id != existing['id']: if existing and user_id != existing['id']:
raise Exception('Duplicate name') msg = 'Duplicate name, %s.' % user['name']
raise exception.Conflict(type='user', details=msg)
# get the old name and delete it too # get the old name and delete it too
old_user = self.db.get('user-%s' % user_id) old_user = self.db.get('user-%s' % user_id)
new_user = old_user.copy() new_user = old_user.copy()
@ -189,9 +193,11 @@ class Identity(kvs.Base, identity.Driver):
def create_tenant(self, tenant_id, tenant): def create_tenant(self, tenant_id, tenant):
if self.get_tenant(tenant_id): if self.get_tenant(tenant_id):
raise Exception('Duplicate id') msg = 'Duplicate ID, %s.' % tenant_id
raise exception.Conflict(type='tenant', details=msg)
if self.get_tenant_by_name(tenant['name']): if self.get_tenant_by_name(tenant['name']):
raise Exception('Duplicate name') msg = 'Duplicate name, %s.' % tenant['name']
raise exception.Conflict(type='tenant', details=msg)
self.db.set('tenant-%s' % tenant_id, tenant) self.db.set('tenant-%s' % tenant_id, tenant)
self.db.set('tenant_name-%s' % tenant['name'], tenant) self.db.set('tenant_name-%s' % tenant['name'], tenant)
return tenant return tenant
@ -200,7 +206,8 @@ class Identity(kvs.Base, identity.Driver):
if 'name' in tenant: if 'name' in tenant:
existing = self.db.get('tenant_name-%s' % tenant['name']) existing = self.db.get('tenant_name-%s' % tenant['name'])
if existing and tenant_id != existing['id']: if existing and tenant_id != existing['id']:
raise Exception('Duplicate name') msg = 'Duplicate name, %s.' % tenant['name']
raise exception.Conflict(type='tenant', details=msg)
# get the old name and delete it too # get the old name and delete it too
old_tenant = self.db.get('tenant-%s' % tenant_id) old_tenant = self.db.get('tenant-%s' % tenant_id)
new_tenant = old_tenant.copy() new_tenant = old_tenant.copy()

View File

@ -545,7 +545,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
def add_user(self, role_id, user_id, tenant_id=None): def add_user(self, role_id, user_id, tenant_id=None):
user = self.user_api.get(user_id) user = self.user_api.get(user_id)
if user is None: if user is None:
raise exception.NotFound('User %s not found' % (user_id,)) raise exception.UserNotFound(user_id=user_id)
role_dn = self._subrole_id_to_dn(role_id, tenant_id) role_dn = self._subrole_id_to_dn(role_id, tenant_id)
conn = self.get_connection() conn = self.get_connection()
user_dn = self.user_api._id_to_dn(user_id) user_dn = self.user_api._id_to_dn(user_id)

View File

@ -15,8 +15,10 @@
# under the License. # under the License.
import copy import copy
import functools
from keystone import identity from keystone import identity
from keystone import exception
from keystone.common import sql from keystone.common import sql
from keystone.common import utils from keystone.common import utils
from keystone.common.sql import migration from keystone.common.sql import migration
@ -35,6 +37,19 @@ def _ensure_hashed_password(user_ref):
return user_ref return user_ref
def handle_conflicts(type='object'):
"""Converts IntegrityError into HTTP 409 Conflict."""
def decorator(method):
@functools.wraps(method)
def wrapper(*args, **kwargs):
try:
return method(*args, **kwargs)
except sql.IntegrityError as e:
raise exception.Conflict(type=type, details=str(e))
return wrapper
return decorator
class User(sql.ModelBase, sql.DictBase): class User(sql.ModelBase, sql.DictBase):
__tablename__ = 'user' __tablename__ = 'user'
id = sql.Column(sql.String(64), primary_key=True) id = sql.Column(sql.String(64), primary_key=True)
@ -280,6 +295,7 @@ class Identity(sql.Base, identity.Driver):
self.create_metadata(user_id, tenant_id, metadata_ref) self.create_metadata(user_id, tenant_id, metadata_ref)
# CRUD # CRUD
@handle_conflicts(type='user')
def create_user(self, user_id, user): def create_user(self, user_id, user):
user = _ensure_hashed_password(user) user = _ensure_hashed_password(user)
session = self.get_session() session = self.get_session()
@ -289,6 +305,7 @@ class Identity(sql.Base, identity.Driver):
session.flush() session.flush()
return user_ref.to_dict() return user_ref.to_dict()
@handle_conflicts(type='user')
def update_user(self, user_id, user): def update_user(self, user_id, user):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -311,6 +328,7 @@ class Identity(sql.Base, identity.Driver):
session.delete(user_ref) session.delete(user_ref)
session.flush() session.flush()
@handle_conflicts(type='tenant')
def create_tenant(self, tenant_id, tenant): def create_tenant(self, tenant_id, tenant):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -319,6 +337,7 @@ class Identity(sql.Base, identity.Driver):
session.flush() session.flush()
return tenant_ref.to_dict() return tenant_ref.to_dict()
@handle_conflicts(type='tenant')
def update_tenant(self, tenant_id, tenant): def update_tenant(self, tenant_id, tenant):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -340,6 +359,7 @@ class Identity(sql.Base, identity.Driver):
session.delete(tenant_ref) session.delete(tenant_ref)
session.flush() session.flush()
@handle_conflicts(type='metadata')
def create_metadata(self, user_id, tenant_id, metadata): def create_metadata(self, user_id, tenant_id, metadata):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -349,6 +369,7 @@ class Identity(sql.Base, identity.Driver):
session.flush() session.flush()
return metadata return metadata
@handle_conflicts(type='metadata')
def update_metadata(self, user_id, tenant_id, metadata): def update_metadata(self, user_id, tenant_id, metadata):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -367,6 +388,7 @@ class Identity(sql.Base, identity.Driver):
self.db.delete('metadata-%s-%s' % (tenant_id, user_id)) self.db.delete('metadata-%s-%s' % (tenant_id, user_id))
return None return None
@handle_conflicts(type='role')
def create_role(self, role_id, role): def create_role(self, role_id, role):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():
@ -374,6 +396,7 @@ class Identity(sql.Base, identity.Driver):
session.flush() session.flush()
return role return role
@handle_conflicts(type='role')
def update_role(self, role_id, role): def update_role(self, role_id, role):
session = self.get_session() session = self.get_session()
with session.begin(): with session.begin():

View File

@ -20,8 +20,6 @@ import uuid
import urllib import urllib
import urlparse import urlparse
import webob.exc
from keystone import config from keystone import config
from keystone import exception from keystone import exception
from keystone import policy from keystone import policy
@ -287,7 +285,7 @@ class TenantController(wsgi.Application):
self.assert_admin(context) self.assert_admin(context)
tenant = self.identity_api.get_tenant(context, tenant_id) tenant = self.identity_api.get_tenant(context, tenant_id)
if not tenant: if not tenant:
return webob.exc.HTTPNotFound() raise exception.TenantNotFound(tenant_id=tenant_id)
return {'tenant': tenant} return {'tenant': tenant}
# CRUD Extension # CRUD Extension
@ -329,16 +327,17 @@ class TenantController(wsgi.Application):
break break
else: else:
msg = 'Marker could not be found' msg = 'Marker could not be found'
raise webob.exc.HTTPBadRequest(explanation=msg) raise exception.ValidationError(message=msg)
limit = kwargs.get('limit') limit = kwargs.get('limit')
if limit is not None: if limit is not None:
try: try:
limit = int(limit) limit = int(limit)
assert limit >= 0 if limit < 0:
raise AssertionError()
except (ValueError, AssertionError): except (ValueError, AssertionError):
msg = 'Invalid limit value' msg = 'Invalid limit value'
raise webob.exc.HTTPBadRequest(explanation=msg) raise exception.ValidationError(message=msg)
tenant_refs = tenant_refs[page_idx:limit] tenant_refs = tenant_refs[page_idx:limit]
@ -361,7 +360,7 @@ class UserController(wsgi.Application):
self.assert_admin(context) self.assert_admin(context)
user_ref = self.identity_api.get_user(context, user_id) user_ref = self.identity_api.get_user(context, user_id)
if not user_ref: if not user_ref:
raise webob.exc.HTTPNotFound() raise exception.UserNotFound(user_id=user_id)
return {'user': user_ref} return {'user': user_ref}
def get_users(self, context): def get_users(self, context):
@ -425,7 +424,8 @@ class RoleController(wsgi.Application):
""" """
if tenant_id is None: if tenant_id is None:
raise Exception('User roles not supported: tenant_id required') raise exception.NotImplemented(message='User roles not supported: '
'tenant_id required')
roles = self.identity_api.get_roles_for_user_and_tenant( roles = self.identity_api.get_roles_for_user_and_tenant(
context, user_id, tenant_id) context, user_id, tenant_id)
return {'roles': [self.identity_api.get_role(context, x) return {'roles': [self.identity_api.get_role(context, x)
@ -436,7 +436,7 @@ class RoleController(wsgi.Application):
self.assert_admin(context) self.assert_admin(context)
role_ref = self.identity_api.get_role(context, role_id) role_ref = self.identity_api.get_role(context, role_id)
if not role_ref: if not role_ref:
raise webob.exc.HTTPNotFound() raise exception.RoleNotFound(role_id=role_id)
return {'role': role_ref} return {'role': role_ref}
def create_role(self, context, role): def create_role(self, context, role):
@ -466,7 +466,8 @@ class RoleController(wsgi.Application):
""" """
self.assert_admin(context) self.assert_admin(context)
if tenant_id is None: if tenant_id is None:
raise Exception('User roles not supported: tenant_id required') raise exception.NotImplemented(message='User roles not supported: '
'tenant_id required')
# This still has the weird legacy semantics that adding a role to # This still has the weird legacy semantics that adding a role to
# a user also adds them to a tenant # a user also adds them to a tenant
@ -485,7 +486,8 @@ class RoleController(wsgi.Application):
""" """
self.assert_admin(context) self.assert_admin(context)
if tenant_id is None: if tenant_id is None:
raise Exception('User roles not supported: tenant_id required') raise exception.NotImplemented(message='User roles not supported: '
'tenant_id required')
# This still has the weird legacy semantics that adding a role to # This still has the weird legacy semantics that adding a role to
# a user also adds them to a tenant # a user also adds them to a tenant

View File

@ -97,7 +97,7 @@ def enforce(credentials, action, target):
try: try:
common_policy.enforce(match_list, target, credentials) common_policy.enforce(match_list, target, credentials)
except common_policy.NotAuthorized: except common_policy.NotAuthorized:
raise exception.Forbidden(action=action) raise exception.ForbiddenAction(action=action)
class Policy(policy.Driver): class Policy(policy.Driver):

View File

@ -17,8 +17,6 @@
import uuid import uuid
import routes import routes
import webob.dec
import webob.exc
from keystone import catalog from keystone import catalog
from keystone import exception from keystone import exception
@ -277,7 +275,7 @@ class TokenController(wsgi.Application):
# If the user is disabled don't allow them to authenticate # If the user is disabled don't allow them to authenticate
if not user_ref.get('enabled', True): if not user_ref.get('enabled', True):
raise webob.exc.HTTPForbidden('User has been disabled') raise exception.Forbidden(message='User has been disabled')
except AssertionError as e: except AssertionError as e:
raise exception.Unauthorized(e.message) raise exception.Unauthorized(e.message)

View File

@ -54,9 +54,9 @@ class ExceptionTestCase(test.TestCase):
e = exception.Unauthorized() e = exception.Unauthorized()
self.assertValidJsonRendering(e) self.assertValidJsonRendering(e)
def test_forbidden(self): def test_forbidden_action(self):
action = uuid.uuid4().hex action = uuid.uuid4().hex
e = exception.Forbidden(action=action) e = exception.ForbiddenAction(action=action)
self.assertValidJsonRendering(e) self.assertValidJsonRendering(e)
self.assertIn(action, str(e)) self.assertIn(action, str(e))