Files
python-barbicanclient/barbicanclient/orders.py
Adam Harwell 0966770a0e Fix consistency between Order/Secret/Container
Order/Secret capitalized factory methods disappear and are replaced
by more appropriate get/create methods in their respective managers,
to match the revised Container code.

The higher level REST functions for get/get_raw/post/delete become
private so that the namespace for developers using the client is
much cleaner. For example, nothing is visible in the top level
client object besides the manager classes for container/secret/order
and nothing is visible inside the manager classes besides the
appropriate get/create/delete methods (or other applicable
functions).

Internally, the barbican_cli code uses at least one private property
from ContainerManager, but it is assumed to be OK since it is within
the same "package", and the code will be maintained simultaneously.

Change-Id: I89525506f3be26b77421a5b8efa49bb645169aaf
2014-10-08 16:45:37 -05:00

286 lines
8.1 KiB
Python

# 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.
import functools
import logging
from oslo.utils.timeutils import parse_isotime
from barbicanclient import base
from barbicanclient import formatter
LOG = logging.getLogger(__name__)
def immutable_after_save(func):
@functools.wraps(func)
def wrapper(self, *args):
if self._order_ref:
raise base.ImmutableException()
return func(self, *args)
return wrapper
class OrderFormatter(formatter.EntityFormatter):
columns = ("Order href",
"Secret href",
"Created",
"Status",
"Error code",
"Error message"
)
def _get_formatted_data(self):
data = (self.order_ref,
self.secret_ref,
self.created,
self.status,
self.error_status_code,
self.error_reason
)
return data
class Order(OrderFormatter):
"""
Orders are used to request the generation of a Secret in Barbican.
"""
_entity = 'orders'
def __init__(self, api, name=None, algorithm=None, bit_length=None,
mode=None, payload_content_type='application/octet-stream',
order_ref=None, secret_ref=None, status=None,
created=None, updated=None, expiration=None,
error_status_code=None, error_reason=None, secret=None,
meta=None, type=None):
self._api = api
self._order_ref = order_ref
self._type = type
self._meta = meta
if order_ref:
self._error_status_code = error_status_code
self._error_reason = error_reason
self._status = status
self._created = created
self._updated = updated
if self._created:
self._created = parse_isotime(self._created)
if self._updated:
self._updated = parse_isotime(self._updated)
self._secret_ref = secret_ref
self._secret = secret
else:
self._error_status_code = None
self._error_reason = None
self._status = None
self._created = None
self._updated = None
self._secret_ref = None
self._secret = base.filter_empty_keys({
'name': name,
'algorithm': algorithm,
'bit_length': bit_length,
'mode': mode,
'payload_content_type': payload_content_type,
'expiration': expiration
})
if self._secret.get("expiration"):
self._secret['expiration'] = parse_isotime(
self._secret.get('expiration'))
@property
def name(self):
return self._secret.get('name')
@property
def expiration(self):
return self._secret.get('expiration')
@property
def algorithm(self):
return self._secret.get('algorithm')
@property
def bit_length(self):
return self._secret.get('bit_length')
@property
def mode(self):
return self._secret.get('mode')
@property
def payload_content_type(self):
return self._secret.get('payload_content_type')
@property
def order_ref(self):
return self._order_ref
@property
def secret_ref(self):
return self._secret_ref
@property
def created(self):
return self._created
@property
def updated(self):
return self._updated
@property
def status(self):
return self._status
@property
def error_status_code(self):
return self._error_status_code
@property
def error_reason(self):
return self._error_reason
@property
def type(self):
return self._type
@property
def meta(self):
return self._meta
@name.setter
@immutable_after_save
def name(self, value):
self._secret['name'] = value
@expiration.setter
@immutable_after_save
def expiration(self, value):
self._secret['expiration'] = value
@algorithm.setter
@immutable_after_save
def algorithm(self, value):
self._secret['algorithm'] = value
@bit_length.setter
@immutable_after_save
def bit_length(self, value):
self._secret['bit_length'] = value
@mode.setter
@immutable_after_save
def mode(self, value):
self._secret['mode'] = value
@payload_content_type.setter
@immutable_after_save
def payload_content_type(self, value):
self._secret['payload_content_type'] = value
@type.setter
@immutable_after_save
def type(self, value):
self._type = value
@meta.setter
@immutable_after_save
def meta(self, value):
self._meta = value
@immutable_after_save
def submit(self):
order_dict = dict({
'secret': self._secret
})
LOG.debug("Request body: {0}".format(order_dict.get('secret')))
response = self._api._post(self._entity, order_dict)
if response:
self._order_ref = response.get('order_ref')
return self._order_ref
def delete(self):
if self._order_ref:
self._api._delete(self._order_ref)
self._order_ref = None
else:
raise LookupError("Order is not yet stored.")
def __repr__(self):
return 'Order(order_ref={0})'.format(self.order_ref)
class OrderManager(base.BaseEntityManager):
def __init__(self, api):
super(OrderManager, self).__init__(api, 'orders')
def get(self, order_ref):
"""
Get an Order
:param order_ref: Full HATEOAS reference to an Order
:returns: Order
"""
LOG.debug("Getting order - Order href: {0}".format(order_ref))
base.validate_ref(order_ref, 'Order')
response = self._api._get(order_ref)
return Order(api=self._api, **response)
def create(self, name=None, payload_content_type=None,
algorithm=None, bit_length=None, mode=None, expiration=None):
"""
Create an Order
:param name: A friendly name for the secret
:param payload_content_type: The format/type of the secret data
:param algorithm: The algorithm associated with this secret key
:param bit_length: The bit length of this secret key
:param mode: The algorithm mode used with this secret key
:param expiration: The expiration time of the secret in ISO 8601 format
:returns: Order
"""
return Order(api=self._api, name=name,
payload_content_type=payload_content_type,
algorithm=algorithm, bit_length=bit_length, mode=mode,
expiration=expiration)
def delete(self, order_ref):
"""
Delete an Order
:param order_ref: The href for the order
"""
if not order_ref:
raise ValueError('order_ref is required.')
self._api._delete(order_ref)
def list(self, limit=10, offset=0):
"""
List all Orders for the tenant
:param limit: Max number of orders returned
:param offset: Offset orders to begin list
:returns: list of Order objects
"""
LOG.debug('Listing orders - offset {0} limit {1}'.format(offset,
limit))
href = '{0}/{1}'.format(self._api._base_url, self._entity)
params = {'limit': limit, 'offset': offset}
response = self._api._get(href, params)
return [Order(api=self._api, **o) for o in response.get('orders', [])]