barbican/barbican/tests/api/test_resources.py

2456 lines
85 KiB
Python

# Copyright (c) 2013-2014 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.
"""
This test module focuses on typical-flow business logic tests with the API
resource classes. For RBAC tests of these classes, see the
'resources_policy_test.py' module.
"""
import base64
import logging
import mimetypes
import urllib
import mock
import pecan
import webtest
from barbican import api
from barbican.api import app
from barbican.api import controllers
from barbican.common import exception as excep
from barbican.common import hrefs
from barbican.common import validators
import barbican.context
from barbican.model import models
from barbican.openstack.common import timeutils
from barbican.tests import utils
LOG = logging.getLogger(__name__)
def get_barbican_env(keystone_id):
"""Create and return a barbican.context for use with the RBAC decorator
Injects the provided keystone_id.
"""
kwargs = {'roles': None,
'user': None,
'tenant': keystone_id,
'is_admin': True}
ctx = barbican.context.RequestContext(**kwargs)
ctx.policy_enforcer = None
barbican_env = {'barbican.context': ctx}
return barbican_env
def create_secret(id_ref="id", name="name",
algorithm=None, bit_length=None,
mode=None, encrypted_datum=None):
"""Generate a Secret entity instance."""
info = {'id': id_ref,
'name': name,
'algorithm': algorithm,
'bit_length': bit_length,
'mode': mode}
secret = models.Secret(info)
secret.id = id_ref
if encrypted_datum:
secret.encrypted_data = [encrypted_datum]
return secret
def create_order(id_ref="id",
name="name",
algorithm=None,
bit_length=None,
mode=None):
"""Generate an Order entity instance."""
order = models.Order()
order.id = id_ref
order.secret_name = name
order.secret_algorithm = algorithm
order.secret_bit_length = bit_length
order.secret_mode = mode
return order
def create_order_with_meta(id_ref="id", order_type="certificate", meta={},
status='PENDING'):
"""Generate an Order entity instance with Metadata."""
order = models.Order()
order.id = id_ref
order.type = order_type
order.meta = meta
order.status = status
return order
def validate_datum(test, datum):
test.assertIsNone(datum.kek_meta_extended)
test.assertIsNotNone(datum.kek_meta_tenant)
test.assertTrue(datum.kek_meta_tenant.bind_completed)
test.assertIsNotNone(datum.kek_meta_tenant.plugin_name)
test.assertIsNotNone(datum.kek_meta_tenant.kek_label)
def create_container(id_ref):
"""Generate a Container entity instance."""
container = models.Container()
container.id = id_ref
container.name = 'test name'
container.type = 'rsa'
container_secret = models.ContainerSecret()
container_secret.container_id = id
container_secret.secret_id = '123'
container.container_secrets.append(container_secret)
return container
def create_consumer(container_id, id_ref):
"""Generate a ContainerConsumerMetadatum entity instance."""
consumer = models.ContainerConsumerMetadatum(container_id)
consumer.id = id_ref
consumer.name = 'test name'
consumer.URL = 'http://test/url'
return consumer
class SecretAllowAllMimeTypesDecoratorTest(utils.BaseTestCase):
def setUp(self):
super(SecretAllowAllMimeTypesDecoratorTest, self).setUp()
self.mimetype_values = set(mimetypes.types_map.values())
@pecan.expose(generic=True)
@controllers.secrets.allow_all_content_types
def _empty_pecan_exposed_function(self):
pass
def _empty_function(self):
pass
def test_mimetypes_successfully_added_to_mocked_function(self):
empty_function = mock.MagicMock()
empty_function._pecan = {}
func = controllers.secrets.allow_all_content_types(empty_function)
cfg = func._pecan
self.assertEqual(len(self.mimetype_values), len(cfg['content_types']))
def test_mimetypes_successfully_added_to_pecan_exposed_function(self):
cfg = self._empty_pecan_exposed_function._pecan
self.assertEqual(len(self.mimetype_values), len(cfg['content_types']))
def test_decorator_raises_if_function_not_pecan_exposed(self):
self.assertRaises(AttributeError,
controllers.secrets.allow_all_content_types,
self._empty_function)
class FunctionalTest(utils.BaseTestCase):
def setUp(self):
super(FunctionalTest, self).setUp()
root = self.root
config = {'app': {'root': root}}
pecan.set_config(config, overwrite=True)
self.app = webtest.TestApp(pecan.make_app(root))
def tearDown(self):
super(FunctionalTest, self).tearDown()
pecan.set_config({}, overwrite=True)
@property
def root(self):
return controllers.versions.VersionController()
class WhenTestingVersionResource(FunctionalTest):
def test_should_return_200_on_get(self):
resp = self.app.get('/')
self.assertEqual(200, resp.status_int)
def test_should_return_version_json(self):
resp = self.app.get('/')
self.assertTrue('v1' in resp.json)
self.assertEqual('current', resp.json['v1'])
class BaseSecretsResource(FunctionalTest):
"""Base test class for the Secrets resource."""
def setUp(self):
super(BaseSecretsResource, self).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.keystone_id)
@property
def root(self):
self._init()
class RootController(object):
secrets = controllers.secrets.SecretsController(
self.tenant_repo, self.secret_repo,
self.tenant_secret_repo, self.datum_repo, self.kek_repo,
self.secret_meta_repo, self.transport_key_repo
)
return RootController()
def _init(self, payload=b'not-encrypted',
payload_content_type='text/plain',
payload_content_encoding=None):
self.name = 'name'
self.payload = payload
self.payload_content_type = payload_content_type
self.payload_content_encoding = payload_content_encoding
self.secret_algorithm = 'AES'
self.secret_bit_length = 256
self.secret_mode = 'CBC'
self.secret_req = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode}
if payload:
self.secret_req['payload'] = payload
if payload_content_type:
self.secret_req['payload_content_type'] = payload_content_type
if payload_content_encoding:
self.secret_req['payload_content_encoding'] = (
payload_content_encoding)
self.keystone_id = 'keystone1234'
self.tenant_entity_id = 'tid1234'
self.tenant = models.Tenant()
self.tenant.id = self.tenant_entity_id
self.tenant.keystone_id = self.keystone_id
self.tenant_repo = mock.MagicMock()
self.tenant_repo.find_by_keystone_id.return_value = self.tenant
self.secret = models.Secret()
self.secret.id = '123'
self.secret_repo = mock.MagicMock()
self.secret_repo.create_from.return_value = self.secret
self.tenant_secret_repo = mock.MagicMock()
self.tenant_secret_repo.create_from.return_value = None
self.datum_repo = mock.MagicMock()
self.datum_repo.create_from.return_value = None
self.kek_datum = models.KEKDatum()
self.kek_datum.kek_label = "kek_label"
self.kek_datum.bind_completed = False
self.kek_datum.algorithm = ''
self.kek_datum.bit_length = 0
self.kek_datum.mode = ''
self.kek_datum.plugin_meta = ''
self.kek_repo = mock.MagicMock()
self.kek_repo.find_or_create_kek_datum.return_value = self.kek_datum
self.secret_meta_repo = mock.MagicMock()
self.transport_key = models.TransportKey(
'default_plugin_name', 'XXXABCDEF')
self.transport_key_id = 'tkey12345'
self.tkey_url = hrefs.convert_transport_key_to_href(
self.transport_key.id)
self.transport_key_repo = mock.MagicMock()
@mock.patch('barbican.plugin.resources.store_secret')
def _test_should_add_new_secret_with_expiration(self, mock_store_secret):
mock_store_secret.return_value = self.secret, None
expiration = '2114-02-28 12:14:44.180394-05:00'
self.secret_req.update({'expiration': expiration})
resp = self.app.post_json(
'/secrets/',
self.secret_req
)
self.assertEqual(resp.status_int, 201)
# Validation replaces the time.
expected = dict(self.secret_req)
expiration_raw = expected['expiration']
expiration_raw = expiration_raw[:-6].replace('12', '17', 1)
expiration_tz = timeutils.parse_isotime(expiration_raw.strip())
expected['expiration'] = timeutils.normalize_time(expiration_tz)
mock_store_secret.assert_called_once_with(
self.secret_req.get('payload'),
self.secret_req.get('payload_content_type',
'application/octet-stream'),
self.secret_req.get('payload_content_encoding'),
expected,
None,
self.tenant,
mock.ANY,
transport_key_needed=False,
transport_key_id=None
)
@mock.patch('barbican.plugin.resources.store_secret')
def _test_should_add_new_secret_one_step(self, mock_store_secret,
check_tenant_id=True):
"""Test the one-step secret creation.
:param check_tenant_id: True if the retrieved Tenant id needs to be
verified, False to skip this check (necessary for new-Tenant flows).
"""
mock_store_secret.return_value = self.secret, None
resp = self.app.post_json(
'/secrets/',
self.secret_req
)
self.assertEqual(resp.status_int, 201)
expected = dict(self.secret_req)
expected['expiration'] = None
mock_store_secret.assert_called_once_with(
self.secret_req.get('payload'),
self.secret_req.get('payload_content_type',
'application/octet-stream'),
self.secret_req.get('payload_content_encoding'),
expected,
None,
self.tenant if check_tenant_id else mock.ANY,
mock.ANY,
transport_key_needed=False,
transport_key_id=None
)
@mock.patch('barbican.plugin.resources.store_secret')
def _test_should_add_new_secret_one_step_with_tkey_id(
self, mock_store_secret, check_tenant_id=True):
"""Test the one-step secret creation with transport_key_id set
:param check_tenant_id: True if the retrieved Tenant id needs to be
verified, False to skip this check (necessary for new-Tenant flows).
"""
mock_store_secret.return_value = self.secret, None
self.secret_req['transport_key_id'] = self.transport_key_id
resp = self.app.post_json('/secrets/', self.secret_req)
self.assertEqual(resp.status_int, 201)
expected = dict(self.secret_req)
expected['expiration'] = None
mock_store_secret.assert_called_once_with(
self.secret_req.get('payload'),
self.secret_req.get('payload_content_type',
'application/octet-stream'),
self.secret_req.get('payload_content_encoding'),
expected,
None,
self.tenant if check_tenant_id else mock.ANY,
mock.ANY,
transport_key_needed=False,
transport_key_id=self.transport_key_id
)
def _test_should_add_new_secret_if_tenant_does_not_exist(self):
self.tenant_repo.get.return_value = None
self.tenant_repo.find_by_keystone_id.return_value = None
self._test_should_add_new_secret_one_step(check_tenant_id=False)
args, kwargs = self.tenant_repo.create_from.call_args
tenant = args[0]
self.assertIsInstance(tenant, models.Tenant)
self.assertEqual(self.keystone_id, tenant.keystone_id)
def _test_should_add_new_secret_metadata_without_payload(self):
self.app.post_json(
'/secrets/',
{'name': self.name}
)
args, kwargs = self.secret_repo.create_from.call_args
secret = args[0]
self.assertIsInstance(secret, models.Secret)
self.assertEqual(secret.name, self.name)
args, kwargs = self.tenant_secret_repo.create_from.call_args
tenant_secret = args[0]
self.assertIsInstance(tenant_secret, models.TenantSecret)
self.assertEqual(tenant_secret.tenant_id, self.tenant_entity_id)
self.assertEqual(tenant_secret.secret_id, secret.id)
self.assertFalse(self.datum_repo.create_from.called)
@mock.patch('barbican.plugin.resources.store_secret')
def _test_should_add_new_secret_metadata_with_tkey(self,
mock_store_secret):
mock_store_secret.return_value = self.secret, self.transport_key
resp = self.app.post_json(
'/secrets/',
{'name': self.name,
'transport_key_needed': 'true'}
)
self.assertTrue('secret_ref' in resp.json)
self.assertTrue('transport_key_ref' in resp.json)
self.assertEqual(resp.json['transport_key_ref'], self.tkey_url)
@mock.patch('barbican.plugin.resources.store_secret')
def _test_should_add_secret_payload_almost_too_large(self,
mock_store_secret):
mock_store_secret.return_value = self.secret, None
if validators.DEFAULT_MAX_SECRET_BYTES % 4:
raise ValueError('Tests currently require max secrets divides by '
'4 evenly, due to base64 encoding.')
big_text = ''.join(['A' for x
in xrange(validators.DEFAULT_MAX_SECRET_BYTES -
8)])
self.secret_req = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': big_text,
'payload_content_type': self.payload_content_type}
payload_encoding = self.payload_content_encoding
if payload_encoding:
self.secret_req['payload_content_encoding'] = payload_encoding
self.app.post_json('/secrets/', self.secret_req)
def _test_should_raise_due_to_payload_too_large(self):
big_text = ''.join(['A' for x
in xrange(validators.DEFAULT_MAX_SECRET_BYTES +
10)])
self.secret_req = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': big_text,
'payload_content_type': self.payload_content_type}
payload_encoding = self.payload_content_encoding
if payload_encoding:
self.secret_req['payload_content_encoding'] = payload_encoding
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 413)
def _test_should_raise_due_to_empty_payload(self):
self.secret_req = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': ''}
payload_type = self.payload_content_type
payload_encoding = self.payload_content_encoding
if payload_type:
self.secret_req['payload_content_type'] = payload_type
if payload_encoding:
self.secret_req['payload_content_encoding'] = payload_encoding
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
assert resp.status_int == 400
class WhenCreatingPlainTextSecretsUsingSecretsResource(BaseSecretsResource):
def test_should_add_new_secret_one_step(self):
self._test_should_add_new_secret_one_step()
def test_should_add_new_secret_one_step_with_tkey_id(self):
self._test_should_add_new_secret_one_step_with_tkey_id()
def test_should_add_new_secret_with_expiration(self):
self._test_should_add_new_secret_with_expiration()
def test_should_add_new_secret_if_tenant_does_not_exist(self):
self._test_should_add_new_secret_if_tenant_does_not_exist()
def test_should_add_new_secret_metadata_without_payload(self):
self._test_should_add_new_secret_metadata_without_payload()
def test_should_add_new_secret_metadata_with_tkey(self):
self._test_should_add_new_secret_metadata_with_tkey()
def test_should_add_new_secret_payload_almost_too_large(self):
self._test_should_add_secret_payload_almost_too_large()
def test_should_raise_due_to_payload_too_large(self):
self._test_should_raise_due_to_payload_too_large()
def test_should_raise_due_to_empty_payload(self):
self._test_should_raise_due_to_empty_payload()
def test_should_raise_due_to_unsupported_payload_content_type(self):
self.secret_req = {'name': self.name,
'payload_content_type': 'somethingbogushere',
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': self.payload}
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
# TODO(jwood) These tests are integration-style unit tests of the REST -> Pecan
# resources, which are more painful to test now that we have a two-tier
# plugin lookup framework. These tests should instead be re-located to
# unit tests of the secret_store.py and store_crypto.py modules. A separate
# CR will add the additional unit tests.
# def test_create_secret_content_type_text_plain(self):
# # payload_content_type has trailing space
# self.secret_req = {'name': self.name,
# 'payload_content_type': 'text/plain ',
# 'algorithm': self.secret_algorithm,
# 'bit_length': self.secret_bit_length,
# 'mode': self.secret_mode,
# 'payload': self.payload}
#
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req
# )
# self.assertEqual(resp.status_int, 201)
#
# self.secret_req = {'name': self.name,
# 'payload_content_type': ' text/plain',
# 'algorithm': self.secret_algorithm,
# 'bit_length': self.secret_bit_length,
# 'mode': self.secret_mode,
# 'payload': self.payload}
#
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req
# )
# self.assertEqual(resp.status_int, 201)
#
# def test_create_secret_content_type_text_plain_space_charset_utf8(self):
# # payload_content_type has trailing space
# self.secret_req = {'name': self.name,
# 'payload_content_type':
# 'text/plain; charset=utf-8 ',
# 'algorithm': self.secret_algorithm,
# 'bit_length': self.secret_bit_length,
# 'mode': self.secret_mode,
# 'payload': self.payload}
#
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req
# )
# self.assertEqual(resp.status_int, 201)
#
# self.secret_req = {'name': self.name,
# 'payload_content_type':
# ' text/plain; charset=utf-8',
# 'algorithm': self.secret_algorithm,
# 'bit_length': self.secret_bit_length,
# 'mode': self.secret_mode,
# 'payload': self.payload}
#
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req
# )
# self.assertEqual(resp.status_int, 201)
#
# def test_create_secret_with_only_content_type(self):
# # No payload just content_type
# self.secret_req = {'payload_content_type':
# 'text/plain'}
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req,
# expect_errors=True
# )
# self.assertEqual(resp.status_int, 400)
#
# self.secret_req = {'payload_content_type':
# 'text/plain',
# 'payload': 'somejunk'}
# resp = self.app.post_json(
# '/secrets/',
# self.secret_req
# )
# self.assertEqual(resp.status_int, 201)
class WhenCreatingBinarySecretsUsingSecretsResource(BaseSecretsResource):
@property
def root(self):
self._init(payload="...lOtfqHaUUpe6NqLABgquYQ==",
payload_content_type='application/octet-stream',
payload_content_encoding='base64')
return super(WhenCreatingBinarySecretsUsingSecretsResource, self).root
def test_should_add_new_secret_one_step(self):
self._test_should_add_new_secret_one_step()
def test_should_add_new_secret_one_step_with_tkey_id(self):
self._test_should_add_new_secret_one_step_with_tkey_id()
def test_should_add_new_secret_with_expiration(self):
self._test_should_add_new_secret_with_expiration()
def test_should_add_new_secret_if_tenant_does_not_exist(self):
self._test_should_add_new_secret_if_tenant_does_not_exist()
def test_should_add_new_secret_metadata_without_payload(self):
self._test_should_add_new_secret_metadata_without_payload()
def test_should_add_new_secret_metadata_with_tkey(self):
self._test_should_add_new_secret_metadata_with_tkey()
def test_should_add_new_secret_payload_almost_too_large(self):
self._test_should_add_secret_payload_almost_too_large()
def test_should_raise_due_to_payload_too_large(self):
self._test_should_raise_due_to_payload_too_large()
def test_should_raise_due_to_empty_payload(self):
self._test_should_raise_due_to_empty_payload()
def test_create_secret_fails_with_binary_payload_no_encoding(self):
self.secret_req = {
'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': 'lOtfqHaUUpe6NqLABgquYQ==',
'payload_content_type': 'application/octet-stream'
}
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_create_secret_fails_with_binary_payload_bad_encoding(self):
self.secret_req = {
'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': 'lOtfqHaUUpe6NqLABgquYQ==',
'payload_content_type': 'application/octet-stream',
'payload_content_encoding': 'bogus64'
}
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_create_secret_fails_with_binary_payload_no_content_type(self):
self.secret_req = {
'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': 'lOtfqHaUUpe6NqLABgquYQ==',
'payload_content_encoding': 'base64'
}
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_create_secret_fails_with_bad_payload(self):
self.secret_req = {
'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'payload': 'AAAAAAAAA',
'payload_content_type': 'application/octet-stream',
'payload_content_encoding': 'base64'
}
resp = self.app.post_json(
'/secrets/',
self.secret_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
class WhenGettingSecretsListUsingSecretsResource(FunctionalTest):
def setUp(self):
super(WhenGettingSecretsListUsingSecretsResource, self).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.keystone_id)
@property
def root(self):
self._init()
class RootController(object):
secrets = controllers.secrets.SecretsController(
self.tenant_repo, self.secret_repo,
self.tenant_secret_repo, self.datum_repo, self.kek_repo,
self.secret_meta_repo, self.transport_key_repo
)
return RootController()
def _init(self):
self.tenant_id = 'tenant1234'
self.keystone_id = 'keystone1234'
self.name = 'name 1234 !@#$%^&*()_+=-{}[];:<>,./?'
self.secret_algorithm = "AES"
self.secret_bit_length = 256
self.secret_mode = "CBC"
self.num_secrets = 10
self.offset = 2
self.limit = 2
secret_params = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode,
'encrypted_datum': None}
self.secrets = [create_secret(id_ref='id' + str(id),
**secret_params) for
id in xrange(self.num_secrets)]
self.total = len(self.secrets)
self.secret_repo = mock.MagicMock()
self.secret_repo.get_by_create_date.return_value = (self.secrets,
self.offset,
self.limit,
self.total)
self.tenant_repo = mock.MagicMock()
self.tenant_secret_repo = mock.MagicMock()
self.tenant_secret_repo.create_from.return_value = None
self.datum_repo = mock.MagicMock()
self.datum_repo.create_from.return_value = None
self.kek_repo = mock.MagicMock()
self.secret_meta_repo = mock.MagicMock()
self.params = {'offset': self.offset,
'limit': self.limit,
'name': None,
'alg': None,
'bits': 0,
'mode': None}
self.transport_key_repo = mock.MagicMock()
def test_should_list_secrets_by_name(self):
# Quote the name parameter to simulate how it would be
# received in practice via a REST-ful GET query.
self.params['name'] = urllib.quote_plus(self.name)
resp = self.app.get(
'/secrets/',
dict((k, v) for k, v in self.params.items() if v is not None)
)
# Verify that the name is unquoted correctly in the
# secrets.on_get function prior to searching the repo.
self.secret_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True,
name=self.name,
alg=None,
mode=None,
bits=0
)
self.assertIn('secrets', resp.namespace)
secrets = resp.namespace['secrets']
# The result should be the unquoted name
self.assertEqual(secrets[0]['name'], self.name)
def test_should_get_list_secrets(self):
resp = self.app.get(
'/secrets/',
dict((k, v) for k, v in self.params.items() if v is not None)
)
self.secret_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True,
name='',
alg=None,
mode=None,
bits=0
)
self.assertTrue('previous' in resp.namespace)
self.assertTrue('next' in resp.namespace)
url_nav_next = self._create_url(self.keystone_id,
self.offset + self.limit, self.limit)
self.assertTrue(resp.body.count(url_nav_next) == 1)
url_nav_prev = self._create_url(self.keystone_id,
0, self.limit)
self.assertTrue(resp.body.count(url_nav_prev) == 1)
url_hrefs = self._create_url(self.keystone_id)
self.assertTrue(resp.body.count(url_hrefs) ==
(self.num_secrets + 2))
def test_response_should_include_total(self):
resp = self.app.get(
'/secrets/',
dict((k, v) for k, v in self.params.items() if v is not None)
)
self.assertIn('total', resp.namespace)
self.assertEqual(resp.namespace['total'], self.total)
def test_should_handle_no_secrets(self):
del self.secrets[:]
resp = self.app.get(
'/secrets/',
dict((k, v) for k, v in self.params.items() if v is not None)
)
self.secret_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True,
name='',
alg=None,
mode=None,
bits=0
)
self.assertFalse('previous' in resp.namespace)
self.assertFalse('next' in resp.namespace)
def _create_url(self, keystone_id, offset_arg=None, limit_arg=None):
if limit_arg:
offset = int(offset_arg)
limit = int(limit_arg)
return '/secrets?limit={0}&offset={1}'.format(limit,
offset)
else:
return '/secrets'
class WhenGettingPuttingOrDeletingSecretUsingSecretResource(FunctionalTest):
def setUp(self):
super(
WhenGettingPuttingOrDeletingSecretUsingSecretResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.keystone_id)
@property
def root(self):
self._init()
class RootController(object):
secrets = controllers.secrets.SecretsController(
self.tenant_repo, self.secret_repo,
self.tenant_secret_repo, self.datum_repo, self.kek_repo,
self.secret_meta_repo, self.transport_key_repo
)
return RootController()
def _init(self):
self.tenant_id = 'tenantid1234'
self.keystone_id = 'keystone1234'
self.name = 'name1234'
secret_id = "idsecret1"
datum_id = "iddatum1"
kek_id = "idkek1"
self.secret_algorithm = "AES"
self.secret_bit_length = 256
self.secret_mode = "CBC"
self.kek_tenant = models.KEKDatum()
self.kek_tenant.id = kek_id
self.kek_tenant.active = True
self.kek_tenant.bind_completed = False
self.kek_tenant.kek_label = "kek_label"
self.datum = models.EncryptedDatum()
self.datum.id = datum_id
self.datum.secret_id = secret_id
self.datum.kek_id = kek_id
self.datum.kek_meta_tenant = self.kek_tenant
self.datum.content_type = "text/plain"
self.datum.cypher_text = "aaaa" # base64 value.
self.secret = create_secret(id_ref=secret_id,
name=self.name,
algorithm=self.secret_algorithm,
bit_length=self.secret_bit_length,
mode=self.secret_mode,
encrypted_datum=self.datum)
self.tenant = models.Tenant()
self.tenant.id = self.tenant_id
self.keystone_id = self.keystone_id
self.tenant_repo = mock.MagicMock()
self.tenant_repo.get.return_value = self.tenant
self.tenant_repo.find_by_keystone_id.return_value = self.tenant
self.secret_repo = mock.MagicMock()
self.secret_repo.get.return_value = self.secret
self.secret_repo.delete_entity_by_id.return_value = None
self.tenant_secret_repo = mock.MagicMock()
self.datum_repo = mock.MagicMock()
self.datum_repo.create_from.return_value = None
self.kek_repo = mock.MagicMock()
self.secret_meta_repo = mock.MagicMock()
self.transport_key_model = models.TransportKey(
"default_plugin", "my transport key")
self.transport_key_repo = mock.MagicMock()
self.transport_key_repo.get.return_value = self.transport_key_model
self.transport_key_id = 'tkey12345'
@mock.patch('barbican.plugin.resources.get_transport_key_id_for_retrieval')
def test_should_get_secret_as_json(self, mock_get_transport_key):
mock_get_transport_key.return_value = None
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={'Accept': 'application/json', 'Accept-Encoding': 'gzip'}
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 200)
self.assertNotIn('content_encodings', resp.namespace)
self.assertIn('content_types', resp.namespace)
self.assertIn(self.datum.content_type,
resp.namespace['content_types'].itervalues())
self.assertNotIn('mime_type', resp.namespace)
@mock.patch('barbican.plugin.resources.get_secret')
def test_should_get_secret_as_plain(self, mock_get_secret):
data = 'unencrypted_data'
mock_get_secret.return_value = data
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={'Accept': 'text/plain'}
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 200)
self.assertEqual(resp.body, data)
mock_get_secret.assert_called_once_with(
'text/plain',
self.secret,
self.tenant,
None,
None
)
@mock.patch('barbican.plugin.resources.get_secret')
def test_should_get_secret_as_plain_with_twsk(self, mock_get_secret):
data = 'encrypted_data'
mock_get_secret.return_value = data
twsk = "trans_wrapped_session_key"
resp = self.app.get(
'/secrets/{0}/?trans_wrapped_session_key={1}&transport_key_id={2}'
.format(self.secret.id, twsk, self.transport_key_id),
headers={'Accept': 'text/plain'}
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 200)
self.assertEqual(resp.body, data)
mock_get_secret.assert_called_once_with(
'text/plain',
self.secret,
self.tenant,
twsk,
self.transport_key_model.transport_key
)
@mock.patch('barbican.plugin.resources.get_secret')
def test_should_throw_exception_for_get_when_twsk_but_no_tkey_id(
self, mock_get_secret):
data = 'encrypted_data'
mock_get_secret.return_value = data
twsk = "trans_wrapped_session_key"
resp = self.app.get(
'/secrets/{0}/?trans_wrapped_session_key={1}'.format(
self.secret.id, twsk),
headers={'Accept': 'text/plain'},
expect_errors=True
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 400)
@mock.patch('barbican.plugin.resources.get_transport_key_id_for_retrieval')
def test_should_get_secret_meta_for_binary(self, mock_get_transport_key):
mock_get_transport_key.return_value = None
self.datum.content_type = "application/octet-stream"
self.datum.cypher_text = 'aaaa'
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={'Accept': 'application/json', 'Accept-Encoding': 'gzip'}
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 200)
self.assertIsNotNone(resp.namespace)
self.assertIn('content_types', resp.namespace)
self.assertIn(self.datum.content_type,
resp.namespace['content_types'].itervalues())
@mock.patch('barbican.plugin.resources.get_transport_key_id_for_retrieval')
def test_should_get_secret_meta_for_binary_with_tkey(
self, mock_get_transport_key_id):
mock_get_transport_key_id.return_value = self.transport_key_id
self.datum.content_type = "application/octet-stream"
self.datum.cypher_text = 'aaaa'
resp = self.app.get(
'/secrets/{0}/?transport_key_needed=true'.format(
self.secret.id),
headers={'Accept': 'application/json', 'Accept-Encoding': 'gzip'}
)
self.secret_repo.get.assert_called_once_with(
entity_id=self.secret.id,
keystone_id=self.keystone_id,
suppress_exception=True)
self.assertEqual(resp.status_int, 200)
self.assertIsNotNone(resp.namespace)
self.assertIn('content_types', resp.namespace)
self.assertIn(self.datum.content_type,
resp.namespace['content_types'].itervalues())
self.assertIn('transport_key_ref', resp.namespace)
self.assertEqual(
resp.namespace['transport_key_ref'],
hrefs.convert_transport_key_to_href(
self.transport_key_id)
)
@mock.patch('barbican.plugin.resources.get_secret')
def test_should_get_secret_as_binary(self, mock_get_secret):
data = 'unencrypted_data'
mock_get_secret.return_value = data
self.datum.content_type = "application/octet-stream"
self.datum.cypher_text = 'aaaa'
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={
'Accept': 'application/octet-stream',
'Accept-Encoding': 'gzip'
}
)
self.assertEqual(resp.body, data)
mock_get_secret.assert_called_once_with(
'application/octet-stream',
self.secret,
self.tenant,
None,
None
)
def test_should_throw_exception_for_get_when_secret_not_found(self):
self.secret_repo.get.return_value = None
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={'Accept': 'application/json', 'Accept-Encoding': 'gzip'},
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
def test_should_throw_exception_for_get_when_accept_not_supported(self):
resp = self.app.get(
'/secrets/{0}/'.format(self.secret.id),
headers={'Accept': 'bogusaccept', 'Accept-Encoding': 'gzip'},
expect_errors=True
)
self.assertEqual(resp.status_int, 406)
@mock.patch('barbican.plugin.resources.store_secret')
def test_should_put_secret_as_plain(self, mock_store_secret):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
)
self.assertEqual(resp.status_int, 204)
mock_store_secret.assert_called_once_with(
'plain text',
'text/plain', None,
self.secret.to_dict_fields(),
self.secret,
self.tenant,
mock.ANY,
transport_key_id=None
)
@mock.patch('barbican.plugin.resources.store_secret')
def test_should_put_secret_as_plain_with_tkey_id(self, mock_store_secret):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/?transport_key_id={1}'.format(
self.secret.id, self.transport_key_id),
'plain text',
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
)
self.assertEqual(resp.status_int, 204)
mock_store_secret.assert_called_once_with(
'plain text',
'text/plain', None,
self.secret.to_dict_fields(),
self.secret,
self.tenant,
mock.ANY,
transport_key_id=self.transport_key_id
)
@mock.patch('barbican.plugin.resources.store_secret')
def test_should_put_secret_as_binary(self, mock_store_secret):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream'
},
)
self.assertEqual(resp.status_int, 204)
mock_store_secret.assert_called_once_with(
'plain text',
'application/octet-stream',
None,
self.secret.to_dict_fields(),
self.secret,
self.tenant,
mock.ANY,
transport_key_id=None
)
@mock.patch('barbican.plugin.resources.store_secret')
def test_should_put_secret_as_binary_with_tkey_id(self, mock_store_secret):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/?transport_key_id={1}'.format(
self.secret.id, self.transport_key_id),
'plain text',
headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream'
},
)
self.assertEqual(resp.status_int, 204)
mock_store_secret.assert_called_once_with(
'plain text',
'application/octet-stream',
None,
self.secret.to_dict_fields(),
self.secret,
self.tenant,
mock.ANY,
transport_key_id=self.transport_key_id
)
@mock.patch('barbican.plugin.resources.store_secret')
def test_should_put_encoded_secret_as_binary(self, mock_store_secret):
self.secret.encrypted_data = []
payload = base64.b64encode('plain text')
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
payload,
headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream',
'Content-Encoding': 'base64'
},
)
self.assertEqual(resp.status_int, 204)
mock_store_secret.assert_called_once_with(
payload,
'application/octet-stream',
'base64', self.secret.to_dict_fields(),
self.secret,
self.tenant,
mock.ANY,
transport_key_id=None
)
def test_should_raise_to_put_secret_with_unsupported_encoding(self):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream',
'Content-Encoding': 'bogusencoding'
},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_put_secret_as_json(self):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={
'Accept': 'text/plain',
'Content-Type': 'application/json'
},
expect_errors=True
)
self.assertEqual(resp.status_int, 415)
def test_should_raise_put_secret_not_found(self):
# Force error, due to secret not found.
self.secret_repo.get.return_value = None
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
def test_should_raise_put_secret_no_payload(self):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
# response.body = None
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_put_secret_with_existing_datum(self):
# Force error due to secret already having data
self.secret.encrypted_data = [self.datum]
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'plain text',
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
expect_errors=True
)
self.assertEqual(resp.status_int, 409)
def test_should_raise_due_to_empty_payload(self):
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
'',
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_due_to_plain_text_too_large(self):
big_text = ''.join(['A' for x in xrange(
2 * validators.DEFAULT_MAX_SECRET_BYTES)])
self.secret.encrypted_data = []
resp = self.app.put(
'/secrets/{0}/'.format(self.secret.id),
big_text,
headers={'Accept': 'text/plain', 'Content-Type': 'text/plain'},
expect_errors=True
)
self.assertEqual(resp.status_int, 413)
@mock.patch('barbican.plugin.resources.delete_secret')
def test_should_delete_secret(self, mock_delete_secret):
self.app.delete(
'/secrets/{0}/'.format(self.secret.id)
)
mock_delete_secret.assert_called_once_with(self.secret,
self.keystone_id,
mock.ANY)
def test_should_throw_exception_for_delete_when_secret_not_found(self):
self.secret_repo.get.return_value = None
resp = self.app.delete(
'/secrets/{0}/'.format(self.secret.id),
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
# Error response should have json content type
self.assertEqual(resp.content_type, "application/json")
class WhenCreatingOrdersUsingOrdersResource(FunctionalTest):
def setUp(self):
super(
WhenCreatingOrdersUsingOrdersResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
orders = controllers.orders.OrdersController(self.tenant_repo,
self.order_repo,
self.queue_resource)
return RootController()
def _init(self):
self.secret_name = 'name'
self.secret_payload_content_type = 'application/octet-stream'
self.secret_algorithm = "aes"
self.secret_bit_length = 128
self.secret_mode = "cbc"
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.order_repo = mock.MagicMock()
self.order_repo.create_from.return_value = None
self.queue_resource = mock.MagicMock()
self.queue_resource.process_order.return_value = None
self.order_req = {
'secret': {
'name': self.secret_name,
'payload_content_type': self.secret_payload_content_type,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode
}
}
def test_should_add_new_order(self):
resp = self.app.post_json(
'/orders/',
self.order_req
)
self.assertEqual(resp.status_int, 202)
self.queue_resource.process_order.assert_called_once_with(
order_id=None, keystone_id=self.tenant_keystone_id)
args, kwargs = self.order_repo.create_from.call_args
order = args[0]
self.assertIsInstance(order, models.Order)
def test_should_raise_add_new_order_no_secret(self):
resp = self.app.post_json(
'/orders/',
{},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_add_new_order_bad_json(self):
resp = self.app.post(
'/orders/',
'',
expect_errors=True,
headers={'Content-Type': 'application/json'},
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_add_new_order_unsupported_field(self):
# Using unsupported algorithm field for this test
self.unsupported_req = {
'secret': {
'name': self.secret_name,
'payload_content_type': self.secret_payload_content_type,
'algorithm': "not supported",
'bit_length': self.secret_bit_length,
'mode': self.secret_mode
}
}
resp = self.app.post_json(
'/orders/',
self.unsupported_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_add_new_order_no_content_type_header(self):
resp = self.app.post(
'/orders/',
self.order_req,
expect_errors=True,
)
self.assertEqual(resp.status_int, 415)
class WhenGettingOrdersListUsingOrdersResource(FunctionalTest):
def setUp(self):
super(
WhenGettingOrdersListUsingOrdersResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.keystone_id)
@property
def root(self):
self._init()
class RootController(object):
orders = controllers.orders.OrdersController(self.tenant_repo,
self.order_repo,
self.queue_resource)
return RootController()
def _init(self):
self.tenant_id = 'tenant1234'
self.keystone_id = 'keystoneid1234'
self.name = 'name1234'
self.mime_type = 'text/plain'
self.secret_algorithm = "algo"
self.secret_bit_length = 512
self.secret_mode = "cytype"
self.params = {'offset': 2, 'limit': 2}
self.num_orders = 10
self.offset = 2
self.limit = 2
order_params = {'name': self.name,
'algorithm': self.secret_algorithm,
'bit_length': self.secret_bit_length,
'mode': self.secret_mode}
self.orders = [create_order(id_ref='id' + str(id), **order_params) for
id in xrange(self.num_orders)]
self.total = len(self.orders)
self.order_repo = mock.MagicMock()
self.order_repo.get_by_create_date.return_value = (self.orders,
self.offset,
self.limit,
self.total)
self.tenant_repo = mock.MagicMock()
self.queue_resource = mock.MagicMock()
self.queue_resource.process_order.return_value = None
self.params = {
'offset': self.offset,
'limit': self.limit
}
def test_should_get_list_orders(self):
resp = self.app.get('/orders/', self.params)
self.order_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True
)
self.assertTrue('previous' in resp.namespace)
self.assertTrue('next' in resp.namespace)
url_nav_next = self._create_url(self.keystone_id,
self.offset + self.limit, self.limit)
self.assertTrue(resp.body.count(url_nav_next) == 1)
url_nav_prev = self._create_url(self.keystone_id,
0, self.limit)
self.assertTrue(resp.body.count(url_nav_prev) == 1)
url_hrefs = self._create_url(self.keystone_id)
self.assertTrue(resp.body.count(url_hrefs) ==
(self.num_orders + 2))
def test_response_should_include_total(self):
resp = self.app.get('/orders/', self.params)
self.assertIn('total', resp.namespace)
self.assertEqual(resp.namespace['total'], self.total)
def test_should_handle_no_orders(self):
del self.orders[:]
resp = self.app.get('/orders/', self.params)
self.order_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True
)
self.assertFalse('previous' in resp.namespace)
self.assertFalse('next' in resp.namespace)
def _create_url(self, keystone_id, offset_arg=None, limit_arg=None):
if limit_arg:
offset = int(offset_arg)
limit = int(limit_arg)
return '/orders?limit={0}&offset={1}'.format(limit,
offset)
else:
return '/orders'
class WhenGettingOrDeletingOrderUsingOrderResource(FunctionalTest):
def setUp(self):
super(
WhenGettingOrDeletingOrderUsingOrderResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
orders = controllers.orders.OrdersController(self.tenant_repo,
self.order_repo,
self.queue_resource)
return RootController()
def _init(self):
self.tenant_keystone_id = 'keystoneid1234'
self.requestor = 'requestor1234'
self.order = create_order(id_ref="id1", name="name")
self.order_repo = mock.MagicMock()
self.order_repo.get.return_value = self.order
self.order_repo.save.return_value = None
self.order_repo.delete_entity_by_id.return_value = None
self.tenant_repo = mock.MagicMock()
self.queue_resource = mock.MagicMock()
def test_should_get_order(self):
self.app.get('/orders/{0}/'.format(self.order.id))
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
def test_should_delete_order(self):
self.app.delete('/orders/{0}/'.format(self.order.id))
self.order_repo.delete_entity_by_id.assert_called_once_with(
entity_id=self.order.id, keystone_id=self.tenant_keystone_id)
def test_should_throw_exception_for_get_when_order_not_found(self):
self.order_repo.get.return_value = None
resp = self.app.get(
'/orders/{0}/'.format(self.order.id),
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
def test_should_throw_exception_for_delete_when_order_not_found(self):
self.order_repo.delete_entity_by_id.side_effect = excep.NotFound(
"Test not found exception")
resp = self.app.delete(
'/orders/{0}/'.format(self.order.id),
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
# Error response should have json content type
self.assertEqual(resp.content_type, "application/json")
class WhenPuttingOrderWithMetadataUsingOrderResource(FunctionalTest):
def setUp(self):
super(
WhenPuttingOrderWithMetadataUsingOrderResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
orders = controllers.orders.OrdersController(self.tenant_repo,
self.order_repo,
self.queue_resource)
return RootController()
def _init(self):
self.tenant_keystone_id = 'keystoneid1234'
self.requestor = 'requestor1234'
self.order = create_order_with_meta(
id_ref='id1',
order_type='certificate',
meta={'email': 'email@email.com'},
status="PENDING"
)
self.order_repo = mock.MagicMock()
self.order_repo.get.return_value = self.order
self.order_repo.save.return_value = None
self.order_repo.delete_entity_by_id.return_value = None
self.type = 'certificate'
self.meta = {'email': 'newemail@email.com'}
self.params = {'type': self.type, 'meta': self.meta}
self.tenant_repo = mock.MagicMock()
self.queue_resource = mock.MagicMock()
def test_should_put_order(self):
resp = self.app.put_json(
'/orders/{0}/'.format(self.order.id),
self.params,
headers={
'Content-Type': 'application/json'
}
)
self.assertEqual(resp.status_int, 204)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
def test_should_fail_bad_type(self):
self.order['type'] = 'secret'
resp = self.app.put_json(
'/orders/{0}/'.format(self.order.id),
self.params,
headers={
'Content-Type': 'application/json'
},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
def test_should_fail_bad_status(self):
self.order['status'] = 'DONE'
resp = self.app.put_json(
'/orders/{0}/'.format(self.order.id),
self.params,
headers={
'Content-Type': 'application/json'
},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
class WhenCreatingTypeOrdersUsingOrdersResource(FunctionalTest):
def setUp(self):
super(
WhenCreatingTypeOrdersUsingOrdersResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
@property
def root(self):
self._init()
class RootController(object):
orders = controllers.orders.OrdersController(self.tenant_repo,
self.order_repo,
self.queue_resource)
return RootController()
def _init(self):
self.type = 'key'
self.meta = {'name': 'secretname',
'algorithm': 'AES',
'bit_length': 256,
'mode': 'cbc',
'payload_content_type':
'application/octet-stream'}
self.order_req = {'type': self.type,
'meta': self.meta}
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.order_repo = mock.MagicMock()
self.order_repo.create_from.return_value = None
self.queue_resource = mock.MagicMock()
self.queue_resource.process_type_order.return_value = None
def test_should_add_new_order(self):
self.skipTest("atiwari: remove skip once CR 111412 merged")
resp = self.app.post_json(
'/orders/', self.order_req
)
self.assertEqual(resp.status_int, 202)
self.queue_resource.process_type_order.assert_called_once_with(
order_id=None, keystone_id=self.tenant_keystone_id)
args, kwargs = self.order_repo.create_from.call_args
order = args[0]
self.assertIsInstance(order, models.Order)
def test_should_fail_add_new_order_no_secret_json(self):
resp = self.app.post_json(
'/orders/', {},
expect_errors=True
)
self.assertEqual(resp.status_int, 400)
def test_should_fail_add_new_order_bad_json(self):
resp = self.app.post(
'/orders/', '',
expect_errors=True
)
self.assertEqual(resp.status_int, 415)
class WhenAddingNavigationHrefs(utils.BaseTestCase):
def setUp(self):
super(WhenAddingNavigationHrefs, self).setUp()
self.resource_name = 'orders'
self.keystone_id = '12345'
self.num_elements = 100
self.data = dict()
def test_add_nav_hrefs_adds_next_only(self):
offset = 0
limit = 10
data_with_hrefs = hrefs.add_nav_hrefs(
self.resource_name, offset, limit, self.num_elements, self.data)
self.assertNotIn('previous', data_with_hrefs)
self.assertIn('next', data_with_hrefs)
def test_add_nav_hrefs_adds_both_next_and_previous(self):
offset = 10
limit = 10
data_with_hrefs = hrefs.add_nav_hrefs(
self.resource_name, offset, limit, self.num_elements, self.data)
self.assertIn('previous', data_with_hrefs)
self.assertIn('next', data_with_hrefs)
def test_add_nav_hrefs_adds_previous_only(self):
offset = 90
limit = 10
data_with_hrefs = hrefs.add_nav_hrefs(
self.resource_name, offset, limit, self.num_elements, self.data)
self.assertIn('previous', data_with_hrefs)
self.assertNotIn('next', data_with_hrefs)
class TestingJsonSanitization(utils.BaseTestCase):
def test_json_sanitization_without_array(self):
json_without_array = {"name": "name", "algorithm": "AES",
"payload_content_type": " text/plain ",
"mode": "CBC", "bit_length": 256,
"payload": "not-encrypted"}
self.assertTrue(json_without_array['payload_content_type']
.startswith(' '), "whitespace should be there")
self.assertTrue(json_without_array['payload_content_type']
.endswith(' '), "whitespace should be there")
api.strip_whitespace(json_without_array)
self.assertFalse(json_without_array['payload_content_type']
.startswith(' '), "whitespace should be gone")
self.assertFalse(json_without_array['payload_content_type']
.endswith(' '), "whitespace should be gone")
def test_json_sanitization_with_array(self):
json_with_array = {"name": "name", "algorithm": "AES",
"payload_content_type": "text/plain",
"mode": "CBC", "bit_length": 256,
"payload": "not-encrypted",
"an-array":
[{"name": " item 1"},
{"name": "item2 "}]}
self.assertTrue(json_with_array['an-array'][0]['name']
.startswith(' '), "whitespace should be there")
self.assertTrue(json_with_array['an-array'][1]['name']
.endswith(' '), "whitespace should be there")
api.strip_whitespace(json_with_array)
self.assertFalse(json_with_array['an-array'][0]['name']
.startswith(' '), "whitespace should be gone")
self.assertFalse(json_with_array['an-array'][1]['name']
.endswith(' '), "whitespace should be gone")
class WhenCreatingContainersUsingContainersResource(FunctionalTest):
def setUp(self):
super(
WhenCreatingContainersUsingContainersResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
containers = controllers.containers.ContainersController(
self.tenant_repo, self.container_repo, self.secret_repo,
self.consumer_repo
)
return RootController()
def _init(self):
self.name = 'test container name'
self.type = 'generic'
self.secret_refs = [
{
'name': 'test secret 1',
'secret_ref': '1231'
},
{
'name': 'test secret 2',
'secret_ref': '1232'
},
{
'name': 'test secret 3',
'secret_ref': '1233'
}
]
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.container_repo = mock.MagicMock()
self.container_repo.create_from.return_value = None
self.secret_repo = mock.MagicMock()
self.secret_repo.create_from.return_value = None
self.consumer_repo = mock.MagicMock()
self.consumer_repo.create_from.return_value = None
self.container_req = {'name': self.name,
'type': self.type,
'secret_refs': self.secret_refs}
def test_should_add_new_container(self):
resp = self.app.post_json(
'/containers/',
self.container_req
)
self.assertEqual(resp.status_int, 201)
self.assertNotIn(self.tenant_keystone_id, resp.headers['Location'])
args, kwargs = self.container_repo.create_from.call_args
container = args[0]
self.assertIsInstance(container, models.Container)
def test_should_raise_container_bad_json(self):
resp = self.app.post(
'/containers/',
'',
expect_errors=True,
headers={'Content-Type': 'application/json'},
)
self.assertEqual(resp.status_int, 400)
def test_should_raise_container_no_content_type_header(self):
resp = self.app.post(
'/containers/',
self.container_req,
expect_errors=True,
)
self.assertEqual(resp.status_int, 415)
def test_should_throw_exception_when_secret_ref_doesnt_exist(self):
self.secret_repo.get.return_value = None
resp = self.app.post_json(
'/containers/',
self.container_req,
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
class WhenGettingOrDeletingContainerUsingContainerResource(FunctionalTest):
def setUp(self):
super(
WhenGettingOrDeletingContainerUsingContainerResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
containers = controllers.containers.ContainersController(
self.tenant_repo, self.container_repo, self.secret_repo,
self.consumer_repo
)
return RootController()
def _init(self):
self.tenant_keystone_id = 'keystoneid1234'
self.tenant_internal_id = 'tenantid1234'
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.container = create_container(id_ref='id1')
self.container_repo = mock.MagicMock()
self.container_repo.get.return_value = self.container
self.container_repo.delete_entity_by_id.return_value = None
self.secret_repo = mock.MagicMock()
self.consumer_repo = mock.MagicMock()
def test_should_get_container(self):
self.app.get('/containers/{0}/'.format(
self.container.id
))
self.container_repo.get.assert_called_once_with(
entity_id=self.container.id,
keystone_id=self.tenant_keystone_id,
suppress_exception=True)
def test_should_delete_container(self):
self.app.delete('/containers/{0}/'.format(
self.container.id
))
self.container_repo.delete_entity_by_id.assert_called_once_with(
entity_id=self.container.id, keystone_id=self.tenant_keystone_id)
def test_should_throw_exception_for_get_when_container_not_found(self):
self.container_repo.get.return_value = None
resp = self.app.get('/containers/{0}/'.format(
self.container.id
), expect_errors=True)
self.assertEqual(resp.status_int, 404)
def test_should_throw_exception_for_delete_when_container_not_found(self):
self.container_repo.delete_entity_by_id.side_effect = excep.NotFound(
"Test not found exception")
resp = self.app.delete('/containers/{0}/'.format(
self.container.id
), expect_errors=True)
self.assertEqual(resp.status_int, 404)
# Error response should have json content type
self.assertEqual(resp.content_type, "application/json")
class WhenCreatingConsumersUsingConsumersResource(FunctionalTest):
def setUp(self):
super(
WhenCreatingConsumersUsingConsumersResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
containers = controllers.containers.ContainersController(
self.tenant_repo, self.container_repo, self.secret_repo,
self.consumer_repo
)
return RootController()
def _init(self):
self.name = 'test container name'
self.type = 'generic'
self.secret_refs = [
{
'name': 'test secret 1',
'secret_ref': '1231'
},
{
'name': 'test secret 2',
'secret_ref': '1232'
},
{
'name': 'test secret 3',
'secret_ref': '1233'
}
]
self.consumer_ref = {
'name': 'test_consumer1',
'URL': 'http://consumer/1'
}
self.tenant_internal_id = 'tenantid1234'
self.tenant_keystone_id = 'keystoneid1234'
self.container = create_container(id_ref='id1')
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.container_repo = mock.MagicMock()
self.container_repo.get.return_value = self.container
self.secret_repo = mock.MagicMock()
self.secret_repo.create_from.return_value = None
self.consumer_repo = mock.MagicMock()
self.consumer_repo.create_from.return_value = None
self.container_req = {'name': self.name,
'type': self.type,
'secret_refs': self.secret_refs}
def test_should_add_new_consumer(self):
resp = self.app.post_json(
'/containers/{0}/consumers/'.format(self.container.id),
self.consumer_ref
)
self.assertEqual(resp.status_int, 200)
self.assertNotIn(self.tenant_keystone_id, resp.headers['Location'])
args, kwargs = self.consumer_repo.create_from.call_args
consumer = args[0]
self.assertIsInstance(consumer, models.ContainerConsumerMetadatum)
def test_should_fail_consumer_bad_json(self):
resp = self.app.post(
'/containers/{0}/consumers/'.format(self.container.id),
'',
expect_errors=True
)
self.assertEqual(resp.status_int, 415)
def test_should_404_consumer_bad_container_id(self):
self.container_repo.get.side_effect = excep.NotFound()
resp = self.app.post_json(
'/containers/{0}/consumers/'.format('bad_id'),
self.consumer_ref, expect_errors=True
)
self.container_repo.get.side_effect = None
self.assertEqual(resp.status_int, 404)
def test_should_raise_exception_when_container_ref_doesnt_exist(self):
self.container_repo.get.return_value = None
resp = self.app.post_json(
'/containers/{0}/consumers/'.format(self.container.id),
self.consumer_ref,
expect_errors=True
)
self.assertEqual(resp.status_int, 404)
class WhenGettingOrDeletingConsumersUsingConsumerResource(FunctionalTest):
def setUp(self):
super(
WhenGettingOrDeletingConsumersUsingConsumerResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.tenant_keystone_id)
@property
def root(self):
self._init()
class RootController(object):
containers = controllers.containers.ContainersController(
self.tenant_repo, self.container_repo, self.secret_repo,
self.consumer_repo
)
return RootController()
def _init(self):
self.tenant_keystone_id = 'keystoneid1234'
self.tenant_internal_id = 'tenantid1234'
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.consumer_repo = mock.MagicMock()
self.container = create_container(id_ref='id1')
self.consumer = create_consumer(self.container.id, id_ref='id2')
self.consumer_ref = {
'name': self.consumer.name,
'URL': self.consumer.URL
}
self.container_repo = mock.MagicMock()
self.container_repo.get.return_value = self.container
self.consumer_repo.get_by_values.return_value = self.consumer
self.consumer_repo.delete_entity_by_id.return_value = None
self.secret_repo = mock.MagicMock()
def test_should_get_consumer(self):
ret_val = ([self.consumer], 0, 0, 1)
self.consumer_repo.get_by_container_id.return_value = ret_val
resp = self.app.get('/containers/{0}/consumers/'.format(
self.container.id
))
self.assertEqual(resp.status_int, 200)
self.consumer_repo.get_by_container_id.assert_called_once_with(
self.container.id,
limit_arg=None,
offset_arg=0,
suppress_exception=True
)
self.assertEqual(self.consumer.name, resp.json['consumers'][0]['name'])
self.assertEqual(self.consumer.URL, resp.json['consumers'][0]['URL'])
def test_should_404_with_bad_container_id(self):
self.container_repo.get.side_effect = excep.NotFound()
resp = self.app.get('/containers/{0}/consumers/'.format(
'bad_id'
), expect_errors=True)
self.container_repo.get.side_effect = None
self.assertEqual(resp.status_int, 404)
def test_should_get_consumer_by_id(self):
self.consumer_repo.get.return_value = self.consumer
resp = self.app.get('/containers/{0}/consumers/{1}/'.format(
self.container.id, self.consumer.id
))
self.assertEqual(resp.status_int, 200)
def test_should_404_with_bad_consumer_id(self):
self.consumer_repo.get.return_value = None
resp = self.app.get('/containers/{0}/consumers/{1}/'.format(
self.container.id, 'bad_id'
), expect_errors=True)
self.assertEqual(resp.status_int, 404)
def test_should_get_no_consumers(self):
self.consumer_repo.get_by_container_id.return_value = ([], 0, 0, 0)
resp = self.app.get('/containers/{0}/consumers/'.format(
self.container.id
))
self.assertEqual(resp.status_int, 200)
def test_should_delete_consumer(self):
self.app.delete_json('/containers/{0}/consumers/'.format(
self.container.id
), self.consumer_ref)
self.consumer_repo.delete_entity_by_id.assert_called_once_with(
self.consumer.id, self.tenant_keystone_id)
def test_should_fail_deleting_consumer_bad_json(self):
resp = self.app.delete(
'/containers/{0}/consumers/'.format(self.container.id),
'',
expect_errors=True
)
self.assertEqual(resp.status_int, 415)
def test_should_404_on_delete_when_consumer_not_found(self):
old_return = self.consumer_repo.get_by_values.return_value
self.consumer_repo.get_by_values.return_value = None
resp = self.app.delete_json('/containers/{0}/consumers/'.format(
self.container.id
), self.consumer_ref, expect_errors=True)
self.consumer_repo.get_by_values.return_value = old_return
self.assertEqual(resp.status_int, 404)
# Error response should have json content type
self.assertEqual(resp.content_type, "application/json")
def test_should_404_on_delete_when_consumer_not_found_later(self):
self.consumer_repo.delete_entity_by_id.side_effect = excep.NotFound()
resp = self.app.delete_json('/containers/{0}/consumers/'.format(
self.container.id
), self.consumer_ref, expect_errors=True)
self.consumer_repo.delete_entity_by_id.side_effect = None
self.assertEqual(resp.status_int, 404)
# Error response should have json content type
self.assertEqual(resp.content_type, "application/json")
class WhenGettingContainersListUsingResource(FunctionalTest):
def setUp(self):
super(
WhenGettingContainersListUsingResource, self
).setUp()
self.app = webtest.TestApp(app.PecanAPI(self.root))
self.app.extra_environ = get_barbican_env(self.keystone_id)
@property
def root(self):
self._init()
class RootController(object):
containers = controllers.containers.ContainersController(
self.tenant_repo, self.container_repo, self.secret_repo,
self.consumer_repo
)
return RootController()
def _init(self):
self.tenant_id = 'tenant1234'
self.keystone_id = 'keystoneid1234'
self.num_containers = 10
self.offset = 2
self.limit = 2
self.containers = [create_container(id_ref='id' + str(id_ref)) for
id_ref in xrange(self.num_containers)]
self.total = len(self.containers)
self.container_repo = mock.MagicMock()
self.container_repo.get_by_create_date.return_value = (self.containers,
self.offset,
self.limit,
self.total)
self.tenant_repo = mock.MagicMock()
self.secret_repo = mock.MagicMock()
self.consumer_repo = mock.MagicMock()
self.params = {
'offset': self.offset,
'limit': self.limit,
}
def test_should_get_list_containers(self):
resp = self.app.get(
'/containers/',
self.params
)
self.container_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True
)
self.assertTrue('previous' in resp.namespace)
self.assertTrue('next' in resp.namespace)
url_nav_next = self._create_url(self.keystone_id,
self.offset + self.limit, self.limit)
self.assertTrue(resp.body.count(url_nav_next) == 1)
url_nav_prev = self._create_url(self.keystone_id,
0, self.limit)
self.assertTrue(resp.body.count(url_nav_prev) == 1)
url_hrefs = self._create_url(self.keystone_id)
self.assertTrue(resp.body.count(url_hrefs) ==
(self.num_containers + 2))
def test_response_should_include_total(self):
resp = self.app.get(
'/containers/',
self.params
)
self.assertIn('total', resp.namespace)
self.assertEqual(resp.namespace['total'], self.total)
def test_should_handle_no_containers(self):
del self.containers[:]
resp = self.app.get(
'/containers/',
self.params
)
self.container_repo.get_by_create_date.assert_called_once_with(
self.keystone_id,
offset_arg=u'{0}'.format(self.offset),
limit_arg=u'{0}'.format(self.limit),
suppress_exception=True
)
self.assertFalse('previous' in resp.namespace)
self.assertFalse('next' in resp.namespace)
def _create_url(self, keystone_id, offset_arg=None, limit_arg=None):
if limit_arg:
offset = int(offset_arg)
limit = int(limit_arg)
return '/containers?limit={0}&offset={1}'.format(limit, offset)
else:
return '/containers'