diff --git a/barbicanclient/client.py b/barbicanclient/client.py index 912a91bc..1020f5a4 100644 --- a/barbicanclient/client.py +++ b/barbicanclient/client.py @@ -111,7 +111,7 @@ class Connection(object): """ LOG.debug(_("Listing secrets")) href = "{0}/{1}?limit=100".format(self._tenant, self.SECRETS_PATH) - LOG.debug("href: {}".format(href)) + LOG.debug("href: {0}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) @@ -130,9 +130,9 @@ class Connection(object): bit_length=None, cypher_type=None, expiration=None): - LOG.debug(_("Creating secret of mime_type {}").format(mime_type)) + LOG.debug(_("Creating secret of mime_type {0}").format(mime_type)) href = "{0}/{1}".format(self._tenant, self.SECRETS_PATH) - LOG.debug(_("href: {}").format(href)) + LOG.debug(_("href: {0}").format(href)) secret_dict = {} secret_dict['mime_type'] = mime_type secret_dict['plain_text'] = plain_text @@ -143,21 +143,19 @@ class Connection(object): secret_dict['bit_length'] = int(bit_length) if expiration is not None: secret_dict['expiration'] = parse_isotime(expiration) - #(secret_dict.pop(k) for k in secret_dict.keys() if secret_dict[k] is None) self._remove_empty_keys(secret_dict) - #LOG.critical("DICT: {}".format(secret_dict)) - LOG.debug(_("Request body: {}").format(secret_dict)) + LOG.debug(_("Request body: {0}").format(secret_dict)) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(secret_dict)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) - return body['secret_ref'] - #return Secret(self, body) + return self.get_secret(body['secret_ref']) def delete_secret_by_id(self, secret_id): href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) - LOG.info(_("Deleting secret - Secret ID: {}").format(secret_id)) + LOG.info(_("Deleting secret - Secret ID: {0}").format(secret_id)) return self.delete_secret(href) def delete_secret(self, href): @@ -165,7 +163,7 @@ class Connection(object): LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) def get_secret_by_id(self, secret_id): - LOG.debug(_("Getting secret - Secret ID: {}").format(secret_id)) + LOG.debug(_("Getting secret - Secret ID: {0}").format(secret_id)) href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) return self.get_secret(href) @@ -192,7 +190,7 @@ class Connection(object): """ LOG.debug(_("Listing orders")) href = "{0}/{1}?limit=100".format(self._tenant, self.ORDERS_PATH) - LOG.debug("href: {}".format(href)) + LOG.debug("href: {0}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) @@ -209,9 +207,9 @@ class Connection(object): algorithm=None, bit_length=None, cypher_type=None): - LOG.debug(_("Creating order of mime_type {}").format(mime_type)) + LOG.debug(_("Creating order of mime_type {0}").format(mime_type)) href = "{0}/{1}".format(self._tenant, self.ORDERS_PATH) - LOG.debug("href: {}".format(href)) + LOG.debug("href: {0}".format(href)) order_dict = {'secret': {}} order_dict['secret']['name'] = name order_dict['secret']['mime_type'] = mime_type @@ -219,14 +217,17 @@ class Connection(object): order_dict['secret']['bit_length'] = bit_length order_dict['secret']['cypher_type'] = cypher_type self._remove_empty_keys(order_dict['secret']) - LOG.debug(_("Request body: {}").format(order_dict['secret'])) + LOG.debug(_("Request body: {0}").format(order_dict['secret'])) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(order_dict)) - return body['order_ref'] + + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) + + return self.get_order(body['order_ref']) def delete_order_by_id(self, order_id): - LOG.info(_("Deleting order - Order ID: {}").format(order_id)) + LOG.info(_("Deleting order - Order ID: {0}").format(order_id)) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.delete_order(href) @@ -235,7 +236,7 @@ class Connection(object): LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) def get_order_by_id(self, order_id): - LOG.debug(_("Getting order - Order ID: {}").format(order_id)) + LOG.debug(_("Getting order - Order ID: {0}").format(order_id)) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.get_order(href) @@ -269,10 +270,9 @@ class Connection(object): response = self.request(method=method, url=url, data=request_body, headers=headers) - LOG.critical("Response: {}".format(response.content)) # Check if the status code is 2xx class if not response.ok: - LOG.error('Bad response: {}'.format(response.status_code)) + LOG.error('Bad response: {0}'.format(response.status_code)) raise ClientException(href=href, method=method, http_status=response.status_code, http_response_content=response.content) diff --git a/barbicanclient/orders.py b/barbicanclient/orders.py index b30590b8..b23e329a 100644 --- a/barbicanclient/orders.py +++ b/barbicanclient/orders.py @@ -11,7 +11,7 @@ class Order(object): """ self.connection = connection self.status = order_dict.get('status') - self.secret = order_dict.get('secret') # TODO: store as object? + self.secret = order_dict.get('secret') self.secret_ref = order_dict.get('secret_ref') self.order_ref = order_dict.get('order_ref') self.created = parse_isotime(order_dict.get('created')) @@ -26,6 +26,9 @@ class Order(object): def id(self): return self._id + def get_secret(self): + return self.connection.get_secret(self.secret_ref) + def save(self): self.connection.update_order(self) diff --git a/barbicanclient/secrets.py b/barbicanclient/secrets.py index 14750b8e..293771cf 100644 --- a/barbicanclient/secrets.py +++ b/barbicanclient/secrets.py @@ -11,10 +11,10 @@ class Secret(object): Builds a secret object from a json representation. Includes the connection object for subtasks. """ - self._connection = connection + self.connection = connection self.secret_ref = secret_dict['secret_ref'] - self.created = parse_isotime(secret_dict['created']) - self.status = secret_dict['status'] + self.created = parse_isotime(secret_dict.get('created')) + self.status = secret_dict.get('status') self.algorithm = secret_dict.get('algorithm') self.bit_length = secret_dict.get('bit_length') diff --git a/etc/barbicanclient.conf b/etc/barbicanclient.conf index f6412b2a..39603249 100644 --- a/etc/barbicanclient.conf +++ b/etc/barbicanclient.conf @@ -3,7 +3,7 @@ verbose = True # Show debugging output in logs (sets DEBUG log level output) -debug = True +debug = False # Log to this file. Make sure you do not set the same log # file for both the API and registry servers! diff --git a/tests/client_test.py b/tests/client_test.py index 058f94e8..28f08f63 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -13,10 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import unittest from mock import MagicMock -from barbicanclient.client import Connection + +from barbicanclient import client +from barbicanclient.common.exceptions import ClientException def suite(): @@ -28,7 +31,6 @@ def suite(): class WhenTestingConnection(unittest.TestCase): - def setUp(self): self.auth_endpoint = 'https://keystone.com/v2' self.user = 'user' @@ -36,29 +38,42 @@ class WhenTestingConnection(unittest.TestCase): self.tenant = 'tenant' self.endpoint = 'http://localhost:9311/v1/' self.auth_token = 'token' + self.href = 'http://localhost:9311/v1/12345/orders' self.authenticate = MagicMock() self.authenticate.return_value = (self.endpoint, self.auth_token) self.request = MagicMock() - self.request.return_value.content = {"secret_ref": "http://localhost:9" - + "311/v1/None/secrets/ea1bb4e5-" - + "e769-4d2c-8e58-cbbb17f1a3de"} + self.request.return_value.content = json.dumps( + { + "secret_ref": "http://localhost:9311/None/secrets" + "/8502cea9-9d35-46d7-96f5-80e43905e4c5" + } + ) + self.request.return_value.headers = { + 'content-length': '92', + 'content-type': 'application/json; charset=utf-8', + 'location': 'http://localhost:9311/None/' + 'secrets/8502cea9-9d35-46d7-96f5-80e43905e4c5', + 'x-openstack-request-id': + 'req-6c19d09e-1167-445c-b435-d6b0818b59b9' + } self.request.return_value.ok = True - self.connection = Connection(self.auth_endpoint, self.user, self.key, - self.tenant, token=self.auth_token, - authenticate=self.authenticate,) + self.connection = client.Connection(self.auth_endpoint, self.user, + self.key, self.tenant, + token=self.auth_token, + authenticate=self.authenticate, + request=self.request) def test_should_connect_with_token(self): self.assertFalse(self.authenticate.called) def test_should_connect_without_token(self): - self.connection = Connection(self.auth_endpoint, - self.user, - self.key, - self.tenant, - authenticate=self.authenticate, - endpoint=self.endpoint - ) + self.connection = client.Connection(self.auth_endpoint, + self.user, + self.key, + self.tenant, + authenticate=self.authenticate, + endpoint=self.endpoint) self.authenticate\ .assert_called_once_with(self.auth_endpoint, self.user, @@ -75,7 +90,202 @@ class WhenTestingConnection(unittest.TestCase): self.assertEqual(self.endpoint, self.connection._endpoint) def test_should_create_secret(self): - pass + body = {'status': 'ACTIVE', + 'content_types': {'default': 'text/plain'}, + 'updated': '2013-06-07T16:13:38.889857', + 'cypher_type': 'CDC', + 'name': 'test_secret', + 'algorithm': 'aes', + 'created': '2013-06-07T16:13:38.889851', + 'secret_ref': 'http://localhost:9311/v1/None/secrets/e6e7d' + 'b5e-3738-408e-aaba-05a7177cade5', + 'expiration': '2015-06-07T16:13:38.889851', + 'bit_length': 256, + 'mime_type': 'text/plain' + } + + secret = client.Secret(self.connection, body) + self.request.return_value.content = json.dumps(body) + created = self.connection.create_secret('text/plain', + 'Test secret', + name='test_secret', + algorithm=None, + bit_length=None, + cypher_type=None, + expiration=None) + self.assertTrue(self._are_equivalent(secret, created)) + + def test_should_create_order(self): + body = {"status": "ACTIVE", + "secret_ref": "http://localhost:9311/v1/12345/secrets/5706054" + "9-2fcf-46eb-92bb-bf49fcf5d089", + "updated": "2013-06-07T19:00:37.338386", + "created": "2013-06-07T19:00:37.298704", + "secret": { + "cypher_type": "CDC", + "name": "test_secret", + "algorithm": "aes", + "expiration": "2015-06-07T19:00:37.298704", + "bit_length": 256, + "mime_type": "text/plain" + }, + "order_ref": "http://localhost:9311/v1/12345/orders/003f2b91-" + "2f53-4c0a-a0f3-33796671efc3" + } + + order = client.Order(self.connection, body) + self.request.return_value.content = json.dumps(body) + created = self.connection.create_order('text/plain', + name='test_secret', + bit_length=256, + algorithm='aes', + cypher_type='CDC') + self.assertTrue(self._are_equivalent(order, created)) + + def test_list_no_secrets(self): + body0 = {'secrets': []} + secrets = [] + self.request.return_value.content = json.dumps(body0) + self.assertTrue(self._are_equivalent(secrets, + self.connection.list_secrets())) + + def test_list_single_secret(self): + body1 = {'secrets': [{'status': 'ACTIVE', + 'content_types': {'default': 'text/plain'}, + 'updated': '2013-06-03T21:16:58.349230', + 'cypher_type': None, + 'name': 'test_1', + 'algorithm': None, + 'created': '2013-06-03T21:16:58.349222', + 'secret_ref': 'http://localhost:9311/v1/' + 'None/secrets/bbd2036f-730' + '7-4090-bbef-bbb6025e5e7b', + 'expiration': None, + 'bit_length': None, + 'mime_type': 'text/plain'}]} + secrets = [client.Secret(self.connection, body1['secrets'][0])] + self.request.return_value.content = json.dumps(body1) + self.assertTrue(self._are_equivalent(secrets, + self.connection.list_secrets())) + + def test_list_multiple_secrets(self): + body1 = {'secrets': [{'status': 'ACTIVE', + 'content_types': {'default': 'text/plain'}, + 'updated': '2013-06-03T21:16:58.349230', + 'cypher_type': None, + 'name': 'test_1', + 'algorithm': None, + 'created': '2013-06-03T21:16:58.349222', + 'secret_ref': 'http://localhost:9311/v1/' + 'None/secrets/bbd2036f-730' + '7-4090-bbef-bbb6025e5e7b', + 'expiration': None, + 'bit_length': None, + 'mime_type': 'text/plain'}]} + + body2 = body1 + body2['secrets'][0]['name'] = 'test_2' + body2['secrets'][0]['secret_ref'] = 'http://localhost:9311/v1/No'\ + + 'ne/secrets/bbd2036f-7307-'\ + + '4090-bbef-bbb6025eabcd' + + secrets = [client.Secret(self.connection, b['secrets'][0]) + for b in (body1, body2)] + body2['secrets'].insert(0, body1['secrets'][0]) + self.request.return_value.content = json.dumps(body2) + self.assertTrue(self._are_equivalent(secrets, + self.connection.list_secrets())) + + def test_list_no_orders(self): + body0 = {'orders': []} + orders = [] + self.request.return_value.content = json.dumps(body0) + self.assertTrue(self._are_equivalent(orders, + self.connection.list_orders())) + + def test_list_single_order(self): + body1 = {'orders': [{'status': 'PENDING', + 'updated': '2013-06-05T15:15:30.904760', + 'created': '2013-06-05T15:15:30.904752', + 'order_ref': 'http://localhost:9311/v1/' + 'None/orders/9f651441-3ccd' + '-45b3-bc60-3051656d5168', + 'secret_ref': 'http://localhost:9311/' + 'v1/None/secrets/????', + 'secret': {'cypher_type': None, + 'name': 'test_1', + 'algorithm': None, + 'expiration': None, + 'bit_length': None, + 'mime_type': 'text/plain'}}]} + orders = [client.Order(self.connection, body1['orders'][0])] + self.request.return_value.content = json.dumps(body1) + self.assertTrue(self._are_equivalent(orders, + self.connection.list_orders())) + + def test_list_multiple_orders(self): + body1 = {'orders': [{'status': 'PENDING', + 'updated': '2013-06-05T15:15:30.904760', + 'created': '2013-06-05T15:15:30.904752', + 'order_ref': 'http://localhost:9311/v1/' + 'None/orders/9f651441-3ccd' + '-45b3-bc60-3051656d5168', + 'secret_ref': 'http://localhost:9311/' + 'v1/None/secrets/????', + 'secret': {'cypher_type': None, + 'name': 'test_1', + 'algorithm': None, + 'expiration': None, + 'bit_length': None, + 'mime_type': 'text/plain'}}]} + body2 = body1 + body2['orders'][0]['order_ref'] = 'http://localhost:9311/v1/No'\ + + 'ne/orders/9f651441-3ccd-4'\ + + '5b3-bc60-3051656382fj' + body2['orders'][0]['secret']['name'] = 'test_2' + + orders = [client.Order(self.connection, b['orders'][0]) + for b in (body1, body2)] + body2['orders'].insert(0, body1['orders'][0]) + self.request.return_value.content = json.dumps(body2) + self.assertTrue(self._are_equivalent(orders, + self.connection.list_orders())) + + def test_should_get_response(self): + self._setup_request() + headers, body = self.connection._perform_http('GET', self.href) + self.assertEqual(self.request.return_value.headers, headers) + self.assertEqual(json.loads(self.request.return_value.content), body) + + def test_should_parse_json(self): + self._setup_request() + headers, body = self.connection._perform_http('GET', self.href, + parse_json=True) + self.assertEqual(json.loads(self.request.return_value.content), body) + + def test_should_not_parse_json(self): + self._setup_request() + headers, body = self.connection._perform_http('GET', self.href, + parse_json=False) + self.assertEqual(self.request.return_value.content, body) + + def test_should_raise_exception(self): + self._setup_request() + self.request.return_value.ok = False + with self.assertRaises(ClientException): + self.connection._perform_http('GET', self.href) + + def _setup_request(self): + self.request.return_value.headers = {'Accept': 'application/json'} + self.request.return_value.content = '{"test": "response"}' + self.href = 'http://localhost:9311/v1/12345/orders' + + def _are_equivalent(self, a, b): + if isinstance(a, list) and isinstance(b, list): + return all([self._are_equivalent(x, y) for x, y in zip(a, b)]) + else: + return (a.__dict__ == b.__dict__) + if __name__ == '__main__': unittest.main()