Merge "Add verification REST service to Barbican"

This commit is contained in:
Jenkins 2013-11-26 21:49:06 +00:00 committed by Gerrit Code Review
commit a6fc975e4f
13 changed files with 621 additions and 15 deletions

View File

@ -51,6 +51,8 @@ def create_main_app(global_config, **local_conf):
secret = res.SecretResource(crypto_mgr) secret = res.SecretResource(crypto_mgr)
orders = res.OrdersResource() orders = res.OrdersResource()
order = res.OrderResource() order = res.OrderResource()
verifications = res.VerificationsResource()
verification = res.VerificationResource()
# For performance testing only # For performance testing only
performance = res.PerformanceResource() performance = res.PerformanceResource()
@ -65,6 +67,9 @@ def create_main_app(global_config, **local_conf):
api.add_route('/v1/{keystone_id}/secrets/{secret_id}', secret) api.add_route('/v1/{keystone_id}/secrets/{secret_id}', secret)
api.add_route('/v1/{keystone_id}/orders', orders) api.add_route('/v1/{keystone_id}/orders', orders)
api.add_route('/v1/{keystone_id}/orders/{order_id}', order) api.add_route('/v1/{keystone_id}/orders/{order_id}', order)
api.add_route('/v1/{keystone_id}/verifications', verifications)
api.add_route('/v1/{keystone_id}/verifications/{verification_id}',
verification)
# For performance testing only # For performance testing only
api.add_route('/{0}'.format(performance_uri), performance) api.add_route('/{0}'.format(performance_uri), performance)

View File

@ -52,6 +52,13 @@ def _order_not_found(req, resp):
'another castle.'), req, resp) 'another castle.'), req, resp)
def _verification_not_found(req, resp):
"""Throw exception indicating verification not found."""
api.abort(falcon.HTTP_404, u._('Not Found. Sorry but your verification '
'result is in '
'another castle.'), req, resp)
def _put_accept_incorrect(ct, req, resp): def _put_accept_incorrect(ct, req, resp):
"""Throw exception indicating request content-type is not supported.""" """Throw exception indicating request content-type is not supported."""
api.abort(falcon.HTTP_415, api.abort(falcon.HTTP_415,
@ -86,6 +93,15 @@ def convert_secret_to_href(keystone_id, secret_id):
return utils.hostname_for_refs(keystone_id=keystone_id, resource=resource) return utils.hostname_for_refs(keystone_id=keystone_id, resource=resource)
def convert_verification_to_href(keystone_id, verification_id):
"""Convert the tenant/verification IDs to a HATEOS-style href."""
if verification_id:
resource = 'verifications/' + verification_id
else:
resource = 'verifications/????'
return utils.hostname_for_refs(keystone_id=keystone_id, resource=resource)
def convert_order_to_href(keystone_id, order_id): def convert_order_to_href(keystone_id, order_id):
"""Convert the tenant/order IDs to a HATEOS-style href.""" """Convert the tenant/order IDs to a HATEOS-style href."""
if order_id: if order_id:
@ -105,6 +121,11 @@ def convert_to_hrefs(keystone_id, fields):
fields['order_ref'] = convert_order_to_href(keystone_id, fields['order_ref'] = convert_order_to_href(keystone_id,
fields['order_id']) fields['order_id'])
del fields['order_id'] del fields['order_id']
if 'verification_id' in fields:
fields['verification_ref'] = \
convert_verification_to_href(keystone_id,
fields['verification_id'])
del fields['verification_id']
return fields return fields
@ -537,3 +558,105 @@ class OrderResource(api.ApiResource):
_order_not_found(req, resp) _order_not_found(req, resp)
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
class VerificationsResource(api.ApiResource):
"""Handles Verification creation requests.
Creating a verification entity initiates verification processing
on a target resource. The results of this verification processing
can be monitored via this entity.
"""
def __init__(self, tenant_repo=None, verification_repo=None,
queue_resource=None):
self.tenant_repo = tenant_repo or repo.TenantRepo()
self.verification_repo = verification_repo or repo.VerificationRepo()
self.validator = validators.VerificationValidator()
self.queue = queue_resource or queue.get_queue_api()
@handle_exceptions(u._('Verification creation'))
@handle_rbac('verifications:post')
def on_post(self, req, resp, keystone_id):
LOG.debug('Start on_post for tenant-ID {0}:...'.format(keystone_id))
data = api.load_body(req, resp, self.validator)
tenant = res.get_or_create_tenant(keystone_id, self.tenant_repo)
new_verification = models.Verification(data)
new_verification.tenant_id = tenant.id
self.verification_repo.create_from(new_verification)
# Send to workers to process.
self.queue.process_verification(verification_id=new_verification.id,
keystone_id=keystone_id)
resp.status = falcon.HTTP_202
resp.set_header('Location',
'/{0}/verifications/{1}'.format(keystone_id,
new_verification.id))
url = convert_verification_to_href(keystone_id, new_verification.id)
LOG.debug('URI to verification is {0}'.format(url))
resp.body = json.dumps({'verification_ref': url})
@handle_exceptions(u._('Verification(s) retrieval'))
@handle_rbac('verifications:get')
def on_get(self, req, resp, keystone_id):
LOG.debug('Start verifications on_get '
'for tenant-ID {0}:'.format(keystone_id))
result = self.verification_repo.get_by_create_date(
keystone_id,
offset_arg=req.get_param('offset'),
limit_arg=req.get_param('limit'),
suppress_exception=True
)
verifications, offset, limit, total = result
if not verifications:
resp_overall = {'verifications': [], 'total': total}
else:
resp = [convert_to_hrefs(keystone_id, v.to_dict_fields()) for
v in verifications]
resp_overall = add_nav_hrefs('verifications', keystone_id,
offset, limit, total,
{'verifications': resp})
resp_overall.update({'total': total})
resp.status = falcon.HTTP_200
resp.body = json.dumps(resp_overall,
default=json_handler)
class VerificationResource(api.ApiResource):
"""Handles Verification entity retrieval and deletion requests."""
def __init__(self, verification_repo=None):
self.repo = verification_repo or repo.VerificationRepo()
@handle_exceptions(u._('Verification retrieval'))
@handle_rbac('verification:get')
def on_get(self, req, resp, keystone_id, verification_id):
verif = self.repo.get(entity_id=verification_id,
keystone_id=keystone_id,
suppress_exception=True)
if not verif:
_verification_not_found(req, resp)
resp.status = falcon.HTTP_200
resp.body = json.dumps(convert_to_hrefs(keystone_id,
verif.to_dict_fields()),
default=json_handler)
@handle_exceptions(u._('Verification deletion'))
@handle_rbac('verification:delete')
def on_delete(self, req, resp, keystone_id, verification_id):
try:
self.repo.delete_entity_by_id(entity_id=verification_id,
keystone_id=keystone_id)
except exception.NotFound:
_verification_not_found(req, resp)
resp.status = falcon.HTTP_200

View File

@ -53,7 +53,7 @@ def create_secret(data, tenant, crypto_manager,
time_keeper.mark('after Secret model create') time_keeper.mark('after Secret model create')
new_datum = None new_datum = None
content_type = data.get('payload_content_type', content_type = data.get('payload_content_type',
'application/octet-stream') # TODO: Add to Order! 'application/octet-stream')
if 'payload' in data: if 'payload' in data:
payload = data.get('payload') payload = data.get('payload')

View File

@ -244,3 +244,41 @@ class NewOrderValidator(ValidatorBase):
"multiple of 8")) "multiple of 8"))
return json_data return json_data
class VerificationValidator(ValidatorBase):
"""Validate a verification resource request."""
def __init__(self):
self.name = 'Verification'
self.schema = {
"type": "object",
"required": ["resource_type", "resource_ref",
"resource_action", "impersonation_allowed"],
"properties": {
"resource_type": {
"type": "string",
"enum": [
"image"
]
},
"resource_ref": {"type": "string"},
"resource_action": {
"type": "string",
"enum": [
"vm_attach"
]
},
"impersonation_allowed": {"type": "boolean"},
},
}
def validate(self, json_data, parent_schema=None):
schema_name = self._full_name(parent_schema)
try:
schema.validate(json_data, self.schema)
except schema.ValidationError as e:
raise exception.InvalidObject(schema=schema_name, reason=str(e))
return json_data

View File

@ -0,0 +1,37 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Resource verification business logic.
"""
from barbican.common import utils
LOG = utils.getLogger(__name__)
def verify(verification):
"""Verifies if a resource is 'valid' for an action or not.
Based on the target resource information in the supplied verification
entity this function determines if it is valid to use for the specified
action. The supplied entity is then updated with the processing result.
:param verification: A Verification entity
"""
if 'image' == verification.resource_type:
#TODO(jfwood) Add rules or else consider a plugin approach similar to
# barbican/crypto/plugin.py.
verification.is_verified = True

View File

@ -164,6 +164,7 @@ class Tenant(BASE, ModelBase):
keystone_id = sa.Column(sa.String(255), unique=True) keystone_id = sa.Column(sa.String(255), unique=True)
orders = orm.relationship("Order", backref="tenant") orders = orm.relationship("Order", backref="tenant")
verifications = orm.relationship("Verification", backref="tenant")
secrets = orm.relationship("TenantSecret", backref="tenants") secrets = orm.relationship("TenantSecret", backref="tenants")
keks = orm.relationship("KEKDatum", backref="tenant") keks = orm.relationship("KEKDatum", backref="tenant")
@ -196,10 +197,11 @@ class Secret(BASE, ModelBase):
# See barbican.api.resources.py::SecretsResource.on_get() # See barbican.api.resources.py::SecretsResource.on_get()
encrypted_data = orm.relationship("EncryptedDatum", lazy='joined') encrypted_data = orm.relationship("EncryptedDatum", lazy='joined')
def __init__(self, parsed_request): def __init__(self, parsed_request=None):
"""Creates secret from a dict.""" """Creates secret from a dict."""
super(Secret, self).__init__() super(Secret, self).__init__()
if parsed_request:
self.name = parsed_request.get('name') self.name = parsed_request.get('name')
self.expiration = parsed_request.get('expiration') self.expiration = parsed_request.get('expiration')
self.algorithm = parsed_request.get('algorithm') self.algorithm = parsed_request.get('algorithm')
@ -346,8 +348,58 @@ class Order(BASE, ModelBase):
return ret return ret
class Verification(BASE, ModelBase):
"""Represents a Verification result in the datastore.
Verification represent that status of resource verification requests
made by Tenants.
"""
__tablename__ = 'verifications'
tenant_id = sa.Column(sa.String(36), sa.ForeignKey('tenants.id'),
nullable=False)
error_status_code = sa.Column(sa.String(16))
error_reason = sa.Column(sa.String(255))
resource_type = sa.Column(sa.String(255), nullable=False)
resource_ref = sa.Column(sa.String(255), nullable=False)
resource_action = sa.Column(sa.String(255), nullable=False)
impersonation_allowed = sa.Column(sa.Boolean, nullable=False,
default=True)
is_verified = sa.Column(sa.Boolean, nullable=False,
default=False)
def __init__(self, parsed_request=None):
"""Creates a Verification entity from a dict."""
super(Verification, self).__init__()
if parsed_request:
self.resource_type = parsed_request.get('resource_type')
self.resource_ref = parsed_request.get('resource_ref')
self.resource_action = parsed_request.get('resource_action')
self.impersonation_allowed = parsed_request.get('impersonation_'
'allowed')
self.status = States.PENDING
def _do_extra_dict_fields(self):
"""Sub-class hook method: return dict of fields."""
ret = {'verification_id': self.id,
'resource_type': self.resource_type,
'resource_ref': self.resource_ref,
'resource_action': self.resource_action,
'impersonation_allowed': self.impersonation_allowed,
'is_verified': self.is_verified}
if self.error_status_code:
ret['error_status_code'] = self.error_status_code
if self.error_reason:
ret['error_reason'] = self.error_reason
return ret
# Keep this tuple synchronized with the models in the file # Keep this tuple synchronized with the models in the file
MODELS = [TenantSecret, Tenant, Secret, EncryptedDatum, Order] MODELS = [TenantSecret, Tenant, Secret, EncryptedDatum, Order, Verification]
def register_models(engine): def register_models(engine):

View File

@ -228,7 +228,7 @@ def clean_paging_values(offset_arg=0, limit_arg=CONF.default_limit_paging):
limit, offset limit, offset
)) ))
return (offset, limit) return offset, limit
class BaseRepo(object): class BaseRepo(object):
@ -720,3 +720,63 @@ class OrderRepo(BaseRepo):
def _do_validate(self, values): def _do_validate(self, values):
"""Sub-class hook: validate values.""" """Sub-class hook: validate values."""
pass pass
class VerificationRepo(BaseRepo):
"""Repository for the Verification entity."""
def get_by_create_date(self, keystone_id, offset_arg=None, limit_arg=None,
suppress_exception=False, session=None):
"""
Returns a list of verifications, ordered by the date they were
created at and paged based on the offset and limit fields. The
keystone_id is external-to-Barbican value assigned to the tenant
by Keystone.
"""
offset, limit = clean_paging_values(offset_arg, limit_arg)
session = self.get_session(session)
try:
query = session.query(models.Verification) \
.order_by(models.Verification.created_at)
query = query.filter_by(deleted=False) \
.join(models.Tenant, models.Verification.tenant) \
.filter(models.Tenant.keystone_id == keystone_id)
start = offset
end = offset + limit
LOG.debug('Retrieving from {0} to {1}'.format(start, end))
total = query.count()
entities = query[start:end]
LOG.debug('Number entities retrieved: {0} out of {1}'.format(
len(entities), total
))
except sa_orm.exc.NoResultFound:
entities = None
total = 0
if not suppress_exception:
raise exception.NotFound("No %s's found"
% (self._do_entity_name()))
return entities, offset, limit, total
def _do_entity_name(self):
"""Sub-class hook: return entity name, such as for debugging."""
return "Verification"
def _do_create_instance(self):
return models.Verification()
def _do_build_get_query(self, entity_id, keystone_id, session):
"""Sub-class hook: build a retrieve query."""
return session.query(models.Verification).filter_by(id=entity_id) \
.filter_by(deleted=False) \
.join(models.Tenant, models.Verification.tenant) \
.filter(models.Tenant.keystone_id == keystone_id)
def _do_validate(self, values):
"""Sub-class hook: validate values."""
pass

View File

@ -19,7 +19,7 @@ Celery Queue Resources related objects and functions.
from celery import Celery from celery import Celery
from oslo.config import cfg from oslo.config import cfg
from barbican.tasks.resources import BeginOrder from barbican.tasks import resources
from barbican.common import utils from barbican.common import utils
@ -44,7 +44,6 @@ CONF.import_opt('debug', 'barbican.openstack.common.log')
# the bin/barbican-worker to boot up a Celery worker server instance. # the bin/barbican-worker to boot up a Celery worker server instance.
celery = Celery(CONF.celery.project, celery = Celery(CONF.celery.project,
broker=CONF.celery.broker, broker=CONF.celery.broker,
# backend='amqp://',
include=[CONF.celery.include]) include=[CONF.celery.include])
@ -53,9 +52,22 @@ def process_order(order_id, keystone_id):
return process_order_wrapper.delay(order_id, keystone_id) return process_order_wrapper.delay(order_id, keystone_id)
def process_verification(verification_id, keystone_id):
"""Process Verification."""
return process_verification_wrapper.delay(verification_id, keystone_id)
@celery.task @celery.task
def process_order_wrapper(order_id, keystone_id): def process_order_wrapper(order_id, keystone_id):
"""(Celery wrapped task) Process Order.""" """(Celery wrapped task) Process Order."""
LOG.debug('Order id is {0}'.format(order_id)) LOG.debug('Order id is {0}'.format(order_id))
task = BeginOrder() task = resources.BeginOrder()
return task.process(order_id, keystone_id) return task.process(order_id, keystone_id)
@celery.task
def process_verification_wrapper(verification_id, keystone_id):
"""(Celery wrapped task) Process Verification."""
LOG.debug('Verification id is {0}'.format(verification_id))
task = resources.PerformVerification()
return task.process(verification_id, keystone_id)

View File

@ -18,7 +18,7 @@ Simple Queue Resources related objects and functions, making direct calls
to the worker tasks. to the worker tasks.
""" """
from oslo.config import cfg from oslo.config import cfg
from barbican.tasks.resources import BeginOrder from barbican.tasks import resources
from barbican.common import utils from barbican.common import utils
LOG = utils.getLogger(__name__) LOG = utils.getLogger(__name__)
@ -29,9 +29,21 @@ CONF = cfg.CONF
def process_order(order_id, keystone_id): def process_order(order_id, keystone_id):
"""Process Order.""" """Process Order."""
LOG.debug('Order id is {0}'.format(order_id)) LOG.debug('Order id is {0}'.format(order_id))
task = BeginOrder() task = resources.BeginOrder()
try: try:
task.process(order_id, keystone_id) task.process(order_id, keystone_id)
except Exception: except Exception:
LOG.exception(">>>>> Task exception seen, but simulating async " LOG.exception(">>>>> Task exception seen, but simulating async "
"reporting via the Orders entity on the worker side.") "reporting via the Orders entity on the worker side.")
def process_verification(verification_id, keystone_id):
"""Process Verification."""
LOG.debug('Verification id is {0}'.format(verification_id))
task = resources.PerformVerification()
try:
task.process(verification_id, keystone_id)
except Exception:
LOG.exception(">>>>> Task exception seen, but simulating async "
"reporting via the Verification entity on the "
"worker side.")

View File

@ -21,6 +21,7 @@ import abc
from barbican import api from barbican import api
from barbican.common import resources as res from barbican.common import resources as res
from barbican.common import utils from barbican.common import utils
from barbican.common import verifications as ver
from barbican.crypto import extension_manager as em from barbican.crypto import extension_manager as em
from barbican.model import models from barbican.model import models
from barbican.model import repositories as rep from barbican.model import repositories as rep
@ -195,3 +196,44 @@ class BeginOrder(BaseTask):
order.secret_id = new_secret.id order.secret_id = new_secret.id
LOG.debug("...done creating order's secret.") LOG.debug("...done creating order's secret.")
class PerformVerification(BaseTask):
"""Handles beginning processing a Verification request."""
def get_name(self):
return u._('Perform Verification')
def __init__(self, verification_repo=None):
LOG.debug('Creating PerformVerification task processor')
self.verification_repo = verification_repo or rep.VerificationRepo()
def retrieve_entity(self, verification_id, keystone_id):
return self.verification_repo.get(entity_id=verification_id,
keystone_id=keystone_id)
def handle_processing(self, verification, *args, **kwargs):
self.handle_verification(verification)
def handle_error(self, verification, status, message, exception,
*args, **kwargs):
verification.status = models.States.ERROR
verification.error_status_code = status
verification.error_reason = message
self.verification_repo.save(verification)
def handle_success(self, verification, *args, **kwargs):
verification.status = models.States.ACTIVE
self.verification_repo.save(verification)
def handle_verification(self, verification):
"""Handle performing a verification.
Performs a verification process on a reference.
:param verification: Verification to process on behalf of.
"""
# Perform the verification.
ver.verify(verification)
LOG.debug("...done verifying resource.")

View File

@ -913,13 +913,15 @@ class WhenCreatingOrdersUsingOrdersResource(unittest.TestCase):
def test_should_add_new_order(self): def test_should_add_new_order(self):
self.resource.on_post(self.req, self.resp, self.tenant_keystone_id) self.resource.on_post(self.req, self.resp, self.tenant_keystone_id)
self.assertEquals(falcon.HTTP_202, self.resp.status)
self.queue_resource.process_order \ self.queue_resource.process_order \
.assert_called_once_with(order_id=None, .assert_called_once_with(order_id=None,
keystone_id=self.tenant_keystone_id) keystone_id=self.tenant_keystone_id)
args, kwargs = self.order_repo.create_from.call_args args, kwargs = self.order_repo.create_from.call_args
order = args[0] order = args[0]
self.assertTrue(isinstance(order, models.Order)) self.assertIsInstance(order, models.Order)
def test_should_fail_add_new_order_no_secret(self): def test_should_fail_add_new_order_no_secret(self):
self.stream.read.return_value = '{}' self.stream.read.return_value = '{}'
@ -1142,3 +1144,156 @@ class WhenAddingNavigationHrefs(unittest.TestCase):
self.assertIn('previous', data_with_hrefs) self.assertIn('previous', data_with_hrefs)
self.assertNotIn('next', data_with_hrefs) self.assertNotIn('next', data_with_hrefs)
class WhenCreatingVerificationsUsingVerificationsResource(unittest.TestCase):
def setUp(self):
self.resource_type = 'image'
self.resource_ref = 'http://www.images.com/v1/images/12345'
self.resource_action = 'vm_attach'
self.impersonation = True
self.tenant_internal_id = 'tenantid1234'
self.tenant_keystone_id = 'keystoneid1234'
self.tenant = models.Tenant()
self.tenant.id = self.tenant_internal_id
self.tenant.keystone_id = self.tenant_keystone_id
self.tenant_repo = mock.MagicMock()
self.tenant_repo.get.return_value = self.tenant
self.verification_repo = mock.MagicMock()
self.verification_repo.create_from.return_value = None
self.queue_resource = mock.MagicMock()
self.queue_resource.process_verification.return_value = None
self.stream = mock.MagicMock()
self.verify_req = {'resource_type': self.resource_type,
'resource_ref': self.resource_ref,
'resource_action': self.resource_action,
'impersonation_allowed': self.impersonation}
self.json = json.dumps(self.verify_req)
self.stream.read.return_value = self.json
self.req = mock.MagicMock()
self.req.stream = self.stream
self.resp = mock.MagicMock()
self.resource = res.VerificationsResource(self.tenant_repo,
self.verification_repo,
self.queue_resource)
def test_should_add_new_verification(self):
self.resource.on_post(self.req, self.resp, self.tenant_keystone_id)
self.assertEquals(falcon.HTTP_202, self.resp.status)
self.queue_resource.process_verification \
.assert_called_once_with(verification_id=None,
keystone_id=self.tenant_keystone_id)
args, kwargs = self.verification_repo.create_from.call_args
verification = args[0]
self.assertIsInstance(verification, models.Verification)
def test_should_fail_add_new_verification_no_resource_ref(self):
self.verify_req.pop('resource_ref')
self.json = json.dumps(self.verify_req)
self.stream.read.return_value = self.json
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_post(self.req, self.resp,
self.tenant_keystone_id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
def test_should_fail_verification_unsupported_resource_type(self):
self.verify_req['resource_type'] = 'not-a-valid-type'
self.json = json.dumps(self.verify_req)
self.stream.read.return_value = self.json
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_post(self.req, self.resp,
self.tenant_keystone_id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
def test_should_fail_verification_bad_json(self):
self.stream.read.return_value = ''
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_post(self.req, self.resp,
self.tenant_keystone_id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
class WhenGettingOrDeletingVerificationUsingVerifyResource(unittest.TestCase):
def setUp(self):
self.tenant_keystone_id = 'keystoneid1234'
self.requestor = 'requestor1234'
self.verification = self._create_verification(id="id1")
self.verify_repo = mock.MagicMock()
self.verify_repo.get.return_value = self.verification
self.verify_repo.delete_entity_by_id.return_value = None
self.req = mock.MagicMock()
self.resp = mock.MagicMock()
self.resource = res.VerificationResource(self.verify_repo)
def test_should_get_verification(self):
self.resource.on_get(self.req, self.resp, self.tenant_keystone_id,
self.verification.id)
self.verify_repo.get \
.assert_called_once_with(entity_id=self.verification.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
def test_should_delete_verification(self):
self.resource.on_delete(self.req, self.resp, self.tenant_keystone_id,
self.verification.id)
self.verify_repo.delete_entity_by_id \
.assert_called_once_with(entity_id=self.verification.id,
keystone_id=self.tenant_keystone_id)
def test_should_throw_exception_for_get_when_verify_not_found(self):
self.verify_repo.get.return_value = None
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_get(self.req, self.resp, self.tenant_keystone_id,
self.verification.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_404, exception.status)
def test_should_throw_exception_for_delete_when_verify_not_found(self):
self.verify_repo.delete_entity_by_id.side_effect = excep.NotFound(
"Test not found exception")
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_delete(self.req, self.resp,
self.tenant_keystone_id,
self.verification.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_404, exception.status)
def _create_verification(self, id="id",
resource_type='image',
resource_ref='http://www.images.com/images/123',
resource_action='vm_attach',
impersonation_allowed=True):
"""Generate a Verification entity instance."""
verification = models.Verification()
verification.id = id
verification.resource_type = resource_type
verification.resource_ref = resource_ref
verification.resource_action = resource_action
verification.impersonation_allowed = impersonation_allowed
return verification

View File

@ -162,3 +162,69 @@ class WhenBeginningOrder(unittest.TestCase):
# secondary one (ValueError). # secondary one (ValueError).
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.resource.process(self.order.id, self.keystone_id) self.resource.process(self.order.id, self.keystone_id)
class WhenPerformingVerification(unittest.TestCase):
def setUp(self):
self.verif = models.Verification()
self.verif.id = "id1"
self.resource_type = 'image',
self.resource_ref = 'http://www.images.com/images/123',
self.resource_action = 'vm_attach',
self.impersonation_allowed = True
self.keystone_id = 'keystone1234'
self.tenant_id = 'tenantid1234'
self.tenant = models.Tenant()
self.tenant.id = self.tenant_id
self.tenant.keystone_id = self.keystone_id
self.tenant_repo = mock.MagicMock()
self.tenant_repo.get.return_value = self.tenant
self.verif.status = models.States.PENDING
self.verif.tenant_id = self.tenant_id
self.verif.resource_type = self.resource_type
self.verif.resource_ref = self.resource_ref
self.verif.resource_action = self.resource_action
self.verif.impersonation_allowed = self.impersonation_allowed
self.verif_repo = mock.MagicMock()
self.verif_repo.get.return_value = self.verif
self.resource = resources.PerformVerification(self.verif_repo)
def test_should_process_verification(self):
self.resource.process(self.verif.id, self.keystone_id)
self.verif_repo.get \
.assert_called_once_with(entity_id=self.verif.id,
keystone_id=self.keystone_id)
self.assertEqual(self.verif.status, models.States.ACTIVE)
args, kwargs = self.verif_repo.save.call_args
verif = args[0]
self.assertIsInstance(verif, models.Verification)
self.assertEqual(verif.resource_type, self.resource_type)
self.assertEqual(verif.resource_action, self.resource_action)
def test_should_fail_during_retrieval(self):
# Force an error during the verification retrieval phase.
self.verif_repo.get = mock.MagicMock(return_value=None,
side_effect=ValueError())
with self.assertRaises(ValueError):
self.resource.process(self.verif.id, self.keystone_id)
# Verification state doesn't change because can't retrieve
# it to change it.
self.assertEqual(models.States.PENDING, self.verif.status)
def test_should_fail_during_success_report_fail(self):
# Force an error during the processing handler phase.
self.verif_repo.save = mock.MagicMock(return_value=None,
side_effect=ValueError())
with self.assertRaises(ValueError):
self.resource.process(self.verif.id, self.keystone_id)

View File

@ -10,6 +10,10 @@
"orders:get": "rule:all_but_audit", "orders:get": "rule:all_but_audit",
"order:get": "rule:all_users", "order:get": "rule:all_users",
"order:delete": "rule:admin", "order:delete": "rule:admin",
"verifications:post": "rule:admin_or_creator",
"verifications:get": "rule:all_but_audit",
"verification:get": "rule:all_users",
"verification:delete": "rule:admin",
"admin": ["role:admin"], "admin": ["role:admin"],
"observer": ["role:observer"], "observer": ["role:observer"],
"creator": ["role:creator"], "creator": ["role:creator"],