def create_key(self, context, algorithm, length, expiration=None, name=None): """Creates a symmetric key. :param context: contains information of the user and the environment for the request (castellan/context.py) :param algorithm: the algorithm associated with the secret :param length: the bit length of the secret :param name: the name of the key :param expiration: the date the key will expire :return: the UUID of the new key :raises KeyManagerError: if key creation fails """ barbican_client = self._get_barbican_client(context) try: key_order = barbican_client.orders.create_key( name=name, algorithm=algorithm, bit_length=length, expiration=expiration ) order_ref = key_order.submit() order = self._get_active_order(barbican_client, order_ref) return self._retrieve_secret_uuid(order.secret_ref) except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: LOG.error(u._LE("Error creating key: %s"), e) raise exception.KeyManagerError(reason=e)
def create_key(self, context, algorithm, length, expiration=None, name=None): """Creates a symmetric key. :param context: contains information of the user and the environment for the request (castellan/context.py) :param algorithm: the algorithm associated with the secret :param length: the bit length of the secret :param name: the name of the key :param expiration: the date the key will expire :return: the UUID of the new key :raises KeyManagerError: if key creation fails """ barbican_client = self._get_barbican_client(context) try: key_order = barbican_client.orders.create_key( name=name, algorithm=algorithm, bit_length=length, expiration=expiration) order_ref = key_order.submit() order = self._get_active_order(barbican_client, order_ref) return self._retrieve_secret_uuid(order.secret_ref) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: LOG.error(u._LE("Error creating key: %s"), e) raise exception.KeyManagerError(reason=e)
def store(self, context, managed_object, expiration=None): """Stores (i.e., registers) an object with the key manager. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object: a secret object with unencrypted payload. Known as "secret" to the barbicanclient api :param expiration: the expiration time of the secret in ISO 8601 format :returns: the UUID of the stored object :raises KeyManagerError: if object store fails """ barbican_client = self._get_barbican_client(context) try: secret = self._get_barbican_object(barbican_client, managed_object) secret.expiration = expiration secret_ref = secret.store() return self._retrieve_secret_uuid(secret_ref) except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: LOG.error(u._LE("Error storing object: %s"), e) raise exception.KeyManagerError(reason=e)
def create_key_pair(self, context, algorithm, length, expiration=None, name=None): """Creates an asymmetric key pair. :param context: contains information of the user and the environment for the request (castellan/context.py) :param algorithm: the algorithm associated with the secret :param length: the bit length of the secret :param name: the name of the key :param expiration: the date the key will expire :return: the UUIDs of the new key, in the order (private, public) :raises NotImplementedError: until implemented :raises KeyManagerError: if key pair creation fails """ barbican_client = self._get_barbican_client(context) try: key_pair_order = barbican_client.orders.create_asymmetric( algorithm=algorithm, bit_length=length, name=name, expiration=expiration ) order_ref = key_pair_order.submit() order = self._get_active_order(barbican_client, order_ref) container = barbican_client.containers.get(order.container_ref) private_key_uuid = self._retrieve_secret_uuid(container.secret_refs["private_key"]) public_key_uuid = self._retrieve_secret_uuid(container.secret_refs["public_key"]) return private_key_uuid, public_key_uuid except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: LOG.error(u._LE("Error creating key pair: %s"), e) raise exception.KeyManagerError(reason=e)
def get(self, context, managed_object_id): """Retrieves the specified managed object. Currently only supports retrieving symmetric keys. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object_id: the UUID of the object to retrieve :return: SymmetricKey representation of the key :raises KeyManagerError: if object retrieval fails :raises ManagedObjectNotFoundError: if object not found """ try: secret = self._get_secret(context, managed_object_id) return self._get_castellan_object(secret) except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: LOG.error(u._LE("Error retrieving object: %s"), e) if self._is_secret_not_found_error(e): raise exception.ManagedObjectNotFoundError(uuid=managed_object_id) else: raise exception.KeyManagerError(reason=e)
def _get_active_order(self, barbican_client, order_ref): """Returns the order when it is active. Barbican key creation is done asynchronously, so this loop continues checking until the order is active or a timeout occurs. """ active_status = u'ACTIVE' error_status = u'ERROR' number_of_retries = self.conf.barbican.number_of_retries retry_delay = self.conf.barbican.retry_delay order = barbican_client.orders.get(order_ref) time.sleep(.25) for n in range(number_of_retries): if order.status == error_status: kwargs = { "status": error_status, "code": order.error_status_code, "reason": order.error_reason } msg = u._LE("Order is in %(status)s status - status code: " "%(code)s, status reason: %(reason)s") % kwargs LOG.error(msg) raise exception.KeyManagerError(reason=msg) if order.status != active_status: kwargs = { 'attempt': n, 'total': number_of_retries, 'status': order.status, 'active': active_status, 'delay': retry_delay } msg = u._LI("Retry attempt #%(attempt)i out of %(total)i: " "Order status is '%(status)s'. Waiting for " "'%(active)s', will retry in %(delay)s " "seconds") LOG.info(msg, kwargs) time.sleep(retry_delay) order = barbican_client.orders.get(order_ref) else: return order msg = u._LE("Exceeded retries: Failed to find '%(active)s' status " "within %(num_retries)i retries") % { 'active': active_status, 'num_retries': number_of_retries } LOG.error(msg) raise exception.KeyManagerError(reason=msg)
def _create_base_url(self, auth, sess, endpoint): if self.conf.barbican.barbican_api_version: api_version = self.conf.barbican.barbican_api_version else: discovery = auth.get_discovery(sess, url=endpoint) raw_data = discovery.raw_version_data() if len(raw_data) == 0: msg = u._LE("Could not find discovery information for %s") % endpoint LOG.error(msg) raise exception.KeyManagerError(reason=msg) latest_version = raw_data[-1] api_version = latest_version.get("id") base_url = urllib.parse.urljoin(endpoint, api_version) return base_url
def _create_base_url(self, auth, sess, endpoint): if self.conf.barbican.barbican_api_version: api_version = self.conf.barbican.barbican_api_version else: discovery = auth.get_discovery(sess, url=endpoint) raw_data = discovery.raw_version_data() if len(raw_data) == 0: msg = u._LE( "Could not find discovery information for %s") % endpoint LOG.error(msg) raise exception.KeyManagerError(reason=msg) latest_version = raw_data[-1] api_version = latest_version.get('id') base_url = urllib.parse.urljoin(endpoint, api_version) return base_url
def _get_barbican_client(self, context): """Creates a client to connect to the Barbican service. :param context: the user context for authentication :return: a Barbican Client object :raises Forbidden: if the context is None :raises KeyManagerError: if context is missing tenant or tenant is None or error occurs while creating client """ # Confirm context is provided, if not raise forbidden if not context: msg = u._("User is not authorized to use key manager.") LOG.error(msg) raise exception.Forbidden(msg) if not hasattr(context, 'tenant') or context.tenant is None: msg = u._("Unable to create Barbican Client without tenant " "attribute in context object.") LOG.error(msg) raise exception.KeyManagerError(reason=msg) if self._barbican_client and self._current_context == context: return self._barbican_client try: self._current_context = context auth = self._get_keystone_auth(context) sess = session.Session(auth=auth) self._barbican_endpoint = self._get_barbican_endpoint(auth, sess) self._barbican_client = barbican_client.Client( session=sess, endpoint=self._barbican_endpoint) except Exception as e: LOG.error(u._LE("Error creating Barbican client: %s"), e) raise exception.KeyManagerError(reason=e) self._base_url = self._create_base_url(auth, sess, self._barbican_endpoint) return self._barbican_client
def create_key_pair(self, context, algorithm, length, expiration=None, name=None): """Creates an asymmetric key pair. :param context: contains information of the user and the environment for the request (castellan/context.py) :param algorithm: the algorithm associated with the secret :param length: the bit length of the secret :param name: the name of the key :param expiration: the date the key will expire :return: the UUIDs of the new key, in the order (private, public) :raises NotImplementedError: until implemented :raises KeyManagerError: if key pair creation fails """ barbican_client = self._get_barbican_client(context) try: key_pair_order = barbican_client.orders.create_asymmetric( algorithm=algorithm, bit_length=length, name=name, expiration=expiration) order_ref = key_pair_order.submit() order = self._get_active_order(barbican_client, order_ref) container = barbican_client.containers.get(order.container_ref) private_key_uuid = self._retrieve_secret_uuid( container.secret_refs['private_key']) public_key_uuid = self._retrieve_secret_uuid( container.secret_refs['public_key']) return private_key_uuid, public_key_uuid except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: LOG.error(u._LE("Error creating key pair: %s"), e) raise exception.KeyManagerError(reason=e)
def _get_secret(self, context, object_id): """Returns the metadata of the secret. :param context: contains information of the user and the environment for the request (castellan/context.py) :param object_id: UUID of the secret :return: the secret's metadata :raises HTTPAuthError: if object retrieval fails with 401 :raises HTTPClientError: if object retrieval fails with 4xx :raises HTTPServerError: if object retrieval fails with 5xx """ barbican_client = self._get_barbican_client(context) try: secret_ref = self._create_secret_ref(object_id) return barbican_client.secrets.get(secret_ref) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: with excutils.save_and_reraise_exception(): LOG.error(u._LE("Error getting secret metadata: %s"), e)
def _get_barbican_client(self, context): """Creates a client to connect to the Barbican service. :param context: the user context for authentication :return: a Barbican Client object :raises Forbidden: if the context is None :raises KeyManagerError: if context is missing tenant or tenant is None or error occurs while creating client """ # Confirm context is provided, if not raise forbidden if not context: msg = u._("User is not authorized to use key manager.") LOG.error(msg) raise exception.Forbidden(msg) if not hasattr(context, "tenant") or context.tenant is None: msg = u._("Unable to create Barbican Client without tenant " "attribute in context object.") LOG.error(msg) raise exception.KeyManagerError(reason=msg) if self._barbican_client and self._current_context == context: return self._barbican_client try: self._current_context = context auth = self._get_keystone_auth(context) sess = session.Session(auth=auth) self._barbican_endpoint = self._get_barbican_endpoint(auth, sess) self._barbican_client = barbican_client.Client(session=sess, endpoint=self._barbican_endpoint) except Exception as e: LOG.error(u._LE("Error creating Barbican client: %s"), e) raise exception.KeyManagerError(reason=e) self._base_url = self._create_base_url(auth, sess, self._barbican_endpoint) return self._barbican_client
def delete(self, context, managed_object_id): """Deletes the specified managed object. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object_id: the UUID of the object to delete :raises KeyManagerError: if key deletion fails :raises ManagedObjectNotFoundError: if the object could not be found """ barbican_client = self._get_barbican_client(context) try: secret_ref = self._create_secret_ref(managed_object_id) barbican_client.secrets.delete(secret_ref) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: LOG.error(u._LE("Error deleting object: %s"), e) if self._is_secret_not_found_error(e): raise exception.ManagedObjectNotFoundError( uuid=managed_object_id) else: raise exception.KeyManagerError(reason=e)
def _get_active_order(self, barbican_client, order_ref): """Returns the order when it is active. Barbican key creation is done asynchronously, so this loop continues checking until the order is active or a timeout occurs. """ active = u"ACTIVE" number_of_retries = self.conf.barbican.number_of_retries retry_delay = self.conf.barbican.retry_delay order = barbican_client.orders.get(order_ref) time.sleep(0.25) for n in range(number_of_retries): if order.status != active: kwargs = { "attempt": n, "total": number_of_retries, "status": order.status, "active": active, "delay": retry_delay, } msg = u._LI( "Retry attempt #%(attempt)i out of %(total)i: " "Order status is '%(status)s'. Waiting for " "'%(active)s', will retry in %(delay)s " "seconds" ) LOG.info(msg, kwargs) time.sleep(retry_delay) order = barbican_client.orders.get(order_ref) else: return order msg = u._LE("Exceeded retries: Failed to find '%(active)s' status " "within %(num_retries)i retries") % { "active": active, "num_retries": number_of_retries, } LOG.error(msg) raise exception.KeyManagerError(reason=msg)
def get(self, context, managed_object_id): """Retrieves the specified managed object. Currently only supports retrieving symmetric keys. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object_id: the UUID of the object to retrieve :return: SymmetricKey representation of the key :raises KeyManagerError: if object retrieval fails :raises ManagedObjectNotFoundError: if object not found """ try: secret = self._get_secret(context, managed_object_id) return self._get_castellan_object(secret) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: LOG.error(u._LE("Error retrieving object: %s"), e) if self._is_secret_not_found_error(e): raise exception.ManagedObjectNotFoundError( uuid=managed_object_id) else: raise exception.KeyManagerError(reason=e)
def store(self, context, managed_object, expiration=None): """Stores (i.e., registers) an object with the key manager. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object: a secret object with unencrypted payload. Known as "secret" to the barbicanclient api :param expiration: the expiration time of the secret in ISO 8601 format :returns: the UUID of the stored object :raises KeyManagerError: if object store fails """ barbican_client = self._get_barbican_client(context) try: secret = self._get_barbican_object(barbican_client, managed_object) secret.expiration = expiration secret_ref = secret.store() return self._retrieve_secret_uuid(secret_ref) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: LOG.error(u._LE("Error storing object: %s"), e) raise exception.KeyManagerError(reason=e)
def _get_secret(self, context, object_id): """Returns the metadata of the secret. :param context: contains information of the user and the environment for the request (castellan/context.py) :param object_id: UUID of the secret :return: the secret's metadata :raises HTTPAuthError: if object retrieval fails with 401 :raises HTTPClientError: if object retrieval fails with 4xx :raises HTTPServerError: if object retrieval fails with 5xx """ barbican_client = self._get_barbican_client(context) try: secret_ref = self._create_secret_ref(object_id) return barbican_client.secrets.get(secret_ref) except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: with excutils.save_and_reraise_exception(): LOG.error(u._LE("Error getting secret metadata: %s"), e)
def delete(self, context, managed_object_id): """Deletes the specified managed object. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object_id: the UUID of the object to delete :raises KeyManagerError: if key deletion fails :raises ManagedObjectNotFoundError: if the object could not be found """ barbican_client = self._get_barbican_client(context) try: secret_ref = self._create_secret_ref(managed_object_id) barbican_client.secrets.delete(secret_ref) except ( barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError, ) as e: LOG.error(u._LE("Error deleting object: %s"), e) if self._is_secret_not_found_error(e): raise exception.ManagedObjectNotFoundError(uuid=managed_object_id) else: raise exception.KeyManagerError(reason=e)