From c832c613faa7658d0a463b0fc713f91f7e7838c7 Mon Sep 17 00:00:00 2001 From: Marc Koderer Date: Mon, 12 Dec 2016 11:49:10 +0100 Subject: [PATCH] Register client and add first test Add first API test and register the client with the new tempest.lib client interface. Change-Id: I27f15375c46faa48cd56c8d52ecfd585fb325239 Implements: bp tempest-plugin --- barbican_tempest_plugin/clients.py | 32 +++++++ barbican_tempest_plugin/plugin.py | 9 ++ .../services/key_manager/__init__.py | 0 .../services/key_manager/json/__init__.py | 18 ++++ .../key_manager/json/secret_client.py | 40 +++++++++ barbican_tempest_plugin/tests/api/base.py | 84 +++++++++++++++++++ .../tests/api/test_secrets.py | 51 +++++++++++ requirements.txt | 1 + 8 files changed, 235 insertions(+) create mode 100644 barbican_tempest_plugin/clients.py create mode 100644 barbican_tempest_plugin/services/key_manager/__init__.py create mode 100644 barbican_tempest_plugin/services/key_manager/json/__init__.py create mode 100644 barbican_tempest_plugin/services/key_manager/json/secret_client.py create mode 100644 barbican_tempest_plugin/tests/api/base.py create mode 100644 barbican_tempest_plugin/tests/api/test_secrets.py diff --git a/barbican_tempest_plugin/clients.py b/barbican_tempest_plugin/clients.py new file mode 100644 index 0000000..078f383 --- /dev/null +++ b/barbican_tempest_plugin/clients.py @@ -0,0 +1,32 @@ +# Copyright (c) 2016 SAP SE +# +# 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. + +from tempest import config +from tempest.lib.services import clients + + +CONF = config.CONF + + +class Clients(clients.ServiceClients): + """Tempest stable service clients and loaded plugins service clients""" + + def __init__(self, credentials, service=None): + """Emulate the interface of Tempest's clients.Manager""" + # Identity settings + if CONF.identity.auth_version == 'v2': + identity_uri = CONF.identity.uri + else: + identity_uri = CONF.identity.uri_v3 + super(Clients, self).__init__(credentials, identity_uri) diff --git a/barbican_tempest_plugin/plugin.py b/barbican_tempest_plugin/plugin.py index 6cde8ea..6c01ced 100644 --- a/barbican_tempest_plugin/plugin.py +++ b/barbican_tempest_plugin/plugin.py @@ -35,3 +35,12 @@ class BarbicanTempestPlugin(plugins.TempestPlugin): def get_opt_lists(self): return [('service_available', [project_config.service_option])] + + def get_service_clients(self): + v1_params = { + 'name': 'secret_v1', + 'service_version': 'secret.v1', + 'module_path': 'barbican_tempest_plugin.services.key_manager.json', + 'client_names': ['SecretClient'], + } + return [v1_params] diff --git a/barbican_tempest_plugin/services/key_manager/__init__.py b/barbican_tempest_plugin/services/key_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_tempest_plugin/services/key_manager/json/__init__.py b/barbican_tempest_plugin/services/key_manager/json/__init__.py new file mode 100644 index 0000000..4ddf207 --- /dev/null +++ b/barbican_tempest_plugin/services/key_manager/json/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2016 SAP SE +# +# 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. + +from barbican_tempest_plugin.services.key_manager.json.secret_client import \ + SecretClient + +__all__ = ['SecretClient'] diff --git a/barbican_tempest_plugin/services/key_manager/json/secret_client.py b/barbican_tempest_plugin/services/key_manager/json/secret_client.py new file mode 100644 index 0000000..36a2c08 --- /dev/null +++ b/barbican_tempest_plugin/services/key_manager/json/secret_client.py @@ -0,0 +1,40 @@ +# Copyright 2016 SAP SE +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import json + +from tempest import config +from tempest.lib.common import rest_client +from tempest.lib.common.utils import data_utils + +CONF = config.CONF + + +class SecretClient(rest_client.RestClient): + def create_secret(self, **kwargs): + if 'name' not in kwargs: + kwargs['name'] = data_utils.rand_name("tempest-sec") + + post_body = kwargs + body = json.dumps(post_body) + resp, body = self.post("v1/secrets", body) + self.expected_success(201, resp.status) + return self._parse_resp(body) + + def delete_secret(self, secret_id): + resp, body = self.delete("v1/secrets/%s" % secret_id) + self.expected_success(204, resp.status) + return body diff --git a/barbican_tempest_plugin/tests/api/base.py b/barbican_tempest_plugin/tests/api/base.py new file mode 100644 index 0000000..b1717ff --- /dev/null +++ b/barbican_tempest_plugin/tests/api/base.py @@ -0,0 +1,84 @@ +# Copyright 2016 SAP SE +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import functools + +from tempest import config +from tempest import test + +from barbican_tempest_plugin import clients + +CONF = config.CONF + +# NOTE(dane-fichter): We need to track resource types for cleanup. +RESOURCE_TYPES = ['secret'] + + +def _get_uuid(href): + return href.split('/')[-1] + + +def creates(resource): + """Decorator that adds resource UUIDs to queue for cleanup""" + + def decorator(f): + @functools.wraps(f) + def wrapper(cls, *args, **kwargs): + resp = f(cls, *args, **kwargs) + if resource == 'secret': + uuid = _get_uuid(resp['secret_ref']) + cls.created_objects[resource].add(uuid) + return resp + return wrapper + return decorator + + +class BaseKeyManagerTest(test.BaseTestCase): + """Base class for all api tests.""" + + # Why do I have to be an admin to create secrets? No idea... + credentials = ('admin', ) + client_manager = clients.Clients + created_objects = {} + + @classmethod + def setup_clients(cls): + super(BaseKeyManagerTest, cls).setup_clients() + os = getattr(cls, 'os_%s' % cls.credentials[0]) + cls.secret_client = os.secret_v1.SecretClient(service='key-manager') + + @classmethod + def resource_setup(cls): + super(BaseKeyManagerTest, cls).resource_setup() + for resource in RESOURCE_TYPES: + cls.created_objects[resource] = set() + + @classmethod + def resource_cleanup(cls): + try: + for secret_uuid in cls.created_objects['secret']: + cls.delete_secret(secret_uuid) + finally: + super(BaseKeyManagerTest, cls).resource_cleanup() + + @classmethod + @creates('secret') + def create_secret(cls, **kwargs): + return cls.secret_client.create_secret(**kwargs) + + @classmethod + def delete_secret(cls, uuid): + cls.created_objects['secret'].remove(uuid) + return cls.secret_client.delete_secret(uuid) diff --git a/barbican_tempest_plugin/tests/api/test_secrets.py b/barbican_tempest_plugin/tests/api/test_secrets.py new file mode 100644 index 0000000..06a7731 --- /dev/null +++ b/barbican_tempest_plugin/tests/api/test_secrets.py @@ -0,0 +1,51 @@ +# Copyright 2016 SAP SE +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import base64 +from datetime import datetime +from datetime import timedelta +import os + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + +from barbican_tempest_plugin.tests.api import base + + +class SecretsTest(base.BaseKeyManagerTest): + """Secrets API tests.""" + def test_create_delete_empty_secret(self): + sec = self.create_secret() + uuid = base._get_uuid(sec['secret_ref']) + self.delete_secret(uuid) + + def test_create_delete_symmetric_key(self): + password = b"password" + salt = os.urandom(16) + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), length=32, salt=salt, + iterations=1000, backend=default_backend() + ) + key = base64.b64encode(kdf.derive(password)) + expire_time = (datetime.utcnow() + timedelta(days=5)) + sec = self.create_secret( + expiration=expire_time.isoformat(), algorithm="aes", + bit_length=256, mode="cbc", payload=key, + payload_content_type="application/octet-stream", + payload_content_encoding="base64" + ) + uuid = base._get_uuid(sec['secret_ref']) + self.delete_secret(uuid) diff --git a/requirements.txt b/requirements.txt index 16d3d08..415ded0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ pbr>=1.8 # Apache-2.0 tempest>=12.1.0 # Apache-2.0 tempest-lib>=0.14.0 # Apache-2.0 +cryptography!=1.3.0,>=1.0 # BSD/Apache-2.0