commit python-castellan for openSUSE:Factory
Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-castellan for openSUSE:Factory checked in at 2024-06-05 17:41:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-castellan (Old) and /work/SRC/openSUSE:Factory/.python-castellan.new.24587 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-castellan" Wed Jun 5 17:41:54 2024 rev:18 rq:1178601 version:5.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-castellan/python-castellan.changes 2024-01-05 21:43:09.090093425 +0100 +++ /work/SRC/openSUSE:Factory/.python-castellan.new.24587/python-castellan.changes 2024-06-05 17:42:29.078289978 +0200 @@ -1,0 +2,12 @@ +Tue Jun 4 20:01:04 UTC 2024 - cloud-devel@suse.de + +- update to version 5.0.0 + - reno: Update master for unmaintained/yoga + - Update python classifier in setup.cfg + - Bump hacking + - Update master for stable/2023.2 + - tox: Drop envdir + - Add release note about new consumer interfaces + - Raise ManagedObjectNotFoundError in MockKeyManager + +------------------------------------------------------------------- Old: ---- castellan-4.3.0.tar.gz New: ---- castellan-5.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-castellan.spec ++++++ --- /var/tmp/diff_new_pack.MtaQhe/_old 2024-06-05 17:42:29.794316054 +0200 +++ /var/tmp/diff_new_pack.MtaQhe/_new 2024-06-05 17:42:29.798316200 +0200 @@ -17,13 +17,13 @@ Name: python-castellan -Version: 4.3.0 +Version: 5.0.0 Release: 0 Summary: Generic Key Manager interface for OpenStack License: Apache-2.0 Group: Development/Languages/Python URL: https://docs.openstack.org/castellan -Source0: https://files.pythonhosted.org/packages/source/c/castellan/castellan-4.3.0.t... +Source0: https://files.pythonhosted.org/packages/source/c/castellan/castellan-5.0.0.t... BuildRequires: openstack-macros BuildRequires: python3-barbicanclient >= 5.5.0 BuildRequires: python3-cryptography >= 2.7 @@ -74,7 +74,7 @@ This package contains the documentation %prep -%autosetup -p1 -n castellan-4.3.0 +%autosetup -p1 -n castellan-5.0.0 %py_req_cleanup %build ++++++ castellan-4.3.0.tar.gz -> castellan-5.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/.pre-commit-config.yaml new/castellan-5.0.0/.pre-commit-config.yaml --- old/castellan-4.3.0/.pre-commit-config.yaml 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/.pre-commit-config.yaml 2024-03-05 10:56:45.000000000 +0100 @@ -32,7 +32,7 @@ - id: flake8 name: flake8 additional_dependencies: - - hacking>=3.0.1,<3.1.0 + - hacking>=6.1.0,<6.2.0 language: python entry: flake8 files: '^.*\.py$' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/AUTHORS new/castellan-5.0.0/AUTHORS --- old/castellan-4.3.0/AUTHORS 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/AUTHORS 2024-03-05 10:57:16.000000000 +0100 @@ -17,7 +17,6 @@ Doug Hellmann <doug@doughellmann.com> Douglas Mendizábal <dmendiza@redhat.com> Douglas Mendizábal <mail@doug.gt> -Dr. Jens Harbott <harbott@osism.tech> Dung Ha <dunght@vn.fujitsu.com> Ellen Batbouta <ellen.batbouta@oracle.com> Elod Illes <elod.illes@est.tech> @@ -64,6 +63,7 @@ Steve Martinelli <s.martinelli@gmail.com> Sungjin Yook <sungyook@us.ibm.com> Swapnil Kulkarni (coolsvap) <me@coolsvap.net> +Takashi Kajinami <kajinamit@oss.nttdata.com> Takashi Kajinami <tkajinam@redhat.com> Tim Kelsey <tim.kelsey@hp.com> Tom Cocozzello <tjcocozz@us.ibm.com> @@ -81,6 +81,7 @@ kuangcx <kuangcx@inspur.com> lioplhp <hpliu5898@fiberhome.com> liujiong <liujiong@gohighsec.com> +melanie witt <melwittt@gmail.com> melissaml <ma.lei@99cloud.net> rajat29 <rajat.sharma@nectechnologies.in> ramboman <lijie@unitedstack.com> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/ChangeLog new/castellan-5.0.0/ChangeLog --- old/castellan-4.3.0/ChangeLog 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/ChangeLog 2024-03-05 10:57:16.000000000 +0100 @@ -1,16 +1,20 @@ CHANGES ======= -4.3.0 +5.0.0 ----- -* Revert "Add secret consumers" -* Revert "Implement force parameter" -* Revert "Add secret consumers functional tests" -* Revert "Add force parameter functional tests" -* Revert "Add secret consumers documentation and release note" -* Update TOX\_CONSTRAINTS\_FILE for stable/2023.2 -* Update .gitreview for stable/2023.2 +* Add release note about new consumer interfaces + +4.4.0 +----- + +* Raise ManagedObjectNotFoundError in MockKeyManager +* reno: Update master for unmaintained/yoga +* tox: Drop envdir +* Bump hacking +* Update python classifier in setup.cfg +* Update master for stable/2023.2 4.2.0 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/PKG-INFO new/castellan-5.0.0/PKG-INFO --- old/castellan-4.3.0/PKG-INFO 2023-09-22 16:47:32.592500000 +0200 +++ new/castellan-5.0.0/PKG-INFO 2024-03-05 10:57:16.653267600 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: castellan -Version: 4.3.0 +Version: 5.0.0 Summary: Generic Key Manager interface for OpenStack Home-page: https://docs.openstack.org/castellan/latest/ Author: OpenStack @@ -37,6 +37,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/_config_driver.py new/castellan-5.0.0/castellan/_config_driver.py --- old/castellan-4.3.0/castellan/_config_driver.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/_config_driver.py 2024-03-05 10:56:45.000000000 +0100 @@ -95,7 +95,7 @@ class CastellanConfigurationSource(sources.ConfigurationSource): - """A configuration source for configuration values served through castellan. + """A configuration source for configuration values served through castellan. # noqa :param config_file: The path to a castellan configuration file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/common/objects/managed_object.py new/castellan-5.0.0/castellan/common/objects/managed_object.py --- old/castellan-4.3.0/castellan/common/objects/managed_object.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/common/objects/managed_object.py 2024-03-05 10:56:45.000000000 +0100 @@ -40,7 +40,7 @@ self._name = name # If None or POSIX times - if not created or type(created) == int: + if not created or isinstance(created, int): self._created = created else: raise ValueError('created must be of long type, actual type %s' % diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/key_manager/barbican_key_manager.py new/castellan-5.0.0/castellan/key_manager/barbican_key_manager.py --- old/castellan-4.3.0/castellan/key_manager/barbican_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/key_manager/barbican_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -37,7 +37,6 @@ from castellan.i18n import _ from castellan.key_manager import key_manager - from barbicanclient import client as barbican_client_import from barbicanclient import exceptions as barbican_exceptions from oslo_utils import timeutils @@ -160,7 +159,6 @@ self._base_url = self._create_base_url(auth, sess, self._barbican_endpoint) - return self._barbican_client def _get_keystone_auth(self, context): @@ -552,17 +550,20 @@ created = calendar.timegm(time_stamp) if issubclass(secret_type, key_base_class.Key): - return secret_type(secret.algorithm, - secret.bit_length, - secret_data, - secret.name, - created, - object_id) + return secret_type(algorithm=secret.algorithm, + bit_length=secret.bit_length, + key=secret_data, + name=secret.name, + created=created, + id=object_id, + consumers=secret.consumers) else: + # Opaque Data or Passphrase return secret_type(secret_data, - secret.name, - created, - object_id) + name=secret.name, + created=created, + id=object_id, + consumers=secret.consumers) def _get_secret(self, context, object_id): """Returns the metadata of the secret. @@ -618,20 +619,23 @@ else: raise exception.KeyManagerError(reason=e) - def delete(self, context, managed_object_id): + def delete(self, context, managed_object_id, force=False): """Deletes the specified managed object. :param context: contains information of the user and the environment for the request (castellan/context.py) :param managed_object_id: the UUID of the object to delete + :param force: specifies if the secret must be deleted even when they + have consumers. + :raises ValueError: if the secret has consumers but no force parameter + is provided or if force equals False. :raises KeyManagerError: if object deletion fails :raises ManagedObjectNotFoundError: if the object could not be found """ barbican_client = self._get_barbican_client(context) - try: secret_ref = self._create_secret_ref(managed_object_id) - barbican_client.secrets.delete(secret_ref) + barbican_client.secrets.delete(secret_ref, force) except (barbican_exceptions.HTTPAuthError, barbican_exceptions.HTTPClientError, barbican_exceptions.HTTPServerError) as e: @@ -639,6 +643,50 @@ if self._is_secret_not_found_error(e): raise exception.ManagedObjectNotFoundError( uuid=managed_object_id) + else: + raise exception.KeyManagerError(reason=e) + + def add_consumer(self, context, managed_object_id, consumer_data): + """Add a consumer to the specified managed object + + :param context: contains information of the user and the environment + for the request (castellan/context.py) + :param managed_object_id: the UUID of the object to update + :param consumer_data: dict containing consumer data + :raises KeyManagerError: if object deletion fails + :raises ManagedObjectNotFoundError: if the object could not be found + """ + + barbican_client = self._get_barbican_client(context) + try: + secret_ref = self._create_secret_ref(managed_object_id) + barbican_client.secrets.register_consumer( + secret_ref, **consumer_data) + + except (barbican_exceptions.HTTPAuthError, + barbican_exceptions.HTTPClientError, + barbican_exceptions.HTTPServerError) as e: + LOG.error("Error adding consumer: %s", e) + if self._is_secret_not_found_error(e): + raise exception.ManagedObjectNotFoundError( + uuid=managed_object_id) + else: + raise exception.KeyManagerError(reason=e) + + def remove_consumer(self, context, managed_object_id, consumer_data): + + barbican_client = self._get_barbican_client(context) + try: + secret_ref = self._create_secret_ref(managed_object_id) + barbican_client.secrets.remove_consumer( + secret_ref, **consumer_data) + except (barbican_exceptions.HTTPAuthError, + barbican_exceptions.HTTPClientError, + barbican_exceptions.HTTPServerError) as e: + LOG.error("Error removing consumer: %s", e) + if self._is_secret_not_found_error(e): + raise exception.ManagedObjectNotFoundError( + uuid=managed_object_id) else: raise exception.KeyManagerError(reason=e) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/key_manager/key_manager.py new/castellan-5.0.0/castellan/key_manager/key_manager.py --- old/castellan-4.3.0/castellan/key_manager/key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/key_manager/key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -106,7 +106,7 @@ pass @abc.abstractmethod - def delete(self, context, managed_object_id): + def delete(self, context, managed_object_id, force=False): """Deletes the specified managed object. Implementations should verify that the caller has permission to delete @@ -116,6 +116,44 @@ If the specified object does not exist, then a KeyError should be raised. Implementations should preclude users from discerning the + UUIDs of objects that belong to other users by repeatedly calling this + method. That is, objects that belong to other users should be + considered "non-existent" and completely invisible. + + Implementations that block the deletion of secrets with consumers + without making the "force" parameter equals True should raise + an exception. + """ + pass + + @abc.abstractmethod + def add_consumer(self, context, managed_object_id, consumer_data): + """Add a consumer to a managed object. + + Implementations should verify that the caller has permission to + add a consumer to the managed object by checking the context object + (context). A NotAuthorized exception should be raised if the caller + lacks permission. + + If the specified object does not exist, then a KeyError should be + raised. Implementations should preclude users from discerning the + UUIDs of objects that belong to other users by repeatedly calling this + method. That is, objects that belong to other users should be + considered "non-existent" and completely invisible. + """ + pass + + @abc.abstractmethod + def remove_consumer(self, context, managed_object_id, consumer_data): + """Remove a consumer from a managed object. + + Implementations should verify that the caller has permission to + remove a consumer to the managed object by checking the context object + (context). A NotAuthorized exception should be raised if the caller + lacks permission. + + If the specified object does not exist, then a KeyError should be + raised. Implementations should preclude users from discerning the UUIDs of objects that belong to other users by repeatedly calling this method. That is, objects that belong to other users should be considered "non-existent" and completely invisible. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/key_manager/not_implemented_key_manager.py new/castellan-5.0.0/castellan/key_manager/not_implemented_key_manager.py --- old/castellan-4.3.0/castellan/key_manager/not_implemented_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/key_manager/not_implemented_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -48,5 +48,11 @@ def list(self, context, object_type=None): raise NotImplementedError() - def delete(self, context, managed_object_id, **kwargs): + def delete(self, context, managed_object_id, force=False): + raise NotImplementedError() + + def add_consumer(self, context, managed_object_id, consumer_data): + raise NotImplementedError() + + def remove_consumer(self, context, managed_object_id, consumer_data): raise NotImplementedError() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/key_manager/vault_key_manager.py new/castellan-5.0.0/castellan/key_manager/vault_key_manager.py --- old/castellan-4.3.0/castellan/key_manager/vault_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/key_manager/vault_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -347,8 +347,12 @@ record['created'], key_id) - def delete(self, context, key_id): - """Represents deleting the key.""" + def delete(self, context, key_id, force=False): + """Represents deleting the key. + + The 'force' parameter is not used whatsoever and only kept to allow + consistency with the Barbican implementation. + """ if not key_id: raise exception.KeyManagerError('key identifier not provided') @@ -359,6 +363,16 @@ if resp.status_code == requests.codes['not_found']: raise exception.ManagedObjectNotFoundError(uuid=key_id) + def add_consumer(self, context, managed_object_id, consumer_data): + raise NotImplementedError( + "VaultKeyManager does not implement adding consumers" + ) + + def remove_consumer(self, context, managed_object_id, consumer_data): + raise NotImplementedError( + "VaultKeyManager does not implement deleting consumers" + ) + def list(self, context, object_type=None, metadata_only=False): """Lists the managed objects given the criteria.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/tests/functional/key_manager/test_barbican_key_manager.py new/castellan-5.0.0/castellan/tests/functional/key_manager/test_barbican_key_manager.py --- old/castellan-4.3.0/castellan/tests/functional/key_manager/test_barbican_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/tests/functional/key_manager/test_barbican_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -34,11 +34,13 @@ from castellan.key_manager import barbican_key_manager from castellan.tests.functional import config from castellan.tests.functional.key_manager import test_key_manager +from castellan.tests import utils CONF = config.get_config() +@utils.parameterized_test_case class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase): def _create_key_manager(self): @@ -108,6 +110,291 @@ self.assertRaises(exception.Forbidden, self.key_mgr.store, None, key) + def test_secret_create_check_empty_consumers_list(self): + """Check that the consumers entity is a list and is empty.""" + + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + resp = self.key_mgr.get(self.ctxt, stored_id) + consumers = resp.consumers + self.assertIsInstance(consumers, list) + self.assertEqual(len(consumers), 0) + + def test_secret_create_check_consumers_list_consistency(self): + """Consumers List Consistency + + Check that the consumers list contains a single element, + and that it corresponds to the consumer created. + """ + + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + resource_id = uuidutils.generate_uuid() + consumer_data = { + 'service': 'dummy_service', + 'resource_type': 'dummy_resource_type', + 'resource_id': resource_id + } + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer_data) + stored_secret = self.key_mgr.get(self.ctxt, stored_id) + self.assertIsNotNone(stored_secret) + self.assertIsInstance(stored_secret.consumers, list) + self.assertEqual(len(stored_secret.consumers), 1) + self.assertEqual(stored_secret.consumers[0]['service'], + consumer_data['service']) + self.assertEqual(stored_secret.consumers[0]['resource_type'], + consumer_data['resource_type']) + self.assertEqual(stored_secret.consumers[0]['resource_id'], + consumer_data['resource_id']) + + def test_secret_create_remove_nonexistent_consumer(self): + """Removing a nonexistent consumer should raise an exception.""" + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + resource_id = uuidutils.generate_uuid() + consumer_data = { + 'service': 'dummy_service', + 'resource_type': 'dummy_resource_type', + 'resource_id': resource_id + } + self.assertRaises(exception.ManagedObjectNotFoundError, + self.key_mgr.remove_consumer, self.ctxt, + stored_id, consumer_data) + + @utils.parameterized_dataset({ + 'remove_one': [[{'service': 'service_test1', + 'resource_type': 'type_test1', + 'resource_id': 'id_test1'}, + {'service': 'service_test2', + 'resource_type': 'type_test2', + 'resource_id': 'id_test2'}], + [{'service': 'service_test1', + 'resource_type': 'type_test1', + 'resource_id': 'id_test1'}]], + 'remove_all': [[{'service': 'service_test1', + 'resource_type': 'type_test1', + 'resource_id': 'id_test1'}, + {'service': 'service_test2', + 'resource_type': 'type_test2', + 'resource_id': 'id_test2'}], + [{'service': 'service_test1', + 'resource_type': 'type_test1', + 'resource_id': 'id_test1'}, + {'service': 'service_test2', + 'resource_type': 'type_test2', + 'resource_id': 'id_test2'}]] + }) + def test_secret_create_and_adding_removing_consumers( + self, + add_consumers, + remove_consumers): + """The following activities are carried: + + Create a secret, then register each consumer + in the register_consumers list, then remove each consumer + in the remove_consumers list. + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + for consumer in add_consumers: + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer) + stored_secret = self.key_mgr.get(self.ctxt, stored_id) + self.assertCountEqual(add_consumers, stored_secret.consumers) + + for consumer in remove_consumers: + self.key_mgr.remove_consumer(self.ctxt, stored_id, consumer) + stored_secret = self.key_mgr.get(self.ctxt, stored_id) + + removed_ids = set([v['resource_id'] for v in remove_consumers]) + remaining_consumers = [v for v in add_consumers + if v['resource_id'] not in removed_ids] + self.assertCountEqual(remaining_consumers, stored_secret.consumers) + + @utils.parameterized_dataset({ + 'no_args': [[{}]], + 'one_arg_1': [[{'service': 'service1'}]], + 'one_arg_2': [[{'resource_type': 'type1'}]], + 'one_arg_3': [[{'resource_id': 'id1'}]], + 'two_args_1': [[{'service': 'service1', + 'resource_type': 'type1'}]], + 'two_args_2': [[{'service': 'service1', + 'resource_id': 'id1'}]], + 'two_args_3': [[{'resource_type': 'type1', + 'resource_id': 'id'}]] + }) + def test_consumer_add_missing_positional_arguments(self, consumers): + """Missing Positional Arguments - Addition + + Tries to add a secret consumer without providing all of the required + positional arguments (service, resource_type, resource_id). + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + for consumer in consumers: + e = self.assertRaises( + TypeError, + self.key_mgr.add_consumer, + self.ctxt, stored_id, consumer) + self.assertIn('register_consumer() missing', str(e)) + + @utils.parameterized_dataset({ + 'no_args': [[{}]], + 'one_arg_1': [[{'service': 'service1'}]], + 'one_arg_2': [[{'resource_type': 'type1'}]], + 'one_arg_3': [[{'resource_id': 'id1'}]], + 'two_args_1': [[{'service': 'service1', + 'resource_type': 'type1'}]], + 'two_args_2': [[{'service': 'service1', + 'resource_id': 'id1'}]], + 'two_args_3': [[{'resource_type': 'type1', + 'resource_id': 'id'}]] + }) + def test_consumer_remove_missing_positional_arguments(self, consumers): + """Missing Positional Arguments - Removal + + Tries to remove a secret consumer without providing all of the required + positional arguments (service, resource_type, resource_id). + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + consumer_data = { + 'service': 'service1', + 'resource_type': 'type1', + 'resource_id': 'id1' + } + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer_data) + for consumer in consumers: + e = self.assertRaises( + TypeError, + self.key_mgr.remove_consumer, + self.ctxt, stored_id, consumer) + self.assertIn('remove_consumer() missing', str(e)) + + def test_consumer_add_two_remove_one_check_consumers_list(self): + """Consumers addition and removal - check of list consistency + + Adds two consumers, removes one and verifies if the consumers + list's length is consistent (equals to 1). + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + consumers = [ + {'service': 'service1', + 'resource_type': 'type1', + 'resource_id': 'id1'}, + {'service': 'service2', + 'resource_type': 'type2', + 'resource_id': 'id2'} + ] + for consumer in consumers: + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer) + stored_secret = self.key_mgr.get(self.ctxt, stored_id) + self.assertCountEqual(consumers, stored_secret.consumers) + + self.key_mgr.remove_consumer(self.ctxt, stored_id, consumers[0]) + stored_secret = self.key_mgr.get(self.ctxt, stored_id) + self.assertCountEqual(consumers[1:], stored_secret.consumers) + + def test_consumer_add_secret_delete_force_parameter_nonexisting(self): + """Consumer addition, secret deletion with no 'force' parameter + + Creates a secret, adds a consumer to it and tries to delete the secret + without specifying the 'force' parameter. + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + consumer = {'service': 'service1', + 'resource_type': 'type1', + 'resource_id': 'id1'} + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer) + + e = self.assertRaises(ValueError, self.key_mgr.delete, + self.ctxt, stored_id) + self.assertIn("Secret has consumers! Remove them first or use the " + "force parameter to delete it.", str(e)) + + def test_consumer_add_secret_delete_force_parameter_false(self): + """Consumer addition, secret deletion, 'force' parameter equals False + + Creates a secret, adds a consumer to it and tries to delete the secret + specifying the 'force' parameter as False. + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.addCleanup(self.key_mgr.delete, self.ctxt, stored_id, True) + self.assertIsNotNone(stored_id) + + consumer = {'service': 'service1', + 'resource_type': 'type1', + 'resource_id': 'id1'} + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer) + + e = self.assertRaises(ValueError, self.key_mgr.delete, + self.ctxt, stored_id, False) + self.assertIn("Secret has consumers! Remove them first or use the " + "force parameter to delete it.", str(e)) + + def test_consumer_add_secret_delete_force_parameter_true(self): + """Consumer addition, secret deletion, 'force' parameter equals True + + Creates a secret, adds a consumer to it and deletes the secret, + specifying the 'force' parameter as True. + """ + key = test_key_manager._get_test_passphrase() + self.assertIsNotNone(key) + + stored_id = self.key_mgr.store(self.ctxt, key) + self.assertIsNotNone(stored_id) + + consumer = {'service': 'service1', + 'resource_type': 'type1', + 'resource_id': 'id1'} + self.key_mgr.add_consumer(self.ctxt, stored_id, consumer) + + self.key_mgr.delete(self.ctxt, stored_id, True) + class BarbicanKeyManagerOSLOContextTestCase(BarbicanKeyManagerTestCase, base.BaseTestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/tests/functional/key_manager/test_key_manager.py new/castellan-5.0.0/castellan/tests/functional/key_manager/test_key_manager.py --- old/castellan-4.3.0/castellan/tests/functional/key_manager/test_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/tests/functional/key_manager/test_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -194,7 +194,7 @@ # created if older objects were not cleaned up, so we will simply # check if the object we created is in the list retrieved_objects = self.key_mgr.list(self.ctxt) - self.assertTrue(managed_object in retrieved_objects) + self.assertIn(managed_object, retrieved_objects) for retrieved_object in retrieved_objects: self.assertFalse(retrieved_object.is_metadata_only()) self.assertIsNotNone(retrieved_object.id) @@ -217,7 +217,7 @@ # created if older objects were not cleaned up, so we will simply # check if the object we created is in the list retrieved_objects = self.key_mgr.list(self.ctxt, metadata_only=True) - self.assertTrue(expected_obj in retrieved_objects) + self.assertIn(expected_obj, retrieved_objects) for retrieved_object in retrieved_objects: self.assertTrue(retrieved_object.is_metadata_only()) self.assertIsNotNone(retrieved_object.id) @@ -242,4 +242,4 @@ for retrieved_object in retrieved_objects: self.assertEqual(type(object_1), type(retrieved_object)) self.assertIsNotNone(retrieved_object.id) - self.assertTrue(object_1 in retrieved_objects) + self.assertIn(object_1, retrieved_objects) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/tests/unit/key_manager/mock_key_manager.py new/castellan-5.0.0/castellan/tests/unit/key_manager/mock_key_manager.py --- old/castellan-4.3.0/castellan/tests/unit/key_manager/mock_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/tests/unit/key_manager/mock_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -178,7 +178,11 @@ if context is None: raise exception.Forbidden() - obj = copy.deepcopy(self.keys[managed_object_id]) + try: + obj = copy.deepcopy(self.keys[managed_object_id]) + except KeyError: + raise exception.ManagedObjectNotFoundError() + if metadata_only: if hasattr(obj, "_key"): obj._key = None @@ -197,7 +201,26 @@ if context is None: raise exception.Forbidden() - del self.keys[managed_object_id] + try: + del self.keys[managed_object_id] + except KeyError: + raise exception.ManagedObjectNotFoundError() + + def add_consumer(self, context, managed_object_id, consumer_data): + if context is None: + raise exception.Forbidden() + if managed_object_id not in self.keys: + raise exception.ManagedObjectNotFoundError() + self.keys[managed_object_id].consumers.append(consumer_data) + + def remove_consumer(self, context, managed_object_id, consumer_data): + if context is None: + raise exception.Forbidden() + if managed_object_id not in self.keys: + raise exception.ManagedObjectNotFoundError() + self.keys[managed_object_id].consumers = [ + c for c in self.keys[managed_object_id].consumers + if c != consumer_data] def _generate_password(self, length, symbolgroups): """Generate a random password from the supplied symbol groups. @@ -240,6 +263,6 @@ objects = [] for obj_id in self.keys: obj = self.get(context, obj_id, metadata_only=metadata_only) - if type(obj) == object_type or object_type is None: + if object_type is None or isinstance(obj, object_type): objects.append(obj) return objects diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/tests/unit/key_manager/test_barbican_key_manager.py new/castellan-5.0.0/castellan/tests/unit/key_manager/test_barbican_key_manager.py --- old/castellan-4.3.0/castellan/tests/unit/key_manager/test_barbican_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/tests/unit/key_manager/test_barbican_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -24,6 +24,7 @@ from keystoneauth1 import service_token from oslo_context import context from oslo_utils import timeutils +from oslo_utils import uuidutils from castellan.common import exception from castellan.common.objects import symmetric_key as sym_key @@ -78,8 +79,13 @@ self.create = self.mock_barbican.secrets.create self.list = self.mock_barbican.secrets.list + self.add_consumer = self.mock_barbican.secrets.add_consumer + self.remove_consumer = self.mock_barbican.secrets.remove_consumer + self.list_versions = self.mock_barbican.versions.list_versions + self.key_mgr._barbican_client = self.mock_barbican self.key_mgr._current_context = self.ctxt + self.key_mgr._version_client = self.mock_barbican def test_barbican_endpoint(self): endpoint_data = mock.Mock() @@ -377,7 +383,30 @@ def test_delete_key(self): self.key_mgr.delete(self.ctxt, self.key_id) - self.delete.assert_called_once_with(self.secret_ref) + self.delete.assert_called_once_with(self.secret_ref, False) + + def test_delete_secret_with_consumers_no_force_parameter(self): + self.mock_barbican.secrets.delete = mock.Mock( + side_effect=exception.KeyManagerError( + "Secret has consumers! Use the 'force' parameter.")) + self.assertRaises(exception.KeyManagerError, + self.key_mgr.delete, self.ctxt, self.key_id) + self.mock_barbican.secrets.delete.assert_called_once_with( + self.secret_ref, False) + + def test_delete_secret_with_consumers_force_parameter_false(self): + self.mock_barbican.secrets.delete = mock.Mock( + side_effect=barbican_exceptions.HTTPClientError( + "Secret has consumers! Use the 'force' parameter.")) + self.assertRaises(exception.KeyManagerError, + self.key_mgr.delete, self.ctxt, self.key_id, + force=False) + self.mock_barbican.secrets.delete.assert_called_once_with( + self.secret_ref, False) + + def test_delete_secret_with_consumers_force_parameter_true(self): + self.key_mgr.delete(self.ctxt, self.key_id, force=True) + self.delete.assert_called_once_with(self.secret_ref, True) def test_delete_unknown_key(self): self.assertRaises(exception.KeyManagerError, @@ -634,3 +663,245 @@ self.assertIn('cafile', barbican_service_user_opts) # From auth common opts. self.assertIn('auth_section', barbican_service_user_opts) + + def _test_consumer_expects_error( + self, Error, method, ctxt, obj_ref, service="storage", + resource_type='volume', resource_id=uuidutils.generate_uuid()): + consumer_data = self._get_custom_consumer_data( + service=service, resource_type=resource_type, + resource_id=resource_id) + self.assertRaises( + Error, method, ctxt, obj_ref, consumer_data) + + def _mock_list_versions_and_test_consumer_expects_error( + self, Error, method, ctxt, obj_ref, service="storage", + resource_type='volume', resource_id=uuidutils.generate_uuid()): + self._mock_list_versions() + self._test_consumer_expects_error( + Error, method, ctxt, obj_ref, service=service, + resource_type=resource_type, resource_id=resource_id) + + def _mock_list_versions_and_test_add_consumer_expects_error( + self, Error, ctxt, obj_ref, side_effect=None, service="storage", + resource_type='volume', resource_id=uuidutils.generate_uuid()): + self.mock_barbican.secrets.register_consumer = mock.Mock( + side_effect=side_effect) + self._mock_list_versions_and_test_consumer_expects_error( + Error, self.key_mgr.add_consumer, ctxt, + obj_ref, service=service, resource_type=resource_type, + resource_id=resource_id) + + def _mock_list_versions_and_test_remove_consumer_expects_error( + self, Error, ctxt, obj_ref, side_effect=None, service="storage", + resource_type='volume', resource_id=uuidutils.generate_uuid()): + self.mock_barbican.secrets.remove_consumer = mock.Mock( + side_effect=side_effect) + self._mock_list_versions_and_test_consumer_expects_error( + Error, self.key_mgr.remove_consumer, ctxt, + obj_ref, service=service, resource_type=resource_type, + resource_id=resource_id) + + def _mock_list_versions(self): + list_versions = [{ + 'id': 'v1', 'status': 'CURRENT', 'min_version': '1.0', + 'max_version': '1.1', 'links': []} + ] + self.list_versions.return_value = list_versions + + def _get_custom_consumer_data( + self, service="storage", resource_type='volume', + resource_id=uuidutils.generate_uuid()): + return { + 'service': service, 'resource_type': resource_type, + 'resource_id': resource_id} + + def test_add_consumer_without_context_fails(self): + self.key_mgr._barbican_client = None + self._test_consumer_expects_error( + exception.Forbidden, self.key_mgr.add_consumer, None, + self.secret_ref) + + def test_add_consumer_with_different_project_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Forbidden: SecretConsumer creation attempt not allowed - " + "please review your user/project privileges") + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect) + + def test_add_consumer_with_null_managed_object_id_fails(self): + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, None) + + def test_add_consumer_with_empty_managed_object_id_fails(self): + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, "") + + def test_add_consumer_with_invalid_managed_object_id_fails(self): + side_effect = ValueError("Secret incorrectly specified.") + self._mock_list_versions_and_test_add_consumer_expects_error( + ValueError, self.ctxt, uuidutils.generate_uuid()[:-1], + side_effect=side_effect) + + def test_add_consumer_with_inexistent_managed_object_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Not Found: Secret not found.", status_code=404) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.ManagedObjectNotFoundError, self.ctxt, self.secret_ref, + side_effect=side_effect) + + def test_add_consumer_with_null_service_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. Invalid " + "property: 'service'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, service=None) + + def test_add_consumer_with_empty_service_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: 'service'", + status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, service="") + + def test_add_consumer_with_null_resource_type_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. " + "Invalid property: 'resource_type'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_type=None) + + def test_add_consumer_with_empty_resource_type_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: " + "'resource_type'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_type="") + + def test_add_consumer_with_null_resource_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. " + "Invalid property: 'resource_id'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_id=None) + + def test_add_consumer_with_empty_resource_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: " + "'resource_id'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_id="") + + def test_add_consumer_with_valid_parameters_doesnt_fail(self): + self._mock_list_versions() + self.key_mgr.add_consumer( + self.ctxt, self.secret_ref, self._get_custom_consumer_data()) + + def test_remove_consumer_without_context_fails(self): + self.key_mgr._barbican_client = None + self._test_consumer_expects_error( + exception.Forbidden, self.key_mgr.remove_consumer, None, + self.secret_ref) + + def test_remove_consumer_with_different_project_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Forbidden: SecretConsumer creation attempt not allowed - " + "please review your user/project privileges") + self._mock_list_versions_and_test_remove_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect) + + def test_remove_consumer_with_null_managed_object_id_fails(self): + side_effect = ValueError("secret incorrectly specified.") + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, None, + side_effect=side_effect) + + def test_remove_consumer_with_empty_managed_object_id_fails(self): + side_effect = ValueError("secret incorrectly specified.") + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, "", side_effect=side_effect) + + def test_remove_consumer_with_invalid_managed_object_id_fails(self): + side_effect = ValueError("Secret incorrectly specified.") + self._mock_list_versions_and_test_add_consumer_expects_error( + ValueError, self.ctxt, uuidutils.generate_uuid()[:-1], + side_effect=side_effect) + + def test_remove_consumer_without_registered_managed_object_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Not Found: Secret not found.", status_code=404) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.ManagedObjectNotFoundError, self.ctxt, self.secret_ref, + side_effect=side_effect) + + def test_remove_consumer_with_null_service_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. Invalid " + "property: 'service'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, service=None) + + def test_remove_consumer_with_empty_service_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: 'service'", + status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, service="") + + def test_remove_consumer_with_null_resource_type_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. " + "Invalid property: 'resource_type'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_type=None) + + def test_remove_consumer_with_empty_resource_type_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: " + "'resource_type'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_type="") + + def test_remove_consumer_with_null_resource_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': None is not of type 'string'. " + "Invalid property: 'resource_id'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_id=None) + + def test_remove_consumer_with_empty_resource_id_fails(self): + side_effect = barbican_exceptions.HTTPClientError( + "Bad Request: Provided object does not match schema " + "'Secret Consumer': '' is too short. Invalid property: " + "'resource_id'", status_code=400) + self._mock_list_versions_and_test_add_consumer_expects_error( + exception.KeyManagerError, self.ctxt, self.secret_ref, + side_effect=side_effect, resource_id="") + + def test_remove_consumer_with_valid_parameters_doesnt_fail(self): + self._mock_list_versions() + self.key_mgr.remove_consumer( + self.ctxt, self.secret_ref, self._get_custom_consumer_data()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan/tests/unit/key_manager/test_mock_key_manager.py new/castellan-5.0.0/castellan/tests/unit/key_manager/test_mock_key_manager.py --- old/castellan-4.3.0/castellan/tests/unit/key_manager/test_mock_key_manager.py 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/castellan/tests/unit/key_manager/test_mock_key_manager.py 2024-03-05 10:56:45.000000000 +0100 @@ -201,22 +201,26 @@ self.key_mgr.get, None, None) def test_get_unknown_key(self): - self.assertRaises(KeyError, self.key_mgr.get, self.context, None) + self.assertRaises( + exception.ManagedObjectNotFoundError, self.key_mgr.get, + self.context, None) def test_delete_key(self): key_id = self.key_mgr.create_key(self.context) self.key_mgr.delete(self.context, key_id) - self.assertRaises(KeyError, self.key_mgr.get, self.context, - key_id) + self.assertRaises( + exception.ManagedObjectNotFoundError, self.key_mgr.get, + self.context, key_id) def test_delete_null_context(self): self.assertRaises(exception.Forbidden, self.key_mgr.delete, None, None) def test_delete_unknown_key(self): - self.assertRaises(KeyError, self.key_mgr.delete, self.context, - None) + self.assertRaises( + exception.ManagedObjectNotFoundError, self.key_mgr.delete, + self.context, None) def test_list_null_context(self): self.assertRaises(exception.Forbidden, self.key_mgr.list, None) @@ -229,8 +233,8 @@ keys = self.key_mgr.list(self.context) self.assertEqual(2, len(keys)) - self.assertTrue(key1 in keys) - self.assertTrue(key2 in keys) + self.assertIn(key1, keys) + self.assertIn(key2, keys) for key in keys: self.assertIsNotNone(key.id) @@ -246,7 +250,7 @@ bit_length_list = [key1.bit_length, key2.bit_length] for key in keys: self.assertTrue(key.is_metadata_only()) - self.assertTrue(key.bit_length in bit_length_list) + self.assertIn(key.bit_length, bit_length_list) for key in keys: self.assertIsNotNone(key.id) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan.egg-info/PKG-INFO new/castellan-5.0.0/castellan.egg-info/PKG-INFO --- old/castellan-4.3.0/castellan.egg-info/PKG-INFO 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/castellan.egg-info/PKG-INFO 2024-03-05 10:57:16.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: castellan -Version: 4.3.0 +Version: 5.0.0 Summary: Generic Key Manager interface for OpenStack Home-page: https://docs.openstack.org/castellan/latest/ Author: OpenStack @@ -37,6 +37,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan.egg-info/SOURCES.txt new/castellan-5.0.0/castellan.egg-info/SOURCES.txt --- old/castellan-4.3.0/castellan.egg-info/SOURCES.txt 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/castellan.egg-info/SOURCES.txt 2024-03-05 10:57:16.000000000 +0100 @@ -111,6 +111,8 @@ releasenotes/notes/fix-vault-create-key-b4340a3067cbd93c.yaml releasenotes/notes/fix-vault-flaky-kv-api-version-b0cd9d62a39d2907.yaml releasenotes/notes/implements-keymanager-option-discovery-13a46c1dfc036a3f.yaml +releasenotes/notes/secret-consumers-0253330a65b6638b.yaml +releasenotes/notes/secret-consumers-e047d1c6f1a20e47.yaml releasenotes/notes/support-legacy-fixed-key-id-9fa897b547111610.yaml releasenotes/notes/use-barbican-endpoint-type-config-option-e583d30930cc22ba.yaml releasenotes/notes/use-barbican-region-name-config-option-31bec809292302b8.yaml @@ -118,6 +120,7 @@ releasenotes/notes/vault-kv-mountpoint-919eb547764a0c74.yaml releasenotes/notes/vault-namespaces-7d334e7407396df9.yaml releasenotes/source/2023.1.rst +releasenotes/source/2023.2.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/pike.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan.egg-info/pbr.json new/castellan-5.0.0/castellan.egg-info/pbr.json --- old/castellan-4.3.0/castellan.egg-info/pbr.json 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/castellan.egg-info/pbr.json 2024-03-05 10:57:16.000000000 +0100 @@ -1 +1 @@ -{"git_version": "7033db5", "is_release": true} \ No newline at end of file +{"git_version": "d578cbc", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/castellan.egg-info/requires.txt new/castellan-5.0.0/castellan.egg-info/requires.txt --- old/castellan-4.3.0/castellan.egg-info/requires.txt 2023-09-22 16:47:32.000000000 +0200 +++ new/castellan-5.0.0/castellan.egg-info/requires.txt 2024-03-05 10:57:16.000000000 +0100 @@ -6,6 +6,6 @@ oslo.log>=3.36.0 oslo.utils>=3.33.0 pbr!=2.1.0,>=2.0.0 -python-barbicanclient>=4.5.2 +python-barbicanclient>=5.5.0 requests!=2.20.0,>=2.18.0 stevedore>=1.20.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/doc/source/user/index.rst new/castellan-5.0.0/doc/source/user/index.rst --- old/castellan-4.3.0/doc/source/user/index.rst 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/doc/source/user/index.rst 2024-03-05 10:56:45.000000000 +0100 @@ -173,6 +173,47 @@ will not return anything but may raise exceptions if there are communication, identification, or authorization issues. +**Example. Secret consumers.** + +.. code:: python + + import myapp + from castellan import key_manager + + manager = key_manager.API() + + # Listing consumers: + stored_secret = self.key_mgr.get(myapp.context(), stored_id) + consumer_list = stored_secret.consumers # consumers is a list of dicts + + # Adding consumers: + consumer = {'service': 'glance', + 'resource_type': 'image', + 'resource_id': 'image_id'} + try: + manager.add_consumer(myapp.context(), stored_id, consumer) + except NotImplementedError: + pass # backends like Vault don't support adding/removing consumers + + # Remove the consumer before calling secret delete without the force flag: + try: + manager.remove_consumer(myapp.context(), stored_id, consumer) + except NotImplementedError: + pass + manager.delete(myapp.context(), stored_key_id) + + # Alternatively, force delete a secret + manager.delete(myapp.context(), stored_key_id, force=True) + +After creating a secret, we can add consumers to it. Secrets with consumers +cannot be deleted without using the force flag. + +.. note:: + + Secret consumers are currently only avaliable for the Barbican backend. + https://docs.openstack.org/barbican/latest/api/reference/secret_consumers.ht... + + Configuring castellan ~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/releasenotes/notes/secret-consumers-0253330a65b6638b.yaml new/castellan-5.0.0/releasenotes/notes/secret-consumers-0253330a65b6638b.yaml --- old/castellan-4.3.0/releasenotes/notes/secret-consumers-0253330a65b6638b.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/castellan-5.0.0/releasenotes/notes/secret-consumers-0253330a65b6638b.yaml 2024-03-05 10:56:45.000000000 +0100 @@ -0,0 +1,10 @@ +--- +features: > + The Barbican API has been extended to allow secrets to have one or + more consumers. This extension has been documented here: + https://docs.openstack.org/barbican/latest/api/reference/secret_consumers.ht... + + This functionality has now been exposed in the castellan client. + Users may add, remove or delete consumers by calling new mechods on the + SecretManager. Note that this functionality is only available for + the Barbican backend. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/releasenotes/notes/secret-consumers-e047d1c6f1a20e47.yaml new/castellan-5.0.0/releasenotes/notes/secret-consumers-e047d1c6f1a20e47.yaml --- old/castellan-4.3.0/releasenotes/notes/secret-consumers-e047d1c6f1a20e47.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/castellan-5.0.0/releasenotes/notes/secret-consumers-e047d1c6f1a20e47.yaml 2024-03-05 10:56:45.000000000 +0100 @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The KeyManager class now has two new abstract methods, ``add_consumer`` and + ``remove_consumer``. Any implementations inheriting the base KeyManager + class should override these two interfaces. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/releasenotes/source/2023.2.rst new/castellan-5.0.0/releasenotes/source/2023.2.rst --- old/castellan-4.3.0/releasenotes/source/2023.2.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/castellan-5.0.0/releasenotes/source/2023.2.rst 2024-03-05 10:56:45.000000000 +0100 @@ -0,0 +1,6 @@ +=========================== +2023.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2023.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/releasenotes/source/index.rst new/castellan-5.0.0/releasenotes/source/index.rst --- old/castellan-4.3.0/releasenotes/source/index.rst 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/releasenotes/source/index.rst 2024-03-05 10:56:45.000000000 +0100 @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2023.2 2023.1 zed yoga diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/releasenotes/source/yoga.rst new/castellan-5.0.0/releasenotes/source/yoga.rst --- old/castellan-4.3.0/releasenotes/source/yoga.rst 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/releasenotes/source/yoga.rst 2024-03-05 10:56:45.000000000 +0100 @@ -3,4 +3,4 @@ ========================= .. release-notes:: - :branch: stable/yoga + :branch: unmaintained/yoga diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/requirements.txt new/castellan-5.0.0/requirements.txt --- old/castellan-4.3.0/requirements.txt 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/requirements.txt 2024-03-05 10:56:45.000000000 +0100 @@ -1,10 +1,6 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 -python-barbicanclient>=4.5.2 # Apache-2.0 +python-barbicanclient>=5.5.0 # Apache-2.0 oslo.config>=6.4.0 # Apache-2.0 oslo.context>=2.19.2 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/setup.cfg new/castellan-5.0.0/setup.cfg --- old/castellan-4.3.0/setup.cfg 2023-09-22 16:47:32.592500000 +0200 +++ new/castellan-5.0.0/setup.cfg 2024-03-05 10:57:16.653267600 +0100 @@ -18,6 +18,7 @@ Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: Implementation :: CPython diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/test-requirements.txt new/castellan-5.0.0/test-requirements.txt --- old/castellan-4.3.0/test-requirements.txt 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/test-requirements.txt 2024-03-05 10:56:45.000000000 +0100 @@ -1,11 +1,4 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -hacking>=3.0.1,<3.1.0 # Apache-2.0 -# remove this pyflakes from here once you bump the -# hacking to 3.2.0 or above. hacking 3.2.0 takes -# care of pyflakes version compatibilty. -pyflakes>=2.1.1 +hacking>=6.1.0,<6.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 python-barbicanclient>=4.5.2 # Apache-2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/castellan-4.3.0/tox.ini new/castellan-5.0.0/tox.ini --- old/castellan-4.3.0/tox.ini 2023-09-22 16:47:08.000000000 +0200 +++ new/castellan-5.0.0/tox.ini 2024-03-05 10:56:45.000000000 +0100 @@ -10,7 +10,7 @@ VIRTUAL_ENV={envdir} OS_TEST_PATH=./castellan/tests/unit deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2023.2} + -c{env:TOX_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = stestr run --slowest --concurrency=1 {posargs} @@ -61,7 +61,6 @@ [testenv:pdf-docs] deps = {[testenv:docs]deps} -envdir = {toxworkdir}/docs allowlist_externals = rm make @@ -72,7 +71,6 @@ [testenv:releasenotes] deps = {[testenv:docs]deps} -envdir = {toxworkdir}/docs commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:functional]
participants (1)
-
Source-Sync