Update Barbican wrapper
Changes to Barbican key manager to support all managed objects and API changes. Updates to the functional tests will follow in another patch in the interest of shorter code reviews. Change-Id: I2af3e7c2f16f31dcd2b4484a6537d3114bc4b3bb
This commit is contained in:
parent
82a0ded5c8
commit
73e9601095
|
@ -16,28 +16,49 @@
|
|||
"""
|
||||
Key manager implementation for Barbican
|
||||
"""
|
||||
from barbicanclient import client as barbican_client
|
||||
from barbicanclient import exceptions as barbican_exceptions
|
||||
from keystoneclient.auth import token_endpoint
|
||||
import time
|
||||
|
||||
from cryptography.hazmat import backends
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography import x509 as cryptography_x509
|
||||
from keystoneclient.auth import identity
|
||||
from keystoneclient import session
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from castellan.common import exception
|
||||
from castellan.common.objects import key as key_base_class
|
||||
from castellan.common.objects import opaque_data as op_data
|
||||
from castellan.common.objects import passphrase
|
||||
from castellan.common.objects import private_key as pri_key
|
||||
from castellan.common.objects import public_key as pub_key
|
||||
from castellan.common.objects import symmetric_key as sym_key
|
||||
from castellan.common.objects import x_509
|
||||
from castellan.key_manager import key_manager
|
||||
from castellan.openstack.common import _i18n as u
|
||||
|
||||
from barbicanclient import client as barbican_client
|
||||
from barbicanclient import exceptions as barbican_exceptions
|
||||
from six.moves import urllib
|
||||
|
||||
barbican_opts = [
|
||||
cfg.StrOpt('barbican_endpoint',
|
||||
default='http://localhost:9311/',
|
||||
help='Use this endpoint to connect to Barbican'),
|
||||
help='Use this endpoint to connect to Barbican, for example: '
|
||||
'"http://localhost:9311/"'),
|
||||
cfg.StrOpt('barbican_api_version',
|
||||
default='v1',
|
||||
help='Version of the Barbican API'),
|
||||
help='Version of the Barbican API, for example: "v1"'),
|
||||
cfg.StrOpt('auth_endpoint',
|
||||
default='http://localhost:5000/v3',
|
||||
help='Use this endpoint to connect to Keystone'),
|
||||
cfg.IntOpt('retry_delay',
|
||||
default=1,
|
||||
help='Number of seconds to wait before retrying poll for key '
|
||||
'creation completion'),
|
||||
cfg.IntOpt('number_of_retries',
|
||||
default=60,
|
||||
help='Number of times to retry poll for key creation '
|
||||
'completion'),
|
||||
]
|
||||
|
||||
BARBICAN_OPT_GROUP = 'barbican'
|
||||
|
@ -48,6 +69,14 @@ LOG = logging.getLogger(__name__)
|
|||
class BarbicanKeyManager(key_manager.KeyManager):
|
||||
"""Key Manager Interface that wraps the Barbican client API."""
|
||||
|
||||
_secret_type_dict = {
|
||||
op_data.OpaqueData: 'opaque',
|
||||
passphrase.Passphrase: 'passphrase',
|
||||
pri_key.PrivateKey: 'private',
|
||||
pub_key.PublicKey: 'public',
|
||||
sym_key.SymmetricKey: 'symmetric',
|
||||
x_509.X509: 'certificate'}
|
||||
|
||||
def __init__(self, configuration):
|
||||
self._barbican_client = None
|
||||
self._base_url = None
|
||||
|
@ -61,6 +90,8 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
: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
|
||||
"""
|
||||
|
||||
# Confirm context is provided, if not raise forbidden
|
||||
|
@ -69,13 +100,21 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
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(msg)
|
||||
|
||||
if self._barbican_client and self._current_context == context:
|
||||
return self._barbican_client
|
||||
return self._barbican_client
|
||||
|
||||
try:
|
||||
self._current_context = context
|
||||
sess = self._get_keystone_session(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)
|
||||
|
@ -84,31 +123,54 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(u._LE("Error creating Barbican client: %s"), e)
|
||||
|
||||
self._base_url = self._create_base_url()
|
||||
self._base_url = self._create_base_url(auth,
|
||||
sess,
|
||||
self._barbican_endpoint)
|
||||
|
||||
return self._barbican_client
|
||||
|
||||
def _get_keystone_session(self, context):
|
||||
sess = session.Session.load_from_conf_options(
|
||||
self.conf, BARBICAN_OPT_GROUP)
|
||||
def _get_keystone_auth(self, context):
|
||||
# TODO(kfarr): support keystone v2
|
||||
auth = identity.v3.Token(
|
||||
auth_url=self.conf.barbican.auth_endpoint,
|
||||
token=context.auth_token,
|
||||
project_id=context.tenant,
|
||||
domain_id=context.user_domain,
|
||||
project_domain_id=context.project_domain)
|
||||
return auth
|
||||
|
||||
self._barbican_endpoint = self.conf.barbican.barbican_endpoint
|
||||
def _get_barbican_endpoint(self, auth, sess):
|
||||
if self.conf.barbican.barbican_endpoint:
|
||||
return self.conf.barbican.barbican_endpoint
|
||||
else:
|
||||
service_parameters = {'service_type': 'key-manager',
|
||||
'service_name': 'barbican',
|
||||
'interface': 'public'}
|
||||
return auth.get_endpoint(sess, **service_parameters)
|
||||
|
||||
auth = token_endpoint.Token(self._barbican_endpoint,
|
||||
context.auth_token)
|
||||
sess.auth = auth
|
||||
return sess
|
||||
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(msg)
|
||||
latest_version = raw_data[-1]
|
||||
api_version = latest_version.get('id')
|
||||
|
||||
def _create_base_url(self):
|
||||
base_url = urllib.parse.urljoin(
|
||||
self._barbican_endpoint, self.conf.barbican.barbican_api_version)
|
||||
endpoint, api_version)
|
||||
return base_url
|
||||
|
||||
def create_key(self, context, algorithm, length, expiration=None):
|
||||
"""Creates a symmetric key.
|
||||
|
||||
:param context: contains information of the user and the environment
|
||||
for the request (castellan/context.py)
|
||||
for the request (castellan/context.py)
|
||||
:param algorithm: the algorithm associated with the secret
|
||||
:param length: the bit length of the secret
|
||||
:param expiration: the date the key will expire
|
||||
|
@ -125,7 +187,7 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
bit_length=length,
|
||||
expiration=expiration)
|
||||
order_ref = key_order.submit()
|
||||
order = barbican_client.orders.get(order_ref)
|
||||
order = self._get_active_order(barbican_client, order_ref)
|
||||
return self._retrieve_secret_uuid(order.secret_ref)
|
||||
except (barbican_exceptions.HTTPAuthError,
|
||||
barbican_exceptions.HTTPClientError,
|
||||
|
@ -136,20 +198,89 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
def create_key_pair(self, context, algorithm, length, expiration=None):
|
||||
"""Creates an asymmetric key pair.
|
||||
|
||||
Not implemented yet.
|
||||
|
||||
:param context: contains information of the user and the environment
|
||||
for the request (castellan/context.py)
|
||||
for the request (castellan/context.py)
|
||||
:param algorithm: the algorithm associated with the secret
|
||||
:param length: the bit length of the secret
|
||||
:param expiration: the date the key will expire
|
||||
:return: TODO: the UUIDs of the new key, in the order (private, public)
|
||||
:return: the UUIDs of the new key, in the order (private, public)
|
||||
:raises NotImplementedError: until implemented
|
||||
:raises HTTPAuthError: if key creation fails with 401
|
||||
:raises HTTPClientError: if key creation failes with 4xx
|
||||
:raises HTTPServerError: if key creation fails with 5xx
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
barbican_client = self._get_barbican_client(context)
|
||||
|
||||
try:
|
||||
key_pair_order = barbican_client.orders.create_asymmetric(
|
||||
algorithm=algorithm,
|
||||
bit_length=length,
|
||||
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:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(u._LE("Error creating key pair: %s"), e)
|
||||
|
||||
def _get_barbican_object(self, barbican_client, managed_object):
|
||||
"""Converts the Castellan managed_object to a Barbican secret."""
|
||||
try:
|
||||
algorithm = managed_object.algorithm
|
||||
bit_length = managed_object.bit_length
|
||||
except AttributeError:
|
||||
algorithm = None
|
||||
bit_length = None
|
||||
|
||||
secret_type = self._secret_type_dict.get(type(managed_object),
|
||||
'opaque')
|
||||
payload = self._get_normalized_payload(managed_object.get_encoded(),
|
||||
secret_type)
|
||||
secret = barbican_client.secrets.create(payload=payload,
|
||||
algorithm=algorithm,
|
||||
bit_length=bit_length,
|
||||
secret_type=secret_type)
|
||||
return secret
|
||||
|
||||
def _get_normalized_payload(self, encoded_bytes, secret_type):
|
||||
"""Normalizes the bytes of the object.
|
||||
|
||||
Barbican expects certificates, public keys, and private keys in PEM
|
||||
format, but Castellan expects these objects to be DER encoded bytes
|
||||
instead.
|
||||
"""
|
||||
if secret_type == 'public':
|
||||
key = serialization.load_der_public_key(
|
||||
encoded_bytes,
|
||||
backend=backends.default_backend())
|
||||
return key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||
elif secret_type == 'private':
|
||||
key = serialization.load_der_private_key(
|
||||
encoded_bytes,
|
||||
backend=backends.default_backend(),
|
||||
password=None)
|
||||
return key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption())
|
||||
elif secret_type == 'certificate':
|
||||
cert = cryptography_x509.load_der_x509_certificate(
|
||||
encoded_bytes,
|
||||
backend=backends.default_backend())
|
||||
return cert.public_bytes(encoding=serialization.Encoding.PEM)
|
||||
else:
|
||||
return encoded_bytes
|
||||
|
||||
def store(self, context, managed_object, expiration=None):
|
||||
"""Stores (i.e., registers) an object with the key manager.
|
||||
|
@ -168,15 +299,9 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
barbican_client = self._get_barbican_client(context)
|
||||
|
||||
try:
|
||||
if managed_object.algorithm:
|
||||
algorithm = managed_object.algorithm
|
||||
else:
|
||||
algorithm = None
|
||||
encoded_object = managed_object.get_encoded()
|
||||
# TODO(kfarr) add support for objects other than symmetric keys
|
||||
secret = barbican_client.secrets.create(payload=encoded_object,
|
||||
algorithm=algorithm,
|
||||
expiration=expiration)
|
||||
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,
|
||||
|
@ -199,6 +324,40 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
base_url += '/'
|
||||
return urllib.parse.urljoin(base_url, "secrets/" + key_id)
|
||||
|
||||
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(.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(msg)
|
||||
|
||||
def _retrieve_secret_uuid(self, secret_ref):
|
||||
"""Retrieves the UUID of the secret from the secret_ref.
|
||||
|
||||
|
@ -213,19 +372,64 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
return secret_ref.rpartition('/')[2]
|
||||
|
||||
def _get_secret_data(self, secret):
|
||||
"""Retrieves the secret data given a secret and content_type.
|
||||
"""Retrieves the secret data.
|
||||
|
||||
Converts the Barbican secret to bytes suitable for a Castellan object.
|
||||
If the secret is a public key, private key, or certificate, the secret
|
||||
is expected to be in PEM format and will be converted to DER.
|
||||
|
||||
:param secret: the secret from barbican with the payload of data
|
||||
:returns: the secret data
|
||||
"""
|
||||
# TODO(kfarr) support other types of keys
|
||||
return secret.payload
|
||||
if secret.secret_type == 'public':
|
||||
key = serialization.load_pem_public_key(
|
||||
secret.payload,
|
||||
backend=backends.default_backend())
|
||||
return key.public_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||
elif secret.secret_type == 'private':
|
||||
key = serialization.load_pem_private_key(
|
||||
secret.payload,
|
||||
backend=backends.default_backend(),
|
||||
password=None)
|
||||
return key.private_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption())
|
||||
elif secret.secret_type == 'certificate':
|
||||
cert = cryptography_x509.load_pem_x509_certificate(
|
||||
secret.payload,
|
||||
backend=backends.default_backend())
|
||||
return cert.public_bytes(encoding=serialization.Encoding.DER)
|
||||
else:
|
||||
return secret.payload
|
||||
|
||||
def _get_castellan_object(self, secret):
|
||||
"""Creates a Castellan managed object given the Barbican secret.
|
||||
|
||||
:param secret: the secret from barbican with the payload of data
|
||||
:returns: the castellan object
|
||||
"""
|
||||
secret_type = op_data.OpaqueData
|
||||
for castellan_type, barbican_type in self._secret_type_dict.items():
|
||||
if barbican_type == secret.secret_type:
|
||||
secret_type = castellan_type
|
||||
|
||||
secret_data = self._get_secret_data(secret)
|
||||
|
||||
if issubclass(secret_type, key_base_class.Key):
|
||||
return secret_type(secret.algorithm,
|
||||
secret.bit_length,
|
||||
secret_data)
|
||||
else:
|
||||
return secret_type(secret_data)
|
||||
|
||||
def _get_secret(self, context, key_id):
|
||||
"""Returns the metadata of the secret.
|
||||
|
||||
:param context: contains information of the user and the environment
|
||||
for the request (castellan/context.py)
|
||||
for the request (castellan/context.py)
|
||||
:param key_id: UUID of the secret
|
||||
:return: the secret's metadata
|
||||
:raises HTTPAuthError: if object retrieval fails with 401
|
||||
|
@ -250,7 +454,7 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
Currently only supports retrieving symmetric keys.
|
||||
|
||||
:param context: contains information of the user and the environment
|
||||
for the request (castellan/context.py)
|
||||
for the request (castellan/context.py)
|
||||
:param managed_object_id: the UUID of the object to retrieve
|
||||
:return: SymmetricKey representation of the key
|
||||
:raises HTTPAuthError: if object retrieval fails with 401
|
||||
|
@ -259,12 +463,7 @@ class BarbicanKeyManager(key_manager.KeyManager):
|
|||
"""
|
||||
try:
|
||||
secret = self._get_secret(context, managed_object_id)
|
||||
secret_data = self._get_secret_data(secret)
|
||||
# TODO(kfarr) add support for other objects
|
||||
key = sym_key.SymmetricKey(secret.algorithm,
|
||||
secret.bit_length,
|
||||
secret_data)
|
||||
return key
|
||||
return self._get_castellan_object(secret)
|
||||
except (barbican_exceptions.HTTPAuthError,
|
||||
barbican_exceptions.HTTPClientError,
|
||||
barbican_exceptions.HTTPServerError) as e:
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
Test cases for the barbican key manager.
|
||||
"""
|
||||
|
||||
from barbicanclient import exceptions as barbican_exceptions
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from castellan.common import exception
|
||||
from castellan.common.objects import symmetric_key as key_manager_key
|
||||
from castellan.common.objects import symmetric_key as sym_key
|
||||
from castellan.key_manager import barbican_key_manager
|
||||
from castellan.tests.unit.key_manager import test_key_manager
|
||||
|
||||
|
@ -48,12 +49,16 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
self.hex = ("0080f1429dbefae01b29a4d50cc5c5608bbc3c8ba0246aa42b424baa4"
|
||||
"534ae16")
|
||||
self.key_mgr._base_url = "http://host:9311/v1/"
|
||||
|
||||
self.key_mgr.conf.barbican.number_of_retries = 3
|
||||
self.key_mgr.conf.barbican.retry_delay = 1
|
||||
|
||||
self.addCleanup(self._restore)
|
||||
|
||||
def _restore(self):
|
||||
try:
|
||||
getattr(self, 'original_key')
|
||||
key_manager_key.SymmetricKey = self.original_key
|
||||
sym_key.SymmetricKey = self.original_key
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
@ -80,6 +85,7 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
# Create order and assign return value
|
||||
order = mock.Mock()
|
||||
order.secret_ref = self.secret_ref
|
||||
order.status = u'ACTIVE'
|
||||
self.mock_barbican.orders.get.return_value = order
|
||||
|
||||
# Create the key, get the UUID
|
||||
|
@ -90,11 +96,72 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
self.mock_barbican.orders.get.assert_called_once_with(order_ref_url)
|
||||
self.assertEqual(self.key_id, returned_uuid)
|
||||
|
||||
def test_create_null_context(self):
|
||||
def test_create_key_null_context(self):
|
||||
self.key_mgr._barbican_client = None
|
||||
self.assertRaises(exception.Forbidden,
|
||||
self.key_mgr.create_key, None, 'AES', 256)
|
||||
|
||||
def test_create_key_with_error(self):
|
||||
key_order = mock.Mock()
|
||||
self.mock_barbican.orders.create_key.return_value = key_order
|
||||
key_order.submit = mock.Mock(
|
||||
side_effect=barbican_exceptions.HTTPClientError('test error'))
|
||||
self.assertRaises(barbican_exceptions.HTTPClientError,
|
||||
self.key_mgr.create_key, self.ctxt, 'AES', 256)
|
||||
|
||||
def test_create_key_pair(self):
|
||||
# Create order_ref_url and assign return value
|
||||
order_ref_url = ("http://localhost:9311/v1/orders/"
|
||||
"f45bf211-a917-4ead-9aec-1c91e52609df")
|
||||
asym_order = mock.Mock()
|
||||
self.mock_barbican.orders.create_asymmetric.return_value = asym_order
|
||||
asym_order.submit.return_value = order_ref_url
|
||||
|
||||
# Create order and assign return value
|
||||
order = mock.Mock()
|
||||
container_id = "16caa8f4-dd34-4fb3-bf67-6c20533a30e4"
|
||||
container_ref = ("http://localhost:9311/v1/containers/" + container_id)
|
||||
order.container_ref = container_ref
|
||||
order.status = u'ACTIVE'
|
||||
self.mock_barbican.orders.get.return_value = order
|
||||
|
||||
# Create container and assign return value
|
||||
container = mock.Mock()
|
||||
public_key_id = "43ed09c3-e551-4c24-b612-e619abe9b534"
|
||||
pub_key_ref = ("http://localhost:9311/v1/secrets/" + public_key_id)
|
||||
private_key_id = "32a0bc60-4e10-4269-9f17-f49767e99586"
|
||||
priv_key_ref = ("http://localhost:9311/v1/secrets/" + private_key_id)
|
||||
container.secret_refs = {'public_key': pub_key_ref,
|
||||
'private_key': priv_key_ref}
|
||||
self.mock_barbican.containers.get.return_value = container
|
||||
|
||||
# Create the keys, get the UUIDs
|
||||
returned_private_uuid, returned_public_uuid = (
|
||||
self.key_mgr.create_key_pair(self.ctxt,
|
||||
algorithm='RSA',
|
||||
length=2048))
|
||||
|
||||
self.mock_barbican.orders.get.assert_called_once_with(order_ref_url)
|
||||
self.mock_barbican.containers.get.assert_called_once_with(
|
||||
container_ref)
|
||||
|
||||
self.mock_barbican.orders.get.assert_called_once_with(order_ref_url)
|
||||
self.assertEqual(private_key_id, returned_private_uuid)
|
||||
self.assertEqual(public_key_id, returned_public_uuid)
|
||||
|
||||
def test_create_key_pair_null_context(self):
|
||||
self.key_mgr._barbican_client = None
|
||||
self.assertRaises(exception.Forbidden,
|
||||
self.key_mgr.create_key_pair, None, 'RSA', 2048)
|
||||
|
||||
def test_create_key_pair_with_error(self):
|
||||
asym_order = mock.Mock()
|
||||
self.mock_barbican.orders.create_asymmetric.return_value = asym_order
|
||||
asym_order.submit = mock.Mock(
|
||||
side_effect=barbican_exceptions.HTTPClientError('test error'))
|
||||
self.assertRaises(barbican_exceptions.HTTPClientError,
|
||||
self.key_mgr.create_key_pair, self.ctxt, 'RSA', 2048)
|
||||
|
||||
def test_delete_null_context(self):
|
||||
self.key_mgr._barbican_client = None
|
||||
self.assertRaises(exception.Forbidden,
|
||||
|
@ -108,18 +175,25 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
self.assertRaises(exception.KeyManagerError,
|
||||
self.key_mgr.delete, self.ctxt, None)
|
||||
|
||||
def test_delete_with_error(self):
|
||||
self.mock_barbican.secrets.delete = mock.Mock(
|
||||
side_effect=barbican_exceptions.HTTPClientError('test error'))
|
||||
self.assertRaises(barbican_exceptions.HTTPClientError,
|
||||
self.key_mgr.delete, self.ctxt, self.key_id)
|
||||
|
||||
def test_get_key(self):
|
||||
original_secret_metadata = mock.Mock()
|
||||
original_secret_metadata.algorithm = mock.sentinel.alg
|
||||
original_secret_metadata.bit_length = mock.sentinel.bit
|
||||
original_secret_data = mock.Mock()
|
||||
original_secret_metadata.secret_type = 'symmetric'
|
||||
original_secret_data = b'test key'
|
||||
original_secret_metadata.payload = original_secret_data
|
||||
|
||||
self.mock_barbican.secrets.get.return_value = original_secret_metadata
|
||||
key = self.key_mgr.get(self.ctxt, self.key_id)
|
||||
|
||||
self.get.assert_called_once_with(self.secret_ref)
|
||||
self.assertEqual(key.get_encoded(), original_secret_data)
|
||||
self.assertEqual(original_secret_data, key.get_encoded())
|
||||
|
||||
def test_get_null_context(self):
|
||||
self.key_mgr._barbican_client = None
|
||||
|
@ -130,12 +204,19 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
self.assertRaises(exception.KeyManagerError,
|
||||
self.key_mgr.get, self.ctxt, None)
|
||||
|
||||
def test_store_key_base64(self):
|
||||
def test_get_with_error(self):
|
||||
self.mock_barbican.secrets.get = mock.Mock(
|
||||
side_effect=barbican_exceptions.HTTPClientError('test error'))
|
||||
self.assertRaises(barbican_exceptions.HTTPClientError,
|
||||
self.key_mgr.get, self.ctxt, self.key_id)
|
||||
|
||||
def test_store_key(self):
|
||||
# Create Key to store
|
||||
secret_key = bytes(b'\x01\x02\xA0\xB3')
|
||||
_key = key_manager_key.SymmetricKey('AES',
|
||||
len(secret_key) * 8,
|
||||
secret_key)
|
||||
key_length = len(secret_key) * 8
|
||||
_key = sym_key.SymmetricKey('AES',
|
||||
key_length,
|
||||
secret_key)
|
||||
|
||||
# Define the return values
|
||||
secret = mock.Mock()
|
||||
|
@ -146,25 +227,66 @@ class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
|
|||
returned_uuid = self.key_mgr.store(self.ctxt, _key)
|
||||
|
||||
self.create.assert_called_once_with(algorithm='AES',
|
||||
bit_length=key_length,
|
||||
payload=secret_key,
|
||||
expiration=None)
|
||||
secret_type='symmetric')
|
||||
self.assertEqual(self.key_id, returned_uuid)
|
||||
|
||||
def test_store_key_plaintext(self):
|
||||
# Create the plaintext key
|
||||
secret_key_text = "This is a test text key."
|
||||
_key = key_manager_key.SymmetricKey('AES',
|
||||
len(secret_key_text) * 8,
|
||||
secret_key_text)
|
||||
|
||||
# Store the Key
|
||||
self.key_mgr.store(self.ctxt, _key)
|
||||
self.create.assert_called_once_with(algorithm='AES',
|
||||
payload=secret_key_text,
|
||||
expiration=None)
|
||||
self.assertEqual(0, self.store.call_count)
|
||||
|
||||
def test_store_null_context(self):
|
||||
self.key_mgr._barbican_client = None
|
||||
self.assertRaises(exception.Forbidden,
|
||||
self.key_mgr.store, None, None)
|
||||
|
||||
def test_store_with_error(self):
|
||||
self.mock_barbican.secrets.create = mock.Mock(
|
||||
side_effect=barbican_exceptions.HTTPClientError('test error'))
|
||||
secret_key = bytes(b'\x01\x02\xA0\xB3')
|
||||
key_length = len(secret_key) * 8
|
||||
_key = sym_key.SymmetricKey('AES',
|
||||
key_length,
|
||||
secret_key)
|
||||
self.assertRaises(barbican_exceptions.HTTPClientError,
|
||||
self.key_mgr.store, self.ctxt, _key)
|
||||
|
||||
def test_get_active_order(self):
|
||||
order_ref_url = ("http://localhost:9311/v1/orders/"
|
||||
"4fe939b7-72bc-49aa-bd1e-e979589858af")
|
||||
|
||||
pending_order = mock.Mock()
|
||||
pending_order.status = u'PENDING'
|
||||
pending_order.order_ref = order_ref_url
|
||||
|
||||
active_order = mock.Mock()
|
||||
active_order.secret_ref = self.secret_ref
|
||||
active_order.status = u'ACTIVE'
|
||||
active_order.order_ref = order_ref_url
|
||||
|
||||
self.mock_barbican.orders.get.side_effect = [pending_order,
|
||||
active_order]
|
||||
|
||||
self.key_mgr._get_active_order(self.mock_barbican, order_ref_url)
|
||||
|
||||
self.assertEqual(2, self.mock_barbican.orders.get.call_count)
|
||||
|
||||
calls = [mock.call(order_ref_url), mock.call(order_ref_url)]
|
||||
self.mock_barbican.orders.get.assert_has_calls(calls)
|
||||
|
||||
def test_get_active_order_timeout(self):
|
||||
order_ref_url = ("http://localhost:9311/v1/orders/"
|
||||
"4fe939b7-72bc-49aa-bd1e-e979589858af")
|
||||
|
||||
number_of_retries = self.key_mgr.conf.barbican.number_of_retries
|
||||
|
||||
pending_order = mock.Mock()
|
||||
pending_order.status = u'PENDING'
|
||||
pending_order.order_ref = order_ref_url
|
||||
|
||||
self.mock_barbican.orders.get.return_value = pending_order
|
||||
|
||||
self.assertRaises(exception.KeyManagerError,
|
||||
self.key_mgr._get_active_order,
|
||||
self.mock_barbican,
|
||||
order_ref_url)
|
||||
|
||||
self.assertEqual(number_of_retries + 1,
|
||||
self.mock_barbican.orders.get.call_count)
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
"""
|
||||
These utilility functions are borrowed from Barbican's testing utilites.
|
||||
"""
|
||||
"""These utility functions are borrowed from Barbican's testing utilities."""
|
||||
|
||||
|
||||
def get_certificate_der():
|
||||
|
|
Loading…
Reference in New Issue