Merge "Add verification REST service to Barbican"
This commit is contained in:
commit
a6fc975e4f
@ -51,6 +51,8 @@ def create_main_app(global_config, **local_conf):
|
||||
secret = res.SecretResource(crypto_mgr)
|
||||
orders = res.OrdersResource()
|
||||
order = res.OrderResource()
|
||||
verifications = res.VerificationsResource()
|
||||
verification = res.VerificationResource()
|
||||
|
||||
# For performance testing only
|
||||
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}/orders', orders)
|
||||
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
|
||||
api.add_route('/{0}'.format(performance_uri), performance)
|
||||
|
@ -52,6 +52,13 @@ def _order_not_found(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):
|
||||
"""Throw exception indicating request content-type is not supported."""
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
"""Convert the tenant/order IDs to a HATEOS-style href."""
|
||||
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_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
|
||||
|
||||
|
||||
@ -537,3 +558,105 @@ class OrderResource(api.ApiResource):
|
||||
_order_not_found(req, resp)
|
||||
|
||||
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
|
||||
|
@ -53,7 +53,7 @@ def create_secret(data, tenant, crypto_manager,
|
||||
time_keeper.mark('after Secret model create')
|
||||
new_datum = None
|
||||
content_type = data.get('payload_content_type',
|
||||
'application/octet-stream') # TODO: Add to Order!
|
||||
'application/octet-stream')
|
||||
|
||||
if 'payload' in data:
|
||||
payload = data.get('payload')
|
||||
|
@ -244,3 +244,41 @@ class NewOrderValidator(ValidatorBase):
|
||||
"multiple of 8"))
|
||||
|
||||
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
|
||||
|
37
barbican/common/verifications.py
Normal file
37
barbican/common/verifications.py
Normal 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
|
@ -164,6 +164,7 @@ class Tenant(BASE, ModelBase):
|
||||
keystone_id = sa.Column(sa.String(255), unique=True)
|
||||
|
||||
orders = orm.relationship("Order", backref="tenant")
|
||||
verifications = orm.relationship("Verification", backref="tenant")
|
||||
secrets = orm.relationship("TenantSecret", backref="tenants")
|
||||
keks = orm.relationship("KEKDatum", backref="tenant")
|
||||
|
||||
@ -196,15 +197,16 @@ class Secret(BASE, ModelBase):
|
||||
# See barbican.api.resources.py::SecretsResource.on_get()
|
||||
encrypted_data = orm.relationship("EncryptedDatum", lazy='joined')
|
||||
|
||||
def __init__(self, parsed_request):
|
||||
def __init__(self, parsed_request=None):
|
||||
"""Creates secret from a dict."""
|
||||
super(Secret, self).__init__()
|
||||
|
||||
self.name = parsed_request.get('name')
|
||||
self.expiration = parsed_request.get('expiration')
|
||||
self.algorithm = parsed_request.get('algorithm')
|
||||
self.bit_length = parsed_request.get('bit_length')
|
||||
self.mode = parsed_request.get('mode')
|
||||
if parsed_request:
|
||||
self.name = parsed_request.get('name')
|
||||
self.expiration = parsed_request.get('expiration')
|
||||
self.algorithm = parsed_request.get('algorithm')
|
||||
self.bit_length = parsed_request.get('bit_length')
|
||||
self.mode = parsed_request.get('mode')
|
||||
|
||||
self.status = States.ACTIVE
|
||||
|
||||
@ -346,8 +348,58 @@ class Order(BASE, ModelBase):
|
||||
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
|
||||
MODELS = [TenantSecret, Tenant, Secret, EncryptedDatum, Order]
|
||||
MODELS = [TenantSecret, Tenant, Secret, EncryptedDatum, Order, Verification]
|
||||
|
||||
|
||||
def register_models(engine):
|
||||
|
@ -228,7 +228,7 @@ def clean_paging_values(offset_arg=0, limit_arg=CONF.default_limit_paging):
|
||||
limit, offset
|
||||
))
|
||||
|
||||
return (offset, limit)
|
||||
return offset, limit
|
||||
|
||||
|
||||
class BaseRepo(object):
|
||||
@ -720,3 +720,63 @@ class OrderRepo(BaseRepo):
|
||||
def _do_validate(self, values):
|
||||
"""Sub-class hook: validate values."""
|
||||
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
|
||||
|
@ -19,7 +19,7 @@ Celery Queue Resources related objects and functions.
|
||||
from celery import Celery
|
||||
|
||||
from oslo.config import cfg
|
||||
from barbican.tasks.resources import BeginOrder
|
||||
from barbican.tasks import resources
|
||||
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.
|
||||
celery = Celery(CONF.celery.project,
|
||||
broker=CONF.celery.broker,
|
||||
# backend='amqp://',
|
||||
include=[CONF.celery.include])
|
||||
|
||||
|
||||
@ -53,9 +52,22 @@ def process_order(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
|
||||
def process_order_wrapper(order_id, keystone_id):
|
||||
"""(Celery wrapped task) Process Order."""
|
||||
LOG.debug('Order id is {0}'.format(order_id))
|
||||
task = BeginOrder()
|
||||
task = resources.BeginOrder()
|
||||
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)
|
||||
|
@ -18,7 +18,7 @@ Simple Queue Resources related objects and functions, making direct calls
|
||||
to the worker tasks.
|
||||
"""
|
||||
from oslo.config import cfg
|
||||
from barbican.tasks.resources import BeginOrder
|
||||
from barbican.tasks import resources
|
||||
from barbican.common import utils
|
||||
|
||||
LOG = utils.getLogger(__name__)
|
||||
@ -29,9 +29,21 @@ CONF = cfg.CONF
|
||||
def process_order(order_id, keystone_id):
|
||||
"""Process Order."""
|
||||
LOG.debug('Order id is {0}'.format(order_id))
|
||||
task = BeginOrder()
|
||||
task = resources.BeginOrder()
|
||||
try:
|
||||
task.process(order_id, keystone_id)
|
||||
except Exception:
|
||||
LOG.exception(">>>>> Task exception seen, but simulating async "
|
||||
"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.")
|
||||
|
@ -21,6 +21,7 @@ import abc
|
||||
from barbican import api
|
||||
from barbican.common import resources as res
|
||||
from barbican.common import utils
|
||||
from barbican.common import verifications as ver
|
||||
from barbican.crypto import extension_manager as em
|
||||
from barbican.model import models
|
||||
from barbican.model import repositories as rep
|
||||
@ -195,3 +196,44 @@ class BeginOrder(BaseTask):
|
||||
order.secret_id = new_secret.id
|
||||
|
||||
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.")
|
||||
|
@ -913,13 +913,15 @@ class WhenCreatingOrdersUsingOrdersResource(unittest.TestCase):
|
||||
def test_should_add_new_order(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_order \
|
||||
.assert_called_once_with(order_id=None,
|
||||
keystone_id=self.tenant_keystone_id)
|
||||
|
||||
args, kwargs = self.order_repo.create_from.call_args
|
||||
order = args[0]
|
||||
self.assertTrue(isinstance(order, models.Order))
|
||||
self.assertIsInstance(order, models.Order)
|
||||
|
||||
def test_should_fail_add_new_order_no_secret(self):
|
||||
self.stream.read.return_value = '{}'
|
||||
@ -1142,3 +1144,156 @@ class WhenAddingNavigationHrefs(unittest.TestCase):
|
||||
|
||||
self.assertIn('previous', 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
|
||||
|
@ -162,3 +162,69 @@ class WhenBeginningOrder(unittest.TestCase):
|
||||
# secondary one (ValueError).
|
||||
with self.assertRaises(TypeError):
|
||||
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)
|
||||
|
@ -10,6 +10,10 @@
|
||||
"orders:get": "rule:all_but_audit",
|
||||
"order:get": "rule:all_users",
|
||||
"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"],
|
||||
"observer": ["role:observer"],
|
||||
"creator": ["role:creator"],
|
||||
|
Loading…
Reference in New Issue
Block a user