
Now that we no longer support py27, we can use the standard library unittest.mock module instead of the third party mock lib. Change-Id: I2bb5204c1ee88d0b0cd6df830dac80d37bfddcf2 Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
1693 lines
61 KiB
Python
1693 lines
61 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 mimetypes
|
|
from unittest import mock
|
|
|
|
import pecan
|
|
from testtools import testcase
|
|
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 utils as barbican_utils
|
|
import barbican.context
|
|
from barbican.model import models
|
|
from barbican.tests import utils
|
|
|
|
|
|
def get_barbican_env(external_project_id):
|
|
"""Create and return a barbican.context for use with the RBAC decorator
|
|
|
|
Injects the provided external_project_id.
|
|
"""
|
|
kwargs = {'roles': None,
|
|
'user_id': None,
|
|
'project_id': external_project_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, content_type=None, project_id=None):
|
|
"""Generate a Secret entity instance."""
|
|
info = {
|
|
'id': id_ref,
|
|
'name': name,
|
|
'algorithm': algorithm,
|
|
'bit_length': bit_length,
|
|
'mode': mode,
|
|
'project_id': project_id,
|
|
}
|
|
secret = models.Secret(info)
|
|
secret.id = id_ref
|
|
if encrypted_datum:
|
|
secret.encrypted_data = [encrypted_datum]
|
|
if content_type:
|
|
content_meta = models.SecretStoreMetadatum('content_type',
|
|
content_type)
|
|
secret.secret_store_metadata['content_type'] = content_meta
|
|
return secret
|
|
|
|
|
|
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_project)
|
|
test.assertTrue(datum.kek_meta_project.bind_completed)
|
|
test.assertIsNotNone(datum.kek_meta_project.plugin_name)
|
|
test.assertIsNotNone(datum.kek_meta_project.kek_label)
|
|
|
|
|
|
def create_container(id_ref, project_id=None, external_project_id=None):
|
|
"""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_ref
|
|
container_secret.secret_id = '123'
|
|
container.container_secrets.append(container_secret)
|
|
|
|
if project_id:
|
|
project = models.Project()
|
|
project.id = project_id
|
|
project.external_id = external_project_id
|
|
container.project = project
|
|
return container
|
|
|
|
|
|
def create_container_consumer(container_id, project_id, id_ref):
|
|
"""Generate a ContainerConsumerMetadatum entity instance."""
|
|
data = {
|
|
'name': 'test name',
|
|
'URL': 'http://test/url'
|
|
}
|
|
consumer = models.ContainerConsumerMetadatum(container_id,
|
|
project_id,
|
|
data)
|
|
consumer.id = id_ref
|
|
return consumer
|
|
|
|
|
|
def create_secret_consumer(secret_id, project_id, id_ref):
|
|
"""Generate a SecretConsumerMetadatum entity instance."""
|
|
consumer = models.SecretConsumerMetadatum(
|
|
secret_id,
|
|
project_id,
|
|
"service",
|
|
"resource_type",
|
|
"resource_id",
|
|
)
|
|
consumer.id = id_ref
|
|
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)
|
|
@barbican_utils.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 = barbican_utils.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,
|
|
barbican_utils.allow_all_content_types,
|
|
self._empty_function)
|
|
|
|
|
|
class FunctionalTest(utils.BaseTestCase, utils.MockModelRepositoryMixin,
|
|
testcase.WithAttributes):
|
|
|
|
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 BaseSecretsResource(FunctionalTest):
|
|
"""Base test class for the Secrets resource."""
|
|
|
|
def setUp(self):
|
|
super(BaseSecretsResource, self).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
|
|
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,
|
|
'creator_id': None,
|
|
'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)
|
|
|
|
# Set up mocked project
|
|
self.external_project_id = 'keystone1234'
|
|
self.project_entity_id = 'tid1234'
|
|
self.project = models.Project()
|
|
self.project.id = self.project_entity_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.find_by_external_project_id.return_value = (
|
|
self.project)
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked secret
|
|
self.secret = models.Secret()
|
|
self.secret.id = utils.generate_test_valid_uuid()
|
|
|
|
# Set up mocked secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.create_from.return_value = self.secret
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up mocked encrypted datum repo
|
|
self.datum_repo = mock.MagicMock()
|
|
self.datum_repo.create_from.return_value = None
|
|
self.setup_encrypted_datum_repository_mock(self.datum_repo)
|
|
|
|
# Set up mocked kek datum
|
|
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 = ''
|
|
|
|
# Set up mocked kek datum repo
|
|
self.kek_repo = mock.MagicMock()
|
|
self.kek_repo.find_or_create_kek_datum.return_value = self.kek_datum
|
|
self.setup_kek_datum_repository_mock(self.kek_repo)
|
|
|
|
# Set up mocked secret meta repo
|
|
self.setup_secret_meta_repository_mock()
|
|
|
|
# Set up mocked transport key
|
|
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)
|
|
|
|
# Set up mocked transport key
|
|
self.setup_transport_key_repository_mock()
|
|
|
|
|
|
class WhenGettingPuttingOrDeletingSecretUsingSecretResource(FunctionalTest):
|
|
def setUp(self):
|
|
super(
|
|
WhenGettingPuttingOrDeletingSecretUsingSecretResource, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.project_id = 'projectid1234'
|
|
self.external_project_id = 'keystone1234'
|
|
self.name = 'name1234'
|
|
|
|
secret_id = utils.generate_test_valid_uuid()
|
|
datum_id = "iddatum1"
|
|
kek_id = "idkek1"
|
|
|
|
self.secret_algorithm = "AES"
|
|
self.secret_bit_length = 256
|
|
self.secret_mode = "CBC"
|
|
|
|
self.kek_project = models.KEKDatum()
|
|
self.kek_project.id = kek_id
|
|
self.kek_project.active = True
|
|
self.kek_project.bind_completed = False
|
|
self.kek_project.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_project = self.kek_project
|
|
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,
|
|
content_type=self.datum.content_type)
|
|
|
|
self.secret.secret_acls = []
|
|
self.secret.project = mock.MagicMock()
|
|
self.secret.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.project_repo.find_by_external_project_id.return_value = (
|
|
self.project)
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked secret repo
|
|
self.secret_repo = mock.Mock()
|
|
self.secret_repo.get = mock.Mock(return_value=self.secret)
|
|
self.secret_repo.get_secret_by_id = mock.Mock(return_value=self.secret)
|
|
self.secret_repo.delete_entity_by_id = mock.Mock(return_value=None)
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up mocked encrypted datum repo
|
|
self.datum_repo = mock.MagicMock()
|
|
self.datum_repo.create_from.return_value = None
|
|
self.setup_encrypted_datum_repository_mock(self.datum_repo)
|
|
|
|
# Set up mocked kek datum repo
|
|
self.setup_kek_datum_repository_mock()
|
|
|
|
# Set up mocked secret meta repo
|
|
self.secret_meta_repo = mock.MagicMock()
|
|
self.secret_meta_repo.get_metadata_for_secret.return_value = None
|
|
self.setup_secret_meta_repository_mock(self.secret_meta_repo)
|
|
|
|
# Set up mocked transport key
|
|
self.transport_key_model = models.TransportKey(
|
|
"default_plugin", "my transport key")
|
|
|
|
# Set up mocked transport key repo
|
|
self.transport_key_repo = mock.MagicMock()
|
|
self.transport_key_repo.get.return_value = self.transport_key_model
|
|
self.setup_transport_key_repository_mock(self.transport_key_repo)
|
|
|
|
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_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertNotIn('content_encodings', resp.namespace)
|
|
self.assertIn('content_types', resp.namespace)
|
|
self.assertIn(self.datum.content_type,
|
|
resp.namespace['content_types'].values())
|
|
self.assertNotIn('mime_type', resp.namespace)
|
|
|
|
@testcase.attr('deprecated')
|
|
@mock.patch('barbican.plugin.resources.get_secret')
|
|
def test_should_get_secret_as_plain_based_on_content_type(self,
|
|
mock_get_secret):
|
|
data = 'unencrypted_data'
|
|
mock_get_secret.return_value = data
|
|
|
|
resp = self.app.get(
|
|
'/secrets/{0}/payload/'.format(self.secret.id),
|
|
headers={'Accept': 'text/plain'}
|
|
)
|
|
|
|
self.secret_repo.get_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertEqual(data, resp.body.decode())
|
|
mock_get_secret.assert_called_once_with(
|
|
'text/plain',
|
|
self.secret,
|
|
self.project,
|
|
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}/payload/'
|
|
'?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_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertEqual(data, resp.body.decode())
|
|
mock_get_secret.assert_called_once_with(
|
|
'text/plain',
|
|
self.secret,
|
|
self.project,
|
|
twsk,
|
|
self.transport_key_model.transport_key
|
|
)
|
|
|
|
@testcase.attr('deprecated')
|
|
@mock.patch('barbican.plugin.resources.get_secret')
|
|
def test_should_get_secret_as_plain_with_twsk_based_on_content_type(
|
|
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_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertEqual(data, resp.body.decode())
|
|
mock_get_secret.assert_called_once_with(
|
|
'text/plain',
|
|
self.secret,
|
|
self.project,
|
|
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}/payload/?trans_wrapped_session_key={1}'.format(
|
|
self.secret.id, twsk),
|
|
headers={'Accept': 'text/plain'},
|
|
expect_errors=True
|
|
)
|
|
|
|
self.secret_repo.get_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(400, resp.status_int)
|
|
|
|
@testcase.attr('deprecated')
|
|
@mock.patch('barbican.plugin.resources.get_secret')
|
|
def test_should_throw_exception_for_get_when_twsk_but_no_tkey_id_old_way(
|
|
self, mock_get_secret):
|
|
data = 'encrypted_data'
|
|
mock_get_secret.return_value = data
|
|
|
|
twsk = "trans_wrapped_session_key"
|
|
resp = self.app.get(
|
|
'/secrets/{0}/payload/?trans_wrapped_session_key={1}'.format(
|
|
self.secret.id, twsk),
|
|
headers={'Accept': 'text/plain'},
|
|
expect_errors=True
|
|
)
|
|
|
|
self.secret_repo.get_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
self.assertEqual(400, resp.status_int)
|
|
|
|
@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.secret.secret_store_metadata['content_type'].value = (
|
|
self.datum.content_type
|
|
)
|
|
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_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertIsNotNone(resp.namespace)
|
|
self.assertIn('content_types', resp.namespace)
|
|
self.assertIn(self.datum.content_type,
|
|
resp.namespace['content_types'].values())
|
|
|
|
@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.secret.secret_store_metadata['content_type'].value = (
|
|
self.datum.content_type
|
|
)
|
|
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_secret_by_id.assert_called_once_with(
|
|
entity_id=self.secret.id,
|
|
suppress_exception=True)
|
|
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.assertIsNotNone(resp.namespace)
|
|
self.assertIn('content_types', resp.namespace)
|
|
self.assertIn(self.datum.content_type,
|
|
resp.namespace['content_types'].values())
|
|
self.assertIn('transport_key_ref', resp.namespace)
|
|
self.assertEqual(
|
|
hrefs.convert_transport_key_to_href(self.transport_key_id),
|
|
resp.namespace['transport_key_ref']
|
|
)
|
|
|
|
@testcase.attr('deprecated')
|
|
@mock.patch('barbican.plugin.resources.get_secret')
|
|
def test_should_get_secret_as_binary_based_on_content_type(
|
|
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(data, resp.body.decode())
|
|
|
|
mock_get_secret.assert_called_once_with(
|
|
'application/octet-stream',
|
|
self.secret,
|
|
self.project,
|
|
None,
|
|
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 = []
|
|
self.secret.secret_store_metadata = {}
|
|
|
|
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(204, resp.status_int)
|
|
|
|
mock_store_secret.assert_called_once_with(
|
|
unencrypted_raw=b'plain text',
|
|
content_type_raw='text/plain',
|
|
content_encoding=None,
|
|
secret_model=self.secret,
|
|
project_model=self.project,
|
|
transport_key_id=self.transport_key_id
|
|
)
|
|
|
|
@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 = []
|
|
self.secret.secret_store_metadata = {}
|
|
|
|
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(204, resp.status_int)
|
|
|
|
mock_store_secret.assert_called_once_with(
|
|
unencrypted_raw=b'plain text',
|
|
content_type_raw='application/octet-stream',
|
|
content_encoding=None,
|
|
secret_model=self.secret,
|
|
project_model=self.project,
|
|
transport_key_id=self.transport_key_id
|
|
)
|
|
|
|
|
|
class WhenAddingNavigationHrefs(utils.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(WhenAddingNavigationHrefs, self).setUp()
|
|
|
|
self.resource_name = 'orders'
|
|
self.external_project_id = '12345'
|
|
self.num_elements = 100
|
|
self.data = {}
|
|
|
|
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 WhenCreatingContainerConsumersUsingResource(FunctionalTest):
|
|
def setUp(self):
|
|
super(
|
|
WhenCreatingContainerConsumersUsingResource, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
containers = controllers.containers.ContainersController()
|
|
|
|
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.project_internal_id = 'projectid1234'
|
|
self.external_project_id = 'keystoneid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked quota enforcer
|
|
self.quota_patch = mock.patch(
|
|
'barbican.common.quota.QuotaEnforcer.enforce', return_value=None)
|
|
self.quota_patch.start()
|
|
self.addCleanup(self.quota_patch.stop)
|
|
|
|
# Set up mocked container
|
|
self.container = create_container(
|
|
id_ref=utils.generate_test_valid_uuid(),
|
|
project_id=self.project_internal_id,
|
|
external_project_id=self.external_project_id)
|
|
|
|
# Set up mocked container repo
|
|
self.container_repo = mock.MagicMock()
|
|
self.container_repo.get_container_by_id.return_value = self.container
|
|
self.setup_container_repository_mock(self.container_repo)
|
|
|
|
# Set up secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.create_from.return_value = None
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up container consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.create_from.return_value = None
|
|
self.setup_container_consumer_repository_mock(self.consumer_repo)
|
|
|
|
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(200, resp.status_int)
|
|
self.assertNotIn(self.external_project_id, resp.headers['Location'])
|
|
|
|
args, kwargs = self.consumer_repo.create_or_update_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(415, resp.status_int)
|
|
|
|
def test_should_404_when_container_ref_doesnt_exist(self):
|
|
self.container_repo.get_container_by_id.return_value = None
|
|
resp = self.app.post_json(
|
|
'/containers/{0}/consumers/'.format('bad_id'),
|
|
self.consumer_ref, expect_errors=True
|
|
)
|
|
self.assertEqual(404, resp.status_int)
|
|
|
|
|
|
class WhenGettingOrDeletingContainerConsumersUsingResource(FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(
|
|
WhenGettingOrDeletingContainerConsumersUsingResource, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
containers = controllers.containers.ContainersController()
|
|
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked container
|
|
self.container = create_container(
|
|
id_ref=utils.generate_test_valid_uuid(),
|
|
project_id=self.project_internal_id,
|
|
external_project_id=self.external_project_id)
|
|
|
|
# Set up mocked consumers
|
|
self.consumer = create_container_consumer(
|
|
self.container.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
self.consumer2 = create_container_consumer(
|
|
self.container.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
|
|
self.consumer_ref = {
|
|
'name': self.consumer.name,
|
|
'URL': self.consumer.URL
|
|
}
|
|
|
|
# Set up mocked container repo
|
|
self.container_repo = mock.MagicMock()
|
|
self.container_repo.get_container_by_id.return_value = self.container
|
|
self.setup_container_repository_mock(self.container_repo)
|
|
|
|
# Set up mocked container consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_container_consumer_repository_mock(self.consumer_repo)
|
|
|
|
# Set up mocked secret repo
|
|
self.setup_secret_repository_mock()
|
|
|
|
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(200, resp.status_int)
|
|
|
|
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_when_container_ref_doesnt_exist(self):
|
|
self.container_repo.get_container_by_id.return_value = None
|
|
resp = self.app.get('/containers/{0}/consumers/'.format(
|
|
'bad_id'
|
|
), expect_errors=True)
|
|
self.assertEqual(404, resp.status_int)
|
|
|
|
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(200, resp.status_int)
|
|
|
|
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(404, resp.status_int)
|
|
|
|
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(200, resp.status_int)
|
|
|
|
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.external_project_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(415, resp.status_int)
|
|
|
|
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(404, resp.status_int)
|
|
# Error response should have json content type
|
|
self.assertEqual("application/json", resp.content_type)
|
|
|
|
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(404, resp.status_int)
|
|
# Error response should have json content type
|
|
self.assertEqual("application/json", resp.content_type)
|
|
|
|
def test_should_delete_consumers_on_container_delete(self):
|
|
consumers = [self.consumer, self.consumer2]
|
|
ret_val = (consumers, 0, 0, 1)
|
|
self.consumer_repo.get_by_container_id.return_value = ret_val
|
|
|
|
resp = self.app.delete(
|
|
'/containers/{0}/'.format(self.container.id)
|
|
)
|
|
self.assertEqual(204, resp.status_int)
|
|
|
|
# Verify consumers were deleted
|
|
calls = []
|
|
for consumer in consumers:
|
|
calls.append(mock.call(consumer.id, self.external_project_id))
|
|
self.consumer_repo.delete_entity_by_id.assert_has_calls(
|
|
calls, any_order=True
|
|
)
|
|
|
|
def test_should_pass_on_container_delete_with_missing_consumers(self):
|
|
consumers = [self.consumer, self.consumer2]
|
|
ret_val = (consumers, 0, 0, 1)
|
|
self.consumer_repo.get_by_container_id.return_value = ret_val
|
|
self.consumer_repo.delete_entity_by_id.side_effect = excep.NotFound
|
|
|
|
resp = self.app.delete(
|
|
'/containers/{0}/'.format(self.container.id)
|
|
)
|
|
self.assertEqual(204, resp.status_int)
|
|
|
|
# Verify consumers were deleted
|
|
calls = []
|
|
for consumer in consumers:
|
|
calls.append(mock.call(consumer.id, self.external_project_id))
|
|
self.consumer_repo.delete_entity_by_id.assert_has_calls(
|
|
calls, any_order=True
|
|
)
|
|
|
|
|
|
class WhenPerformingUnallowedOperationsOnContainerConsumers(FunctionalTest):
|
|
def setUp(self):
|
|
super(
|
|
WhenPerformingUnallowedOperationsOnContainerConsumers, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
containers = controllers.containers.ContainersController()
|
|
|
|
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.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked container
|
|
self.container = create_container(
|
|
id_ref=utils.generate_test_valid_uuid(),
|
|
project_id=self.project_internal_id,
|
|
external_project_id=self.external_project_id)
|
|
|
|
# Set up mocked container consumers
|
|
self.consumer = create_container_consumer(
|
|
self.container.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
self.consumer2 = create_container_consumer(
|
|
self.container.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
|
|
self.consumer_ref = {
|
|
'name': self.consumer.name,
|
|
'URL': self.consumer.URL
|
|
}
|
|
|
|
# Set up container repo
|
|
self.container_repo = mock.MagicMock()
|
|
self.container_repo.get_container_by_id.return_value = self.container
|
|
self.setup_container_repository_mock(self.container_repo)
|
|
|
|
# Set up container consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_container_consumer_repository_mock(self.consumer_repo)
|
|
|
|
# Set up secret repo
|
|
self.setup_secret_repository_mock()
|
|
|
|
def test_should_not_allow_put_on_consumers(self):
|
|
ret_val = ([self.consumer], 0, 0, 1)
|
|
self.consumer_repo.get_by_container_id.return_value = ret_val
|
|
|
|
resp = self.app.put_json(
|
|
'/containers/{0}/consumers/'.format(self.container.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_post_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.post_json(
|
|
'/containers/{0}/consumers/{1}/'.format(self.container.id,
|
|
self.consumer.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_put_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.put_json(
|
|
'/containers/{0}/consumers/{1}/'.format(self.container.id,
|
|
self.consumer.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_delete_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.delete(
|
|
'/containers/{0}/consumers/{1}/'.format(self.container.id,
|
|
self.consumer.id),
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
|
|
class WhenOwnershipMismatchForContainerConsumer(FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(
|
|
WhenOwnershipMismatchForContainerConsumer, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
containers = controllers.containers.ContainersController()
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked container
|
|
self.container = create_container(
|
|
id_ref=utils.generate_test_valid_uuid(),
|
|
project_id=self.project_internal_id,
|
|
external_project_id='differentProjectId')
|
|
|
|
# Set up mocked consumers
|
|
self.consumer = create_container_consumer(self.container.id,
|
|
self.project_internal_id,
|
|
id_ref='id2')
|
|
self.consumer2 = create_container_consumer(self.container.id,
|
|
self.project_internal_id,
|
|
id_ref='id3')
|
|
|
|
self.consumer_ref = {
|
|
'name': self.consumer.name,
|
|
'URL': self.consumer.URL
|
|
}
|
|
|
|
# Set up mocked container repo
|
|
self.container_repo = mock.MagicMock()
|
|
self.container_repo.get.return_value = self.container
|
|
self.container_repo.get_container_by_id.return_value = self.container
|
|
self.setup_container_repository_mock(self.container_repo)
|
|
|
|
# Set up mocked container consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_container_consumer_repository_mock(self.consumer_repo)
|
|
|
|
# Set up mocked secret repo
|
|
self.setup_secret_repository_mock()
|
|
|
|
def test_consumer_check_ownership_mismatch(self):
|
|
resp = self.app.delete_json(
|
|
'/containers/{0}/consumers/'.format(self.container.id),
|
|
self.consumer_ref, expect_errors=True)
|
|
self.assertEqual(403, resp.status_int)
|
|
|
|
|
|
class WhenCreatingSecretConsumersUsingResource(FunctionalTest):
|
|
def setUp(self):
|
|
super(
|
|
WhenCreatingSecretConsumersUsingResource, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked secret
|
|
self.secret = models.Secret()
|
|
self.secret.id = utils.generate_test_valid_uuid()
|
|
self.secret.project = self.project
|
|
self.secret.project_id = self.project_internal_id
|
|
|
|
# Set up consumer ref
|
|
self.consumer_ref = {
|
|
"service": "service",
|
|
"resource_type": "resource_type",
|
|
"resource_id": "resource_id",
|
|
}
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked quota enforcer
|
|
self.quota_patch = mock.patch(
|
|
'barbican.common.quota.QuotaEnforcer.enforce', return_value=None)
|
|
self.quota_patch.start()
|
|
self.addCleanup(self.quota_patch.stop)
|
|
|
|
# Set up mocked secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.get_secret_by_id.return_value = self.secret
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up mocked secret meta repo
|
|
self.secret_meta_repo = mock.MagicMock()
|
|
self.secret_meta_repo.get_metadata_for_secret.return_value = None
|
|
self.setup_secret_meta_repository_mock(self.secret_meta_repo)
|
|
|
|
# Set up mocked secret consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.create_from.return_value = None
|
|
self.setup_secret_consumer_repository_mock(self.consumer_repo)
|
|
|
|
def test_should_add_new_consumer(self):
|
|
resp = self.app.post_json(
|
|
'/secrets/{0}/consumers/'.format(self.secret.id),
|
|
self.consumer_ref
|
|
)
|
|
self.assertEqual(200, resp.status_int)
|
|
self.assertNotIn(self.external_project_id, resp.headers['Location'])
|
|
|
|
args, kwargs = self.consumer_repo.create_or_update_from.call_args
|
|
consumer = args[0]
|
|
self.assertIsInstance(consumer, models.SecretConsumerMetadatum)
|
|
|
|
def test_should_fail_consumer_bad_json(self):
|
|
resp = self.app.post(
|
|
'/secrets/{0}/consumers/'.format(self.secret.id),
|
|
'',
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(415, resp.status_int)
|
|
|
|
def test_should_404_when_secret_ref_doesnt_exist(self):
|
|
self.secret_repo.get_secret_by_id.return_value = None
|
|
resp = self.app.post_json(
|
|
'/secrets/{0}/consumers/'.format('bad_id'),
|
|
self.consumer_ref, expect_errors=True
|
|
)
|
|
self.assertEqual(404, resp.status_int)
|
|
|
|
|
|
class WhenGettingOrDeletingSecretConsumersUsingResource(FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(
|
|
WhenGettingOrDeletingSecretConsumersUsingResource, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked secret
|
|
self.secret = models.Secret()
|
|
self.secret.id = utils.generate_test_valid_uuid()
|
|
self.secret.project = self.project
|
|
self.secret.project_id = self.project_internal_id
|
|
|
|
# Set up mocked consumers
|
|
self.consumer = create_secret_consumer(
|
|
self.secret.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
self.consumer2 = create_secret_consumer(
|
|
self.secret.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
|
|
self.consumer_ref = {
|
|
"service": self.consumer.service,
|
|
"resource_type": self.consumer.resource_type,
|
|
"resource_id": self.consumer.resource_type,
|
|
}
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.get_secret_by_id.return_value = self.secret
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up mocked secret meta repo
|
|
self.secret_meta_repo = mock.MagicMock()
|
|
self.secret_meta_repo.get_metadata_for_secret.return_value = None
|
|
self.setup_secret_meta_repository_mock(self.secret_meta_repo)
|
|
|
|
# Set up mocked secret consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_secret_consumer_repository_mock(self.consumer_repo)
|
|
|
|
def test_should_get_consumer(self):
|
|
ret_val = ([self.consumer], 0, 0, 1)
|
|
self.consumer_repo.get_by_secret_id.return_value = ret_val
|
|
|
|
resp = self.app.get('/secrets/{0}/consumers/'.format(
|
|
self.secret.id
|
|
))
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
self.consumer_repo.get_by_secret_id.assert_called_once_with(
|
|
self.secret.id,
|
|
limit_arg=None,
|
|
offset_arg=0,
|
|
suppress_exception=True
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.consumer.service,
|
|
resp.json["consumers"][0]["service"],
|
|
)
|
|
self.assertEqual(
|
|
self.consumer.resource_type,
|
|
resp.json["consumers"][0]["resource_type"]
|
|
)
|
|
self.assertEqual(
|
|
self.consumer.resource_id,
|
|
resp.json["consumers"][0]["resource_id"]
|
|
)
|
|
|
|
def test_should_404_when_secret_ref_doesnt_exist(self):
|
|
self.secret_repo.get_secret_by_id.return_value = None
|
|
resp = self.app.get('/secrets/{0}/consumers/'.format(
|
|
'bad_id'
|
|
), expect_errors=True)
|
|
self.assertEqual(404, resp.status_int)
|
|
|
|
def test_should_get_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.get('/secrets/{0}/consumers/{1}/'.format(
|
|
self.secret.id, self.consumer.id
|
|
))
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
def test_should_404_with_bad_consumer_id(self):
|
|
self.consumer_repo.get.return_value = None
|
|
resp = self.app.get('/secrets/{0}/consumers/{1}/'.format(
|
|
self.secret.id, 'bad_id'
|
|
), expect_errors=True)
|
|
self.assertEqual(404, resp.status_int)
|
|
|
|
def test_should_get_no_consumers(self):
|
|
self.consumer_repo.get_by_secret_id.return_value = ([], 0, 0, 0)
|
|
resp = self.app.get('/secrets/{0}/consumers/'.format(
|
|
self.secret.id
|
|
))
|
|
self.assertEqual(200, resp.status_int)
|
|
|
|
def test_should_delete_consumer(self):
|
|
self.app.delete_json('/secrets/{0}/consumers/'.format(
|
|
self.secret.id
|
|
), self.consumer_ref)
|
|
|
|
self.consumer_repo.delete_entity_by_id.assert_called_once_with(
|
|
self.consumer.id, self.external_project_id)
|
|
|
|
def test_should_fail_deleting_consumer_bad_json(self):
|
|
resp = self.app.delete(
|
|
'/secrets/{0}/consumers/'.format(self.secret.id),
|
|
'',
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(415, resp.status_int)
|
|
|
|
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('/secrets/{0}/consumers/'.format(
|
|
self.secret.id
|
|
), self.consumer_ref, expect_errors=True)
|
|
self.consumer_repo.get_by_values.return_value = old_return
|
|
self.assertEqual(404, resp.status_int)
|
|
# Error response should have json content type
|
|
self.assertEqual("application/json", resp.content_type)
|
|
|
|
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('/secrets/{0}/consumers/'.format(
|
|
self.secret.id
|
|
), self.consumer_ref, expect_errors=True)
|
|
self.consumer_repo.delete_entity_by_id.side_effect = None
|
|
self.assertEqual(404, resp.status_int)
|
|
# Error response should have json content type
|
|
self.assertEqual("application/json", resp.content_type)
|
|
|
|
def test_should_delete_consumers_on_secret_delete(self):
|
|
consumers = [self.consumer, self.consumer2]
|
|
ret_val = (consumers, 0, 0, 1)
|
|
self.consumer_repo.get_by_secret_id.return_value = ret_val
|
|
|
|
resp = self.app.delete(
|
|
'/secrets/{0}/'.format(self.secret.id)
|
|
)
|
|
self.assertEqual(204, resp.status_int)
|
|
|
|
# Verify consumers were deleted
|
|
calls = []
|
|
for consumer in consumers:
|
|
calls.append(mock.call(consumer.id, self.external_project_id))
|
|
self.consumer_repo.delete_entity_by_id.assert_has_calls(
|
|
calls, any_order=True
|
|
)
|
|
|
|
def test_should_pass_on_secret_delete_with_missing_consumers(self):
|
|
consumers = [self.consumer, self.consumer2]
|
|
ret_val = (consumers, 0, 0, 1)
|
|
self.consumer_repo.get_by_secret_id.return_value = ret_val
|
|
self.consumer_repo.delete_entity_by_id.side_effect = excep.NotFound
|
|
|
|
resp = self.app.delete(
|
|
'/secrets/{0}/'.format(self.secret.id)
|
|
)
|
|
self.assertEqual(204, resp.status_int)
|
|
|
|
# Verify consumers were deleted
|
|
calls = []
|
|
for consumer in consumers:
|
|
calls.append(mock.call(consumer.id, self.external_project_id))
|
|
self.consumer_repo.delete_entity_by_id.assert_has_calls(
|
|
calls, any_order=True
|
|
)
|
|
|
|
|
|
class WhenPerformingUnallowedOperationsOnSecretConsumers(FunctionalTest):
|
|
def setUp(self):
|
|
super(
|
|
WhenPerformingUnallowedOperationsOnSecretConsumers, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked secret
|
|
self.secret = models.Secret()
|
|
self.secret.id = utils.generate_test_valid_uuid()
|
|
self.secret.project_id = self.project_internal_id
|
|
|
|
# Set up mocked secret consumers
|
|
self.consumer = create_secret_consumer(
|
|
self.secret.id, self.project_internal_id,
|
|
id_ref=utils.generate_test_valid_uuid())
|
|
self.consumer_ref = {
|
|
"service": self.consumer.service,
|
|
"resource_type": self.consumer.resource_type,
|
|
"resource_id": self.consumer.resource_type,
|
|
}
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.get_secret_by_id.return_value = self.secret
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up secret consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_secret_consumer_repository_mock(self.consumer_repo)
|
|
|
|
def test_should_not_allow_put_on_consumers(self):
|
|
ret_val = ([self.consumer], 0, 0, 1)
|
|
self.consumer_repo.get_by_secret_id.return_value = ret_val
|
|
|
|
resp = self.app.put_json(
|
|
'/secrets/{0}/consumers/'.format(self.secret.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_post_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.post_json(
|
|
'/secrets/{0}/consumers/{1}/'.format(self.secret.id,
|
|
self.consumer.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_put_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.put_json(
|
|
'/secrets/{0}/consumers/{1}/'.format(self.secret.id,
|
|
self.consumer.id),
|
|
self.consumer_ref,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
def test_should_not_allow_delete_on_consumer_by_id(self):
|
|
self.consumer_repo.get.return_value = self.consumer
|
|
resp = self.app.delete(
|
|
'/secrets/{0}/consumers/{1}/'.format(self.secret.id,
|
|
self.consumer.id),
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(405, resp.status_int)
|
|
|
|
|
|
class WhenOwnershipMismatchForSecretConsumer(FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(
|
|
WhenOwnershipMismatchForSecretConsumer, self
|
|
).setUp()
|
|
self.app = webtest.TestApp(app.build_wsgi_app(self.root))
|
|
self.app.extra_environ = get_barbican_env(self.external_project_id)
|
|
|
|
@property
|
|
def root(self):
|
|
self._init()
|
|
|
|
class RootController(object):
|
|
secrets = controllers.secrets.SecretsController()
|
|
return RootController()
|
|
|
|
def _init(self):
|
|
self.external_project_id = 'keystoneid1234'
|
|
self.project_internal_id = 'projectid1234'
|
|
|
|
# Set up mocked project
|
|
self.project = models.Project()
|
|
self.project.id = self.project_internal_id
|
|
self.project.external_id = self.external_project_id
|
|
|
|
# Set up mocked secret
|
|
self.secret = models.Secret()
|
|
self.secret.id = utils.generate_test_valid_uuid()
|
|
self.secret.project = models.Project()
|
|
self.secret.project.external_id = "differentProjectId"
|
|
|
|
# Set up mocked consumer
|
|
self.consumer = create_secret_consumer(self.secret.id,
|
|
self.project_internal_id,
|
|
id_ref='consumerid1234')
|
|
|
|
self.consumer_ref = {
|
|
"service": self.consumer.service,
|
|
"resource_type": self.consumer.resource_type,
|
|
"resource_id": self.consumer.resource_type,
|
|
}
|
|
|
|
# Set up mocked project repo
|
|
self.project_repo = mock.MagicMock()
|
|
self.project_repo.get.return_value = self.project
|
|
self.setup_project_repository_mock(self.project_repo)
|
|
|
|
# Set up mocked secret repo
|
|
self.secret_repo = mock.MagicMock()
|
|
self.secret_repo.get.return_value = self.secret
|
|
self.secret_repo.get_secret_by_id.return_value = self.secret
|
|
self.setup_secret_repository_mock(self.secret_repo)
|
|
|
|
# Set up mocked secret consumer repo
|
|
self.consumer_repo = mock.MagicMock()
|
|
self.consumer_repo.get_by_values.return_value = self.consumer
|
|
self.consumer_repo.delete_entity_by_id.return_value = None
|
|
self.setup_secret_consumer_repository_mock(self.consumer_repo)
|
|
|
|
def test_consumer_check_ownership_mismatch(self):
|
|
resp = self.app.delete_json(
|
|
'/secrets/{0}/consumers/'.format(self.secret.id),
|
|
self.consumer_ref, expect_errors=True)
|
|
self.assertEqual(403, resp.status_int)
|