diff -Nru python-barbicanclient-4.6.0/debian/changelog python-barbicanclient-4.6.0/debian/changelog --- python-barbicanclient-4.6.0/debian/changelog 2018-02-14 12:34:23.000000000 -0300 +++ python-barbicanclient-4.6.0/debian/changelog 2020-03-17 11:13:24.000000000 -0300 @@ -1,3 +1,12 @@ +python-barbicanclient (4.6.0-0ubuntu2) bionic; urgency=medium + + * d/p/0001-Allow-fetching-by-UUID-and-respect-interface.patch + * d/p/0002-Secret-payload-should-also-be-fetched-by-UUID.patch + * Adds patches to properly support fetching by UUID, fixes + LP: #1867676. + + -- Jorge Niedbalski Tue, 17 Mar 2020 11:13:24 -0300 + python-barbicanclient (4.6.0-0ubuntu1) bionic; urgency=medium * d/watch: Use pypi.debian.net. diff -Nru python-barbicanclient-4.6.0/debian/patches/0001-Allow-fetching-by-UUID-and-respect-interface.patch python-barbicanclient-4.6.0/debian/patches/0001-Allow-fetching-by-UUID-and-respect-interface.patch --- python-barbicanclient-4.6.0/debian/patches/0001-Allow-fetching-by-UUID-and-respect-interface.patch 1969-12-31 21:00:00.000000000 -0300 +++ python-barbicanclient-4.6.0/debian/patches/0001-Allow-fetching-by-UUID-and-respect-interface.patch 2020-03-17 11:12:22.000000000 -0300 @@ -0,0 +1,1082 @@ +From 663dee9ad9c0f580ccf5971f2359a7eefe2c339c Mon Sep 17 00:00:00 2001 +From: Adam Harwell +Date: Thu, 2 Aug 2018 08:53:32 +0900 +Subject: [PATCH] Allow fetching by UUID, and respect interface + +When passing a UUID to the client, use the Barbican endpoint from +the service catalog to fetch the entity. When passing an href, strip +everything before the UUID and use it the same as a passed UUID. + +This allows for service usage when secrets are created with a public +endpoint but must be retrieved from an internal or admin endpoint, +and is probably how all usage should have worked to begin with. + +Change-Id: I90778a2eeefc4cfe42b0e2a48ba09036e3e6d83d +Story: 2003197 +Task: 23353 +(cherry picked from commit 6651c8ffce48ce7ff08f5563a8e6212677ea0468) +--- + barbicanclient/base.py | 23 +++-- + barbicanclient/tests/test_base.py | 16 +++- + barbicanclient/tests/v1/test_acls.py | 76 +++++++++++----- + barbicanclient/tests/v1/test_cas.py | 17 +++- + barbicanclient/tests/v1/test_containers.py | 47 ++++++++-- + barbicanclient/tests/v1/test_orders.py | 66 ++++++++++++-- + barbicanclient/tests/v1/test_secrets.py | 91 +++++++++++++++++-- + barbicanclient/v1/acls.py | 35 ++++++- + barbicanclient/v1/cas.py | 5 +- + barbicanclient/v1/containers.py | 34 ++++--- + barbicanclient/v1/orders.py | 14 +-- + barbicanclient/v1/secrets.py | 29 +++--- + ...-only-uuid-from-href-81710d1b388dce57.yaml | 8 ++ + tox.ini | 1 + + 14 files changed, 366 insertions(+), 96 deletions(-) + create mode 100644 releasenotes/notes/use-only-uuid-from-href-81710d1b388dce57.yaml + +diff --git a/barbicanclient/base.py b/barbicanclient/base.py +index 9bfcc98..8e38b65 100644 +--- a/barbicanclient/base.py ++++ b/barbicanclient/base.py +@@ -15,9 +15,13 @@ + """ + Base utilities to build API operation managers. + """ ++import logging + import uuid + + ++LOG = logging.getLogger(__name__) ++ ++ + def filter_null_keys(dictionary): + return dict(((k, v) for k, v in dictionary.items() if v is not None)) + +@@ -30,19 +34,26 @@ def censored_copy(data_dict, censor_keys): + data_dict.items()} + + +-def validate_ref(ref, entity): +- """Verifies that there is a real uuid at the end of the uri ++def validate_ref_and_return_uuid(ref, entity): ++ """Verifies that there is a real uuid (possibly at the end of a uri) + +- :return: Returns True for easier testing ++ :return: The uuid.UUID object + :raises ValueError: If it cannot correctly parse the uuid in the ref. + """ + try: +- _, entity_uuid = ref.rstrip('/').rsplit('/', 1) +- uuid.UUID(entity_uuid) ++ # This works for a ref *or* a UUID, since we just pick the last piece ++ ref_pieces = ref.rstrip('/').rsplit('/', 1) ++ return uuid.UUID(ref_pieces[-1]) + except Exception: + raise ValueError('{0} incorrectly specified.'.format(entity)) + +- return True ++ ++def calculate_uuid_ref(ref, entity): ++ entity_uuid = validate_ref_and_return_uuid( ++ ref, entity.capitalize().rstrip('s')) ++ entity_ref = "{entity}/{uuid}".format(entity=entity, uuid=entity_uuid) ++ LOG.info("Calculated %s uuid ref: %s", entity.capitalize(), entity_ref) ++ return entity_ref + + + class ImmutableException(Exception): +diff --git a/barbicanclient/tests/test_base.py b/barbicanclient/tests/test_base.py +index 6f0fa8c..b0d564b 100644 +--- a/barbicanclient/tests/test_base.py ++++ b/barbicanclient/tests/test_base.py +@@ -14,6 +14,7 @@ + # limitations under the License. + + import testtools ++import uuid + + import barbicanclient + from barbicanclient import base +@@ -23,12 +24,21 @@ from barbicanclient import version + class TestValidateRef(testtools.TestCase): + + def test_valid_ref(self): +- ref = 'http://localhost/ff2ca003-5ebb-4b61-8a17-3f9c54ef6356' +- self.assertTrue(base.validate_ref(ref, 'Thing')) ++ secret_uuid = uuid.uuid4() ++ ref = 'http://localhost/' + str(secret_uuid) ++ self.assertEqual(secret_uuid, ++ base.validate_ref_and_return_uuid(ref, 'Thing')) ++ ++ def test_valid_uuid(self): ++ secret_uuid = uuid.uuid4() ++ self.assertEqual(secret_uuid, ++ base.validate_ref_and_return_uuid(str(secret_uuid), ++ 'Thing')) + + def test_invalid_uuid(self): + ref = 'http://localhost/not_a_uuid' +- self.assertRaises(ValueError, base.validate_ref, ref, 'Thing') ++ self.assertRaises(ValueError, base.validate_ref_and_return_uuid, ref, ++ 'Thing') + + def test_censored_copy(self): + d1 = {'a': '1', 'password': 'my_password', 'payload': 'my_key', +diff --git a/barbicanclient/tests/v1/test_acls.py b/barbicanclient/tests/v1/test_acls.py +index b930c57..e8f77d6 100644 +--- a/barbicanclient/tests/v1/test_acls.py ++++ b/barbicanclient/tests/v1/test_acls.py +@@ -25,12 +25,13 @@ class ACLTestCase(test_client.BaseEntityResource): + def setUp(self): + self._setUp('acl', entity_id='d9f95d61-8863-49d3-a045-5c2cb77130b5') + +- self.secret_ref = (self.endpoint + +- '/secrets/8a3108ec-88fc-4f5c-86eb-f37b8ae8358e') +- ++ self.secret_uuid = '8a3108ec-88fc-4f5c-86eb-f37b8ae8358e' ++ self.secret_ref = (self.endpoint + '/v1/secrets/' + self.secret_uuid) + self.secret_acl_ref = '{0}/acl'.format(self.secret_ref) +- self.container_ref = (self.endpoint + '/containers/' +- '83c302c7-86fe-4f07-a277-c4962f121f19') ++ ++ self.container_uuid = '83c302c7-86fe-4f07-a277-c4962f121f19' ++ self.container_ref = (self.endpoint + '/v1/containers/' + ++ self.container_uuid) + self.container_acl_ref = '{0}/acl'.format(self.container_ref) + + self.manager = self.client.acls +@@ -54,16 +55,22 @@ class ACLTestCase(test_client.BaseEntityResource): + + class WhenTestingACLManager(ACLTestCase): + +- def test_should_get_secret_acl(self): ++ def test_should_get_secret_acl(self, entity_ref=None): ++ entity_ref = entity_ref or self.secret_ref + self.responses.get(self.secret_acl_ref, + json=self.get_acl_response_data()) + +- api_resp = self.manager.get(entity_ref=self.secret_ref) ++ api_resp = self.manager.get(entity_ref=entity_ref) + self.assertEqual(self.secret_acl_ref, + self.responses.last_request.url) + self.assertFalse(api_resp.get('read').project_access) + self.assertEqual('read', api_resp.get('read').operation_type) +- self.assertEqual(self.secret_acl_ref, api_resp.get('read').acl_ref) ++ self.assertIn(api_resp.get('read').acl_ref_relative, ++ self.secret_acl_ref) ++ ++ def test_should_get_secret_acl_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/secrets/" + self.secret_uuid ++ self.test_should_get_secret_acl(bad_href) + + def test_should_get_secret_acl_with_extra_trailing_slashes(self): + self.responses.get(requests_mock.ANY, +@@ -73,16 +80,22 @@ class WhenTestingACLManager(ACLTestCase): + self.assertEqual(self.secret_acl_ref, + self.responses.last_request.url) + +- def test_should_get_container_acl(self): ++ def test_should_get_container_acl(self, entity_ref=None): ++ entity_ref = entity_ref or self.container_ref + self.responses.get(self.container_acl_ref, + json=self.get_acl_response_data()) + +- api_resp = self.manager.get(entity_ref=self.container_ref) ++ api_resp = self.manager.get(entity_ref=entity_ref) + self.assertEqual(self.container_acl_ref, + self.responses.last_request.url) + self.assertFalse(api_resp.get('read').project_access) + self.assertEqual('read', api_resp.get('read').operation_type) +- self.assertEqual(self.container_acl_ref, api_resp.get('read').acl_ref) ++ self.assertIn(api_resp.get('read').acl_ref_relative, ++ self.container_acl_ref) ++ ++ def test_should_get_container_acl_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/containers/" + self.container_uuid ++ self.test_should_get_container_acl(bad_href) + + def test_should_get_container_acl_with_trailing_slashes(self): + self.responses.get(requests_mock.ANY, +@@ -122,20 +135,26 @@ class WhenTestingACLManager(ACLTestCase): + read_acl_via_get = entity.get('read') + self.assertEqual(read_acl, read_acl_via_get) + +- def test_should_create_acl_with_users(self): +- entity = self.manager.create(entity_ref=self.container_ref + '///', ++ def test_should_create_acl_with_users(self, entity_ref=None): ++ entity_ref = entity_ref or self.container_ref ++ entity = self.manager.create(entity_ref=entity_ref + '///', + users=self.users2, project_access=False) + self.assertIsInstance(entity, acls.ContainerACL) + # entity ref is kept same as provided input. +- self.assertEqual(self.container_ref + '///', entity.entity_ref) ++ self.assertEqual(entity_ref + '///', entity.entity_ref) + + read_acl = entity.read + self.assertFalse(read_acl.project_access) + self.assertEqual(self.users2, read_acl.users) + self.assertEqual(acls.DEFAULT_OPERATION_TYPE, read_acl.operation_type) + # acl ref removes extra trailing slashes if there +- self.assertIn(self.container_ref, read_acl.acl_ref, ++ self.assertIn(entity_ref, read_acl.acl_ref, + 'ACL ref has additional /acl') ++ self.assertIn(read_acl.acl_ref_relative, self.container_acl_ref) ++ ++ def test_should_create_acl_with_users_stripped_uuid(self): ++ bad_href = "http://badsite.com/containers/" + self.container_uuid ++ self.test_should_create_acl_with_users(bad_href) + + def test_should_create_acl_with_no_users(self): + entity = self.manager.create(entity_ref=self.container_ref, users=[]) +@@ -163,18 +182,23 @@ class WhenTestingACLManager(ACLTestCase): + + class WhenTestingACLEntity(ACLTestCase): + +- def test_should_submit_acl_with_users_project_access_set(self): ++ def test_should_submit_acl_with_users_project_access_set(self, href=None): ++ href = href or self.secret_ref + data = {'acl_ref': self.secret_acl_ref} + # register put acl URI with expected acl ref in response + self.responses.put(self.secret_acl_ref, json=data) + +- entity = self.manager.create(entity_ref=self.secret_ref + '///', ++ entity = self.manager.create(entity_ref=href + '///', + users=self.users1, project_access=True) + api_resp = entity.submit() + self.assertEqual(self.secret_acl_ref, api_resp) + self.assertEqual(self.secret_acl_ref, + self.responses.last_request.url) + ++ def test_should_submit_acl_with_users_project_access_stripped_uuid(self): ++ bad_href = "http://badsite.com/secrets/" + self.secret_uuid ++ self.test_should_submit_acl_with_users_project_access_set(bad_href) ++ + def test_should_submit_acl_with_project_access_set_but_no_users(self): + data = {'acl_ref': self.secret_acl_ref} + # register put acl URI with expected acl ref in response +@@ -368,10 +392,11 @@ class WhenTestingACLEntity(ACLTestCase): + self.assertIsNone(data[4]) # updated + self.assertEqual(self.container_acl_ref, data[5]) + +- def test_should_secret_acl_remove(self): ++ def test_should_secret_acl_remove(self, entity_ref=None): ++ entity_ref = entity_ref or self.secret_ref + self.responses.delete(self.secret_acl_ref) + +- entity = self.manager.create(entity_ref=self.secret_ref, ++ entity = self.manager.create(entity_ref=entity_ref, + users=self.users2) + + api_resp = entity.remove() +@@ -391,14 +416,23 @@ class WhenTestingACLEntity(ACLTestCase): + + self.responses.delete(self.container_acl_ref) + +- def test_should_container_acl_remove(self): ++ def test_should_secret_acl_remove_stripped_uuid(self): ++ bad_href = "http://badsite.com/secrets/" + self.secret_uuid ++ self.test_should_secret_acl_remove(bad_href) ++ ++ def test_should_container_acl_remove(self, entity_ref=None): ++ entity_ref = entity_ref or self.container_ref + self.responses.delete(self.container_acl_ref) + +- entity = self.manager.create(entity_ref=self.container_ref) ++ entity = self.manager.create(entity_ref=entity_ref) + entity.remove() + self.assertEqual(self.container_acl_ref, + self.responses.last_request.url) + ++ def test_should_container_acl_remove_stripped_uuid(self): ++ bad_href = "http://badsite.com/containers/" + self.container_uuid ++ self.test_should_container_acl_remove(bad_href) ++ + def test_should_fail_acl_remove_invalid_uri(self): + # secret_acl URI expected and not secret acl URI + entity = self.manager.create(entity_ref=self.secret_acl_ref) +diff --git a/barbicanclient/tests/v1/test_cas.py b/barbicanclient/tests/v1/test_cas.py +index c97d46e..118cfdb 100644 +--- a/barbicanclient/tests/v1/test_cas.py ++++ b/barbicanclient/tests/v1/test_cas.py +@@ -55,13 +55,15 @@ class WhenTestingCAs(test_client.BaseEntityResource): + self.ca = CAData() + self.manager = self.client.cas + +- def test_should_get_lazy(self): +- data = self.ca.get_dict(self.entity_href) ++ def test_should_get_lazy(self, ca_ref=None): ++ ca_ref = ca_ref or self.entity_href ++ ++ data = self.ca.get_dict(ca_ref) + m = self.responses.get(self.entity_href, json=data) + +- ca = self.manager.get(ca_ref=self.entity_href) ++ ca = self.manager.get(ca_ref=ca_ref) + self.assertIsInstance(ca, cas.CA) +- self.assertEqual(self.entity_href, ca._ca_ref) ++ self.assertEqual(ca_ref, ca._ca_ref) + + # Verify GET wasn't called yet + self.assertFalse(m.called) +@@ -72,6 +74,13 @@ class WhenTestingCAs(test_client.BaseEntityResource): + # Verify the correct URL was used to make the GET call + self.assertEqual(self.entity_href, m.last_request.url) + ++ def test_should_get_lazy_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_get_lazy(bad_href) ++ ++ def test_should_get_lazy_using_only_uuid(self): ++ self.test_should_get_lazy(self.entity_id) ++ + def test_should_get_lazy_in_meta(self): + data = self.ca.get_dict(self.entity_href) + m = self.responses.get(self.entity_href, json=data) +diff --git a/barbicanclient/tests/v1/test_containers.py b/barbicanclient/tests/v1/test_containers.py +index dbe7547..fa49001 100644 +--- a/barbicanclient/tests/v1/test_containers.py ++++ b/barbicanclient/tests/v1/test_containers.py +@@ -367,13 +367,15 @@ class WhenTestingContainers(test_client.BaseEntityResource): + except AttributeError: + pass + +- def test_should_get_generic_container(self): +- data = self.container.get_dict(self.entity_href) ++ def test_should_get_generic_container(self, container_ref=None): ++ container_ref = container_ref or self.entity_href ++ ++ data = self.container.get_dict(container_ref) + self.responses.get(self.entity_href, json=data) + +- container = self.manager.get(container_ref=self.entity_href) ++ container = self.manager.get(container_ref=container_ref) + self.assertIsInstance(container, containers.Container) +- self.assertEqual(self.entity_href, container.container_ref) ++ self.assertEqual(container_ref, container.container_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) +@@ -414,21 +416,39 @@ class WhenTestingContainers(test_client.BaseEntityResource): + self.assertIsNotNone(container.public_key) + self.assertIsNotNone(container.private_key_passphrase) + +- def test_should_delete_from_manager(self): ++ def test_should_get_generic_container_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_get_generic_container(bad_href) ++ ++ def test_should_get_generic_container_using_only_uuid(self): ++ self.test_should_get_generic_container(self.entity_id) ++ ++ def test_should_delete_from_manager(self, container_ref=None): ++ container_ref = container_ref or self.entity_href ++ + self.responses.delete(self.entity_href, status_code=204) + +- self.manager.delete(container_ref=self.entity_href) ++ self.manager.delete(container_ref=container_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) + +- def test_should_delete_from_object(self): +- data = self.container.get_dict(self.entity_href) ++ def test_should_delete_from_manager_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete_from_manager(bad_href) ++ ++ def test_should_delete_from_manager_using_only_uuid(self): ++ self.test_should_delete_from_manager(self.entity_id) ++ ++ def test_should_delete_from_object(self, container_ref=None): ++ container_ref = container_ref or self.entity_href ++ ++ data = self.container.get_dict(container_ref) + m = self.responses.get(self.entity_href, json=data) + n = self.responses.delete(self.entity_href, status_code=204) + +- container = self.manager.get(container_ref=self.entity_href) +- self.assertIsNotNone(container.container_ref) ++ container = self.manager.get(container_ref=container_ref) ++ self.assertEqual(container_ref, container.container_ref) + + container.delete() + +@@ -439,6 +459,13 @@ class WhenTestingContainers(test_client.BaseEntityResource): + # Verify that the Container no longer has a container_ref + self.assertIsNone(container.container_ref) + ++ def test_should_delete_from_object_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete_from_object(bad_href) ++ ++ def test_should_delete_from_object_using_only_uuid(self): ++ self.test_should_delete_from_object(self.entity_id) ++ + def test_should_store_after_delete_from_object(self): + data = self.container.get_dict(self.entity_href) + self.responses.get(self.entity_href, json=data) +diff --git a/barbicanclient/tests/v1/test_orders.py b/barbicanclient/tests/v1/test_orders.py +index 0923aa8..ec1231a 100644 +--- a/barbicanclient/tests/v1/test_orders.py ++++ b/barbicanclient/tests/v1/test_orders.py +@@ -198,6 +198,34 @@ class WhenTestingKeyOrders(OrdersTestCase): + except AttributeError: + pass + ++ def test_should_delete_from_object(self, order_ref=None): ++ order_ref = order_ref or self.entity_href ++ ++ data = {'order_ref': order_ref} ++ self.responses.post(self.entity_base + '/', json=data) ++ self.responses.delete(self.entity_href, status_code=204) ++ ++ order = self.manager.create_key( ++ name='name', ++ algorithm='algorithm', ++ payload_content_type='payload_content_type' ++ ) ++ order_href = order.submit() ++ ++ self.assertEqual(order_ref, order_href) ++ ++ order.delete() ++ ++ # Verify the correct URL was used to make the call. ++ self.assertEqual(self.entity_href, self.responses.last_request.url) ++ ++ def test_should_delete_from_object_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete_from_object(bad_href) ++ ++ def test_should_delete_from_object_using_only_uuid(self): ++ self.test_should_delete_from_object(self.entity_id) ++ + + class WhenTestingAsymmetricOrders(OrdersTestCase): + +@@ -265,16 +293,25 @@ class WhenTestingAsymmetricOrders(OrdersTestCase): + + class WhenTestingOrderManager(OrdersTestCase): + +- def test_should_get(self): ++ def test_should_get(self, order_ref=None): ++ order_ref = order_ref or self.entity_href ++ + self.responses.get(self.entity_href, text=self.key_order_data) + +- order = self.manager.get(order_ref=self.entity_href) ++ order = self.manager.get(order_ref=order_ref) + self.assertIsInstance(order, orders.KeyOrder) + self.assertEqual(self.entity_href, order.order_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) + ++ def test_should_get_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_get(bad_href) ++ ++ def test_should_get_using_only_uuid(self): ++ self.test_should_get(self.entity_id) ++ + def test_should_get_invalid_meta(self): + self.responses.get(self.entity_href, text=self.key_order_invalid_data) + +@@ -300,14 +337,22 @@ class WhenTestingOrderManager(OrdersTestCase): + self.assertEqual(['10'], self.responses.last_request.qs['limit']) + self.assertEqual(['5'], self.responses.last_request.qs['offset']) + +- def test_should_delete(self): ++ def test_should_delete(self, order_ref=None): ++ order_ref = order_ref or self.entity_href + self.responses.delete(self.entity_href, status_code=204) + +- self.manager.delete(order_ref=self.entity_href) ++ self.manager.delete(order_ref=order_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) + ++ def test_should_delete_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete(bad_href) ++ ++ def test_should_delete_using_only_uuid(self): ++ self.test_should_delete(self.entity_id) ++ + def test_should_fail_delete_no_href(self): + self.assertRaises(ValueError, self.manager.delete, None) + +@@ -330,16 +375,25 @@ class WhenTestingOrderManager(OrdersTestCase): + + class WhenTestingCertificateOrders(OrdersTestCase): + +- def test_get(self): ++ def test_get(self, order_ref=None): ++ order_ref = order_ref or self.entity_href ++ + self.responses.get(self.entity_href, text=self.cert_order_data) + +- order = self.manager.get(order_ref=self.entity_href) ++ order = self.manager.get(order_ref=order_ref) + self.assertIsInstance(order, orders.CertificateOrder) + self.assertEqual(self.entity_href, order.order_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) + ++ def test_get_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_get(bad_href) ++ ++ def test_get_using_only_uuid(self): ++ self.test_get(self.entity_id) ++ + def test_repr(self): + order_args = self._get_order_args(self.cert_order_data) + order_obj = orders.CertificateOrder(api=None, **order_args) +diff --git a/barbicanclient/tests/v1/test_secrets.py b/barbicanclient/tests/v1/test_secrets.py +index 94299f5..a0300a5 100644 +--- a/barbicanclient/tests/v1/test_secrets.py ++++ b/barbicanclient/tests/v1/test_secrets.py +@@ -219,13 +219,15 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + except AttributeError: + pass + +- def test_should_get_lazy(self): +- data = self.secret.get_dict(self.entity_href) ++ def test_should_get_lazy(self, secret_ref=None): ++ secret_ref = secret_ref or self.entity_href ++ ++ data = self.secret.get_dict(secret_ref) + m = self.responses.get(self.entity_href, json=data) + +- secret = self.manager.get(secret_ref=self.entity_href) ++ secret = self.manager.get(secret_ref=secret_ref) + self.assertIsInstance(secret, secrets.Secret) +- self.assertEqual(self.entity_href, secret.secret_ref) ++ self.assertEqual(secret_ref, secret.secret_ref) + + # Verify GET wasn't called yet + self.assertFalse(m.called) +@@ -236,6 +238,13 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + # Verify the correct URL was used to make the GET call + self.assertEqual(self.entity_href, m.last_request.url) + ++ def test_should_get_lazy_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_get_lazy(bad_href) ++ ++ def test_should_get_lazy_using_only_uuid(self): ++ self.test_should_get_lazy(self.entity_id) ++ + def test_should_get_acls_lazy(self): + data = self.secret.get_dict(self.entity_href) + m = self.responses.get(self.entity_href, json=data) +@@ -388,22 +397,81 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + self.assertEqual(self.entity_payload_href, + decryption_response.last_request.url) + +- def test_should_delete(self): ++ def test_should_delete_from_manager(self, secret_ref=None): ++ secret_ref = secret_ref or self.entity_href ++ + self.responses.delete(self.entity_href, status_code=204) + +- self.manager.delete(secret_ref=self.entity_href) ++ self.manager.delete(secret_ref=secret_ref) + + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_href, self.responses.last_request.url) + +- def test_should_update(self): +- data = {'secret_ref': self.entity_href} ++ def test_should_delete_from_manager_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete_from_manager(bad_href) ++ ++ def test_should_delete_from_manager_using_only_uuid(self): ++ self.test_should_delete_from_manager(self.entity_id) ++ ++ def test_should_delete_from_object(self, secref_ref=None): ++ secref_ref = secref_ref or self.entity_href ++ data = {'secret_ref': secref_ref} ++ self.responses.post(self.entity_base + '/', json=data) ++ ++ secret = self.manager.create() ++ secret.payload = None ++ secret.store() ++ ++ # Verify the secret has the correct ref for testing deletes ++ self.assertEqual(secref_ref, secret.secret_ref) ++ ++ self.responses.delete(self.entity_href, status_code=204) ++ ++ secret.delete() ++ ++ # Verify the correct URL was used to make the call. ++ self.assertEqual(self.entity_href, self.responses.last_request.url) ++ ++ def test_should_delete_from_object_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_delete_from_object(bad_href) ++ ++ def test_should_delete_from_object_using_only_uuid(self): ++ self.test_should_delete_from_object(self.entity_id) ++ ++ def test_should_update_from_manager(self, secret_ref=None): ++ # This literal will have type(unicode) in Python 2, but will have ++ # type(str) in Python 3. It is six.text_type in both cases. ++ text_payload = u'time for an ice cold \U0001f37a' ++ secret_ref = secret_ref or self.entity_href ++ ++ self.responses.put(self.entity_href, status_code=204) ++ ++ self.manager.update(secret_ref=secret_ref, payload=text_payload) ++ ++ # Verify the correct URL was used to make the call. ++ self.assertEqual(self.entity_href, self.responses.last_request.url) ++ ++ def test_should_update_from_manager_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_update_from_manager(bad_href) ++ ++ def test_should_update_from_manager_using_only_uuid(self): ++ self.test_should_update_from_manager(self.entity_id) ++ ++ def test_should_update_from_object(self, secref_ref=None): ++ secref_ref = secref_ref or self.entity_href ++ data = {'secret_ref': secref_ref} + self.responses.post(self.entity_base + '/', json=data) + + secret = self.manager.create() + secret.payload = None + secret.store() + ++ # Verify the secret has the correct ref for testing updates ++ self.assertEqual(secref_ref, secret.secret_ref) ++ + # This literal will have type(unicode) in Python 2, but will have + # type(str) in Python 3. It is six.text_type in both cases. + text_payload = u'time for an ice cold \U0001f37a' +@@ -419,6 +487,13 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + # Verify that the data has been updated + self.assertEqual(text_payload, secret.payload) + ++ def test_should_update_from_object_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_update_from_object(bad_href) ++ ++ def test_should_update_from_object_using_only_uuid(self): ++ self.test_should_update_from_object(self.entity_id) ++ + def test_should_get_list(self): + secret_resp = self.secret.get_dict(self.entity_href) + +diff --git a/barbicanclient/v1/acls.py b/barbicanclient/v1/acls.py +index 3968ce0..7724048 100644 +--- a/barbicanclient/v1/acls.py ++++ b/barbicanclient/v1/acls.py +@@ -85,10 +85,18 @@ class _PerOperationACL(ACLFormatter): + def acl_ref(self): + return ACL.get_acl_ref_from_entity_ref(self.entity_ref) + ++ @property ++ def acl_ref_relative(self): ++ return self._parent_acl.acl_ref_relative ++ + @property + def entity_ref(self): + return self._entity_ref + ++ @property ++ def entity_uuid(self): ++ return self._parent_acl.entity_uuid ++ + @property + def project_access(self): + """Flag indicating project access behavior is enabled or not""" +@@ -203,6 +211,12 @@ class ACL(object): + """Entity URI reference.""" + return self._entity_ref + ++ @property ++ def entity_uuid(self): ++ """Entity UUID""" ++ return str(base.validate_ref_and_return_uuid( ++ self._entity_ref, self._acl_type)) ++ + @property + def operation_acls(self): + """List of operation specific ACL settings.""" +@@ -212,6 +226,11 @@ class ACL(object): + def acl_ref(self): + return ACL.get_acl_ref_from_entity_ref(self.entity_ref) + ++ @property ++ def acl_ref_relative(self): ++ return ACL.get_acl_ref_from_entity_ref_relative( ++ self.entity_uuid, self._parent_entity_path) ++ + def add_operation_acl(self, users=None, project_access=None, + operation_type=None, created=None, + updated=None,): +@@ -301,7 +320,7 @@ class ACL(object): + acl_data['users'] = per_op_acl.users + acl_dict[op_type] = acl_data + +- response = self._api.put(self.acl_ref, json=acl_dict) ++ response = self._api.put(self.acl_ref_relative, json=acl_dict) + + return response.json().get('acl_ref') + +@@ -314,7 +333,7 @@ class ACL(object): + self.validate_input_ref() + LOG.debug('Removing ACL for {0} for href: {1}' + .format(self.acl_type, self.entity_ref)) +- self._api.delete(self.acl_ref) ++ self._api.delete(self.acl_ref_relative) + + def load_acls_data(self): + """Loads ACL entity from Barbican server using its acl_ref +@@ -328,7 +347,7 @@ class ACL(object): + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + +- response = self._api.get(self.acl_ref) ++ response = self._api.get(self.acl_ref_relative) + + del self.operation_acls[:] # clearing list for all of its references + for op_type in response: +@@ -354,7 +373,7 @@ class ACL(object): + else: + raise ValueError('{0} URI is not specified.'.format(res_title)) + +- base.validate_ref(self.entity_ref, ref_type) ++ base.validate_ref_and_return_uuid(self.entity_ref, ref_type) + return ref_type + + @staticmethod +@@ -364,6 +383,14 @@ class ACL(object): + entity_ref = entity_ref.rstrip('/') + return '{0}/{1}'.format(entity_ref, ACL._resource_name) + ++ @staticmethod ++ def get_acl_ref_from_entity_ref_relative(entity_ref, entity_type): ++ # Utility for converting entity ref to acl ref ++ if entity_ref: ++ entity_ref = entity_ref.rstrip('/') ++ return '{0}/{1}/{2}'.format(entity_type, entity_ref, ++ ACL._resource_name) ++ + @staticmethod + def identify_ref_type(entity_ref): + # Utility for identifying ACL type from given entity URI. +diff --git a/barbicanclient/v1/cas.py b/barbicanclient/v1/cas.py +index d076e4b..6a8f541 100644 +--- a/barbicanclient/v1/cas.py ++++ b/barbicanclient/v1/cas.py +@@ -171,7 +171,8 @@ class CA(CAFormatter): + + def _fill_lazy_properties(self): + if self._ca_ref and not self._plugin_name: +- result = self._api.get(self._ca_ref) ++ uuid_ref = base.calculate_uuid_ref(self._ca_ref, self._entity) ++ result = self._api.get(uuid_ref) + self._fill_from_data( + meta=result.get('meta'), + expiration=result.get('expiration'), +@@ -205,7 +206,7 @@ class CAManager(base.BaseEntityManager): + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + LOG.debug("Getting ca - CA href: {0}".format(ca_ref)) +- base.validate_ref(ca_ref, 'CA') ++ base.validate_ref_and_return_uuid(ca_ref, 'CA') + return CA( + api=self._api, + ca_ref=ca_ref +diff --git a/barbicanclient/v1/containers.py b/barbicanclient/v1/containers.py +index 45cdf48..4c362ab 100644 +--- a/barbicanclient/v1/containers.py ++++ b/barbicanclient/v1/containers.py +@@ -211,7 +211,9 @@ class Container(ContainerFormatter): + def delete(self): + """Delete container from Barbican""" + if self._container_ref: +- self._api.delete(self._container_ref) ++ uuid_ref = base.calculate_uuid_ref(self._container_ref, ++ self._entity) ++ self._api.delete(uuid_ref) + self._container_ref = None + self._status = None + self._created = None +@@ -235,9 +237,10 @@ class Container(ContainerFormatter): + raise AttributeError("container_ref not set, cannot reload data.") + LOG.debug('Getting container - Container href: {0}' + .format(self._container_ref)) +- base.validate_ref(self._container_ref, 'Container') ++ uuid_ref = base.calculate_uuid_ref(self._container_ref, ++ self._entity) + try: +- response = self._api.get(self._container_ref) ++ response = self._api.get(uuid_ref) + except AttributeError: + raise LookupError('Container {0} could not be found.' + .format(self._container_ref)) +@@ -530,14 +533,14 @@ class ContainerManager(base.BaseEntityManager): + def get(self, container_ref): + """Retrieve an existing Container from Barbican + +- :param str container_ref: Full HATEOAS reference to a Container ++ :param container_ref: Full HATEOAS reference to a Container, or a UUID + :returns: Container object or a subclass of the appropriate type + """ + LOG.debug('Getting container - Container href: {0}' + .format(container_ref)) +- base.validate_ref(container_ref, 'Container') ++ uuid_ref = base.calculate_uuid_ref(container_ref, self._entity) + try: +- response = self._api.get(container_ref) ++ response = self._api.get(uuid_ref) + except AttributeError: + raise LookupError('Container {0} could not be found.' + .format(container_ref)) +@@ -688,14 +691,15 @@ class ContainerManager(base.BaseEntityManager): + def delete(self, container_ref): + """Delete a Container from Barbican + +- :param container_ref: Full HATEOAS reference to a Container ++ :param container_ref: Full HATEOAS reference to a Container, or a UUID + :raises barbicanclient.exceptions.HTTPAuthError: 401 Responses + :raises barbicanclient.exceptions.HTTPClientError: 4xx Responses + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + if not container_ref: + raise ValueError('container_ref is required.') +- self._api.delete(container_ref) ++ uuid_ref = base.calculate_uuid_ref(container_ref, self._entity) ++ self._api.delete(uuid_ref) + + def list(self, limit=10, offset=0, name=None, type=None): + """List containers for the project. +@@ -728,7 +732,7 @@ class ContainerManager(base.BaseEntityManager): + def register_consumer(self, container_ref, name, url): + """Add a consumer to the container + +- :param container_ref: Full HATEOAS reference to a Container ++ :param container_ref: Full HATEOAS reference to a Container, or a UUID + :param name: Name of the consuming service + :param url: URL of the consuming resource + :returns: A container object per the get() method +@@ -738,8 +742,9 @@ class ContainerManager(base.BaseEntityManager): + """ + LOG.debug('Creating consumer registration for container ' + '{0} as {1}: {2}'.format(container_ref, name, url)) +- href = '{0}/{1}/consumers'.format(self._entity, +- container_ref.split('/')[-1]) ++ container_uuid = base.validate_ref_and_return_uuid( ++ container_ref, 'Container') ++ href = '{0}/{1}/consumers'.format(self._entity, container_uuid) + consumer_dict = dict() + consumer_dict['name'] = name + consumer_dict['URL'] = url +@@ -750,7 +755,7 @@ class ContainerManager(base.BaseEntityManager): + def remove_consumer(self, container_ref, name, url): + """Remove a consumer from the container + +- :param container_ref: Full HATEOAS reference to a Container ++ :param container_ref: Full HATEOAS reference to a Container, or a UUID + :param name: Name of the previously consuming service + :param url: URL of the previously consuming resource + :raises barbicanclient.exceptions.HTTPAuthError: 401 Responses +@@ -759,8 +764,9 @@ class ContainerManager(base.BaseEntityManager): + """ + LOG.debug('Deleting consumer registration for container ' + '{0} as {1}: {2}'.format(container_ref, name, url)) +- href = '{0}/{1}/consumers'.format(self._entity, +- container_ref.split('/')[-1]) ++ container_uuid = base.validate_ref_and_return_uuid( ++ container_ref, 'Container') ++ href = '{0}/{1}/consumers'.format(self._entity, container_uuid) + consumer_dict = { + 'name': name, + 'URL': url +diff --git a/barbicanclient/v1/orders.py b/barbicanclient/v1/orders.py +index 3b62f97..15eca81 100644 +--- a/barbicanclient/v1/orders.py ++++ b/barbicanclient/v1/orders.py +@@ -241,7 +241,8 @@ class Order(object): + def delete(self): + """Deletes the Order from Barbican""" + if self._order_ref: +- self._api.delete(self._order_ref) ++ uuid_ref = base.calculate_uuid_ref(self._order_ref, self._entity) ++ self._api.delete(uuid_ref) + self._order_ref = None + else: + raise LookupError("Order is not yet stored.") +@@ -388,16 +389,16 @@ class OrderManager(base.BaseEntityManager): + def get(self, order_ref): + """Retrieve an existing Order from Barbican + +- :param order_ref: Full HATEOAS reference to an Order ++ :param order_ref: Full HATEOAS reference to an Order, or a UUID + :returns: An instance of the appropriate subtype of Order + :raises barbicanclient.exceptions.HTTPAuthError: 401 Responses + :raises barbicanclient.exceptions.HTTPClientError: 4xx Responses + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + LOG.debug("Getting order - Order href: {0}".format(order_ref)) +- base.validate_ref(order_ref, 'Order') ++ uuid_ref = base.calculate_uuid_ref(order_ref, self._entity) + try: +- response = self._api.get(order_ref) ++ response = self._api.get(uuid_ref) + except AttributeError: + raise LookupError( + 'Order {0} could not be found.'.format(order_ref) +@@ -518,11 +519,12 @@ class OrderManager(base.BaseEntityManager): + def delete(self, order_ref): + """Delete an Order from Barbican + +- :param order_ref: The href for the order ++ :param order_ref: Full HATEOAS reference to an Order, or a UUID + """ + if not order_ref: + raise ValueError('order_ref is required.') +- self._api.delete(order_ref) ++ uuid_ref = base.calculate_uuid_ref(order_ref, self._entity) ++ self._api.delete(uuid_ref) + + def list(self, limit=10, offset=0): + """List Orders for the project +diff --git a/barbicanclient/v1/secrets.py b/barbicanclient/v1/secrets.py +index c2577d0..5b0c4a1 100644 +--- a/barbicanclient/v1/secrets.py ++++ b/barbicanclient/v1/secrets.py +@@ -357,14 +357,16 @@ class Secret(SecretFormatter): + else: + raise exceptions.PayloadException("Invalid Payload Type") + +- self._api.put(self._secret_ref, ++ uuid_ref = base.calculate_uuid_ref(self._secret_ref, self._entity) ++ self._api.put(uuid_ref, + headers=headers, + data=self.payload) + + def delete(self): + """Deletes the Secret from Barbican""" + if self._secret_ref: +- self._api.delete(self._secret_ref) ++ uuid_ref = base.calculate_uuid_ref(self._secret_ref, self._entity) ++ self._api.delete(uuid_ref) + self._secret_ref = None + else: + raise LookupError("Secret is not yet stored.") +@@ -411,7 +413,8 @@ class Secret(SecretFormatter): + + def _fill_lazy_properties(self): + if self._secret_ref and not self._name: +- result = self._api.get(self._secret_ref) ++ uuid_ref = base.calculate_uuid_ref(self._secret_ref, self._entity) ++ result = self._api.get(uuid_ref) + self._fill_from_data( + name=result.get('name'), + expiration=result.get('expiration'), +@@ -444,7 +447,7 @@ class SecretManager(base.BaseEntityManager): + def get(self, secret_ref, payload_content_type=None): + """Retrieve an existing Secret from Barbican + +- :param str secret_ref: Full HATEOAS reference to a Secret ++ :param str secret_ref: Full HATEOAS reference to a Secret, or a UUID + :param str payload_content_type: DEPRECATED: Content type to use for + payload decryption. Setting this can lead to unexpected results. + See Launchpad Bug #1419166. +@@ -455,7 +458,7 @@ class SecretManager(base.BaseEntityManager): + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + LOG.debug("Getting secret - Secret href: {0}".format(secret_ref)) +- base.validate_ref(secret_ref, 'Secret') ++ base.validate_ref_and_return_uuid(secret_ref, 'Secret') + return Secret( + api=self._api, + payload_content_type=payload_content_type, +@@ -463,16 +466,16 @@ class SecretManager(base.BaseEntityManager): + ) + + def update(self, secret_ref, payload=None): +- """Update an existing Secret from Barbican ++ """Update an existing Secret in Barbican + +- :param str secret_ref: Full HATEOAS reference to a Secret ++ :param str secret_ref: Full HATEOAS reference to a Secret, or a UUID + :param str payload: New payload to add to secret + :raises barbicanclient.exceptions.HTTPAuthError: 401 Responses + :raises barbicanclient.exceptions.HTTPClientError: 4xx Responses + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ + +- base.validate_ref(secret_ref, 'Secret') ++ base.validate_ref_and_return_uuid(secret_ref, 'Secret') + if not secret_ref: + raise ValueError('secret_ref is required.') + +@@ -483,7 +486,8 @@ class SecretManager(base.BaseEntityManager): + else: + raise exceptions.PayloadException("Invalid Payload Type") + +- self._api.put(secret_ref, ++ uuid_ref = base.calculate_uuid_ref(secret_ref, self._entity) ++ self._api.put(uuid_ref, + headers=headers, + data=payload) + +@@ -524,15 +528,16 @@ class SecretManager(base.BaseEntityManager): + def delete(self, secret_ref): + """Delete a Secret from Barbican + +- :param secret_ref: The href for the secret to be deleted ++ :param secret_ref: Full HATEOAS reference to a Secret, or a UUID + :raises barbicanclient.exceptions.HTTPAuthError: 401 Responses + :raises barbicanclient.exceptions.HTTPClientError: 4xx Responses + :raises barbicanclient.exceptions.HTTPServerError: 5xx Responses + """ +- base.validate_ref(secret_ref, 'Secret') ++ base.validate_ref_and_return_uuid(secret_ref, 'Secret') + if not secret_ref: + raise ValueError('secret_ref is required.') +- self._api.delete(secret_ref) ++ uuid_ref = base.calculate_uuid_ref(secret_ref, self._entity) ++ self._api.delete(uuid_ref) + + def list(self, limit=10, offset=0, name=None, algorithm=None, mode=None, + bits=0, secret_type=None, created=None, updated=None, +diff --git a/releasenotes/notes/use-only-uuid-from-href-81710d1b388dce57.yaml b/releasenotes/notes/use-only-uuid-from-href-81710d1b388dce57.yaml +new file mode 100644 +index 0000000..97e135c +--- /dev/null ++++ b/releasenotes/notes/use-only-uuid-from-href-81710d1b388dce57.yaml +@@ -0,0 +1,8 @@ ++--- ++features: ++ - | ++ Lookups (for all Read/Update/Delete actions) are now performed using only ++ the UUID of the entity. For backward compatability, full HATEOS refs may ++ be used, but everything before the UUID will be stripped and the service ++ catalog entry for Barbican will be substituted. This should have no impact ++ on accessing existing secrets with any version of Barbican. +diff --git a/tox.ini b/tox.ini +index df458b5..137d3db 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -16,6 +16,7 @@ commands = + coverage erase + python setup.py testr --coverage --testr-args='{posargs}' + coverage report -m ++whitelist_externals = rm + + [testenv:debug] + commands = oslo_debug_helper -t barbicanclient/tests {posargs} +-- +2.20.1 + diff -Nru python-barbicanclient-4.6.0/debian/patches/0002-Secret-payload-should-also-be-fetched-by-UUID.patch python-barbicanclient-4.6.0/debian/patches/0002-Secret-payload-should-also-be-fetched-by-UUID.patch --- python-barbicanclient-4.6.0/debian/patches/0002-Secret-payload-should-also-be-fetched-by-UUID.patch 1969-12-31 21:00:00.000000000 -0300 +++ python-barbicanclient-4.6.0/debian/patches/0002-Secret-payload-should-also-be-fetched-by-UUID.patch 2020-03-17 11:12:22.000000000 -0300 @@ -0,0 +1,78 @@ +From e12aa561572d32e643c6dc46a1fa7576370e2f00 Mon Sep 17 00:00:00 2001 +From: Adam Harwell +Date: Thu, 20 Dec 2018 14:54:25 -0800 +Subject: [PATCH] Secret payload should also be fetched by UUID + +We changed the client to fetch containers and secrets via their UUID +from the API, rather than by HREF, so that the endpoint URLs set in the +keystone client would be respected. Unfortunately, we (I) missed +updating the payload fetch function to do the same. This brings it into +line with the other fetches. + +Change-Id: Ic71cf6771563d669a2fa37a56d4b40c637db1511 +Story: 2004653 +Task: 28608 +(cherry picked from commit 4eec7121b39de3849b469c56d85b95520aab7bad) +--- + barbicanclient/tests/v1/test_secrets.py | 12 +++++++++--- + barbicanclient/v1/secrets.py | 6 ++---- + 2 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/barbicanclient/tests/v1/test_secrets.py b/barbicanclient/tests/v1/test_secrets.py +index a0300a5..748ec61 100644 +--- a/barbicanclient/tests/v1/test_secrets.py ++++ b/barbicanclient/tests/v1/test_secrets.py +@@ -371,9 +371,11 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + # Verify the correct URL was used to make the call. + self.assertEqual(self.entity_payload_href, m.last_request.url) + +- def test_should_decrypt(self): ++ def test_should_decrypt(self, secret_ref=None): ++ secret_ref = secret_ref or self.entity_href ++ + content_types_dict = {'default': 'application/octet-stream'} +- json = self.secret.get_dict(self.entity_href, content_types_dict) ++ json = self.secret.get_dict(secret_ref, content_types_dict) + metadata_response = self.responses.get( + self.entity_href, + request_headers={'Accept': 'application/json'}, +@@ -386,7 +388,7 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + request_headers=request_headers, + content=decrypted) + +- secret = self.manager.get(secret_ref=self.entity_href) ++ secret = self.manager.get(secret_ref=secret_ref) + secret_payload = secret.payload + self.assertEqual(decrypted, secret_payload) + +@@ -397,6 +399,10 @@ class WhenTestingSecrets(test_client.BaseEntityResource): + self.assertEqual(self.entity_payload_href, + decryption_response.last_request.url) + ++ def test_should_decrypt_using_stripped_uuid(self): ++ bad_href = "http://badsite.com/" + self.entity_id ++ self.test_should_decrypt(bad_href) ++ + def test_should_delete_from_manager(self, secret_ref=None): + secret_ref = secret_ref or self.entity_href + +diff --git a/barbicanclient/v1/secrets.py b/barbicanclient/v1/secrets.py +index 5b0c4a1..a604160 100644 +--- a/barbicanclient/v1/secrets.py ++++ b/barbicanclient/v1/secrets.py +@@ -266,10 +266,8 @@ class Secret(SecretFormatter): + "content-type.") + headers = {'Accept': self.payload_content_type} + +- if self._secret_ref[-1] != "/": +- payload_url = self._secret_ref + '/payload' +- else: +- payload_url = self._secret_ref + 'payload' ++ uuid_ref = base.calculate_uuid_ref(self._secret_ref, self._entity) ++ payload_url = uuid_ref + '/payload' + payload = self._api._get_raw(payload_url, headers=headers) + if self.payload_content_type == u'text/plain': + self._payload = payload.decode('UTF-8') +-- +2.20.1 + diff -Nru python-barbicanclient-4.6.0/debian/patches/series python-barbicanclient-4.6.0/debian/patches/series --- python-barbicanclient-4.6.0/debian/patches/series 1969-12-31 21:00:00.000000000 -0300 +++ python-barbicanclient-4.6.0/debian/patches/series 2020-03-17 11:13:19.000000000 -0300 @@ -0,0 +1,2 @@ +0001-Allow-fetching-by-UUID-and-respect-interface.patch +0002-Secret-payload-should-also-be-fetched-by-UUID.patch