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)
|
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)
|
||||||
|
@ -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
|
||||||
|
@ -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')
|
||||||
|
@ -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
|
||||||
|
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)
|
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):
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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.")
|
||||||
|
@ -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.")
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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"],
|
||||||
|
Loading…
Reference in New Issue
Block a user