211 lines
7.7 KiB
Python
211 lines
7.7 KiB
Python
# 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.
|
|
|
|
import pecan
|
|
|
|
from barbican import api
|
|
from barbican.api import controllers
|
|
from barbican.common import hrefs
|
|
from barbican.common import quota
|
|
from barbican.common import resources as res
|
|
from barbican.common import utils
|
|
from barbican.common import validators
|
|
from barbican import i18n as u
|
|
from barbican.model import models
|
|
from barbican.model import repositories as repo
|
|
from barbican.queue import client as async_client
|
|
|
|
LOG = utils.getLogger(__name__)
|
|
|
|
_DEPRECATION_MSG = '%s has been deprecated in the Newton release. ' \
|
|
'It will be removed in the Pike release.'
|
|
|
|
|
|
def _order_not_found():
|
|
"""Throw exception indicating order not found."""
|
|
pecan.abort(404, u._('Order not found.'))
|
|
|
|
|
|
def _secret_not_in_order():
|
|
"""Throw exception that secret info is not available in the order."""
|
|
pecan.abort(400, u._("Secret metadata expected but not received."))
|
|
|
|
|
|
def _order_update_not_supported():
|
|
"""Throw exception that PUT operation is not supported for orders."""
|
|
pecan.abort(405, u._("Order update is not supported."))
|
|
|
|
|
|
def _order_cannot_be_updated_if_not_pending(order_status):
|
|
"""Throw exception that order cannot be updated if not PENDING."""
|
|
pecan.abort(400, u._("Only PENDING orders can be updated. Order is in the"
|
|
"{0} state.").format(order_status))
|
|
|
|
|
|
def order_cannot_modify_order_type():
|
|
"""Throw exception that order type cannot be modified."""
|
|
pecan.abort(400, u._("Cannot modify order type."))
|
|
|
|
|
|
class OrderController(controllers.ACLMixin):
|
|
|
|
"""Handles Order retrieval and deletion requests."""
|
|
|
|
def __init__(self, order, queue_resource=None):
|
|
self.order = order
|
|
self.order_repo = repo.get_order_repository()
|
|
self.queue = queue_resource or async_client.TaskClient()
|
|
self.type_order_validator = validators.TypeOrderValidator()
|
|
|
|
def get_acl_tuple(self, req, **kwargs):
|
|
acl = dict()
|
|
acl['project_id'] = self.order.project.external_id
|
|
acl['creator_id'] = self.order.creator_id
|
|
return 'order', acl
|
|
|
|
@pecan.expose(generic=True)
|
|
def index(self, **kwargs):
|
|
pecan.abort(405) # HTTP 405 Method Not Allowed as default
|
|
|
|
@index.when(method='GET', template='json')
|
|
@controllers.handle_exceptions(u._('Order retrieval'))
|
|
@controllers.enforce_rbac('order:get')
|
|
def on_get(self, external_project_id):
|
|
return hrefs.convert_to_hrefs(self.order.to_dict_fields())
|
|
|
|
@index.when(method='DELETE')
|
|
@utils.allow_all_content_types
|
|
@controllers.handle_exceptions(u._('Order deletion'))
|
|
@controllers.enforce_rbac('order:delete')
|
|
def on_delete(self, external_project_id, **kwargs):
|
|
self.order_repo.delete_entity_by_id(
|
|
entity_id=self.order.id,
|
|
external_project_id=external_project_id)
|
|
|
|
|
|
class OrdersController(controllers.ACLMixin):
|
|
"""Handles Order requests for Secret creation."""
|
|
|
|
def __init__(self, queue_resource=None):
|
|
|
|
LOG.debug('Creating OrdersController')
|
|
self.order_repo = repo.get_order_repository()
|
|
self.queue = queue_resource or async_client.TaskClient()
|
|
self.type_order_validator = validators.TypeOrderValidator()
|
|
self.quota_enforcer = quota.QuotaEnforcer('orders', self.order_repo)
|
|
|
|
@pecan.expose()
|
|
def _lookup(self, order_id, *remainder):
|
|
# NOTE(jaosorior): It's worth noting that even though this section
|
|
# actually does a lookup in the database regardless of the RBAC policy
|
|
# check, the execution only gets here if authentication of the user was
|
|
# previously successful.
|
|
|
|
ctx = controllers._get_barbican_context(pecan.request)
|
|
|
|
order = self.order_repo.get(entity_id=order_id,
|
|
external_project_id=ctx.project_id,
|
|
suppress_exception=True)
|
|
if not order:
|
|
_order_not_found()
|
|
|
|
return OrderController(order, self.order_repo), remainder
|
|
|
|
@pecan.expose(generic=True)
|
|
def index(self, **kwargs):
|
|
pecan.abort(405) # HTTP 405 Method Not Allowed as default
|
|
|
|
@index.when(method='GET', template='json')
|
|
@controllers.handle_exceptions(u._('Order(s) retrieval'))
|
|
@controllers.enforce_rbac('orders:get')
|
|
def on_get(self, external_project_id, **kw):
|
|
LOG.debug('Start orders on_get '
|
|
'for project-ID %s:', external_project_id)
|
|
|
|
result = self.order_repo.get_by_create_date(
|
|
external_project_id, offset_arg=kw.get('offset', 0),
|
|
limit_arg=kw.get('limit', None), meta_arg=kw.get('meta', None),
|
|
suppress_exception=True)
|
|
orders, offset, limit, total = result
|
|
|
|
if not orders:
|
|
orders_resp_overall = {'orders': [],
|
|
'total': total}
|
|
else:
|
|
orders_resp = [
|
|
hrefs.convert_to_hrefs(o.to_dict_fields())
|
|
for o in orders
|
|
]
|
|
orders_resp_overall = hrefs.add_nav_hrefs('orders',
|
|
offset, limit, total,
|
|
{'orders': orders_resp})
|
|
orders_resp_overall.update({'total': total})
|
|
|
|
return orders_resp_overall
|
|
|
|
@index.when(method='PUT', template='json')
|
|
@controllers.handle_exceptions(u._('Order update'))
|
|
@controllers.enforce_rbac('orders:put')
|
|
def on_put(self, external_project_id, **kwargs):
|
|
_order_update_not_supported()
|
|
|
|
@index.when(method='POST', template='json')
|
|
@controllers.handle_exceptions(u._('Order creation'))
|
|
@controllers.enforce_rbac('orders:post')
|
|
@controllers.enforce_content_types(['application/json'])
|
|
def on_post(self, external_project_id, **kwargs):
|
|
project = res.get_or_create_project(external_project_id)
|
|
|
|
body = api.load_body(pecan.request,
|
|
validator=self.type_order_validator)
|
|
|
|
order_type = body.get('type')
|
|
order_meta = body.get('meta')
|
|
request_type = order_meta.get('request_type')
|
|
|
|
LOG.debug('Processing order type %(order_type)s,'
|
|
' request type %(request_type)s' %
|
|
{'order_type': order_type,
|
|
'request_type': request_type})
|
|
|
|
self.quota_enforcer.enforce(project)
|
|
|
|
new_order = models.Order()
|
|
new_order.meta = body.get('meta')
|
|
new_order.type = order_type
|
|
new_order.project_id = project.id
|
|
|
|
request_id = None
|
|
ctxt = controllers._get_barbican_context(pecan.request)
|
|
if ctxt:
|
|
new_order.creator_id = ctxt.user
|
|
request_id = ctxt.request_id
|
|
|
|
self.order_repo.create_from(new_order)
|
|
|
|
# Grab our id before commit due to obj expiration from sqlalchemy
|
|
order_id = new_order.id
|
|
|
|
# Force commit to avoid async issues with the workers
|
|
repo.commit()
|
|
|
|
self.queue.process_type_order(order_id=order_id,
|
|
project_id=external_project_id,
|
|
request_id=request_id)
|
|
|
|
url = hrefs.convert_order_to_href(order_id)
|
|
|
|
pecan.response.status = 202
|
|
pecan.response.headers['Location'] = url
|
|
|
|
return {'order_ref': url}
|