From 613cc42a390d06ed0d4107246cc79722bda107a0 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 22 Oct 2025 01:41:51 +0200 Subject: [PATCH] Inject os_glance.* section in the property protection file Users who use a property protection file and use either the import workflow or the metadata injection plugin will get a glance.common.exception.ReservedProperty exception because we try to add os_glance_* properties to their images. To work around this issue, users could add the following lines to their property protection file: [os_glance.*] create = @ read = @ update = @ delete = @ However, it is not something users should have to do. This patch automatically injects this section into the config we read from the property protection file. Change-Id: Iea2fffa75b973b5a34da6fc4145841888ea19034 Closes-Bug: #2085321 Signed-off-by: Cyril Roelandt --- glance/common/property_utils.py | 42 +++++++++++++++++++ .../tests/unit/common/test_property_utils.py | 38 ++++++++++++++++- ...n-file-import-plugin-e3e724a65e319a40.yaml | 7 ++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/property-protection-file-import-plugin-e3e724a65e319a40.yaml diff --git a/glance/common/property_utils.py b/glance/common/property_utils.py index f791fcb736..213d8170c0 100644 --- a/glance/common/property_utils.py +++ b/glance/common/property_utils.py @@ -13,6 +13,7 @@ # under the License. import configparser +import io import re from oslo_config import cfg @@ -108,10 +109,51 @@ class PropertyRules(object): self.prop_prot_rule_format = self.prop_prot_rule_format.lower() self._load_rules() + def _inject_os_glance_section(self): + # Inject an 'os_glance.*' section to work around bug #2085321. + # https://bugs.launchpad.net/glance/+bug/2085321 + # An exception will be raised if a property protection file is in + # use and does not contain the os_glance.* section. Since this is + # internal to Glance, the users should not have to specify that + # section in the config file themselves. We therefore inject it + # here on their behalf. + # + # We also remove all sections starting with 'os_glance' since the + # keyword is reserved for internal use. + # + # See: + # glance/tests/etc/property-protections-policies.conf + # glance/tests/etc/property-protections.conf + global CONFIG + + for section in CONFIG.sections(): + if section.startswith('os_glance'): + CONFIG.remove_section(section) + + # We want to insert the [os_glance.*] section at the top of the config, + # so it is not shadowed by sections such as [.*]. To do this, we: + # 1) Dump the user config into a StringIO + # 2) Init a new ConfigParser object + # 3) Read our [os_glance.*] section + # 4) Read the user config + txt_config = io.StringIO() + CONFIG.write(txt_config) + CONFIG = configparser.ConfigParser() + CONFIG.read_dict({ + 'os_glance.*': { + 'create': '@', + 'read': '@', + 'update': '@', + 'delete': '@', + } + }) + CONFIG.read_string(txt_config.getvalue()) + def _load_rules(self): try: conf_file = CONF.find_file(CONF.property_protection_file) CONFIG.read(conf_file) + self._inject_os_glance_section() except Exception as e: msg = (_LE("Couldn't find property protection file %(file)s: " "%(error)s.") % {'file': CONF.property_protection_file, diff --git a/glance/tests/unit/common/test_property_utils.py b/glance/tests/unit/common/test_property_utils.py index d2bd0fa387..b7b2a40b4c 100644 --- a/glance/tests/unit/common/test_property_utils.py +++ b/glance/tests/unit/common/test_property_utils.py @@ -20,6 +20,7 @@ import glance.context from glance.tests.unit import base CONFIG_SECTIONS = [ + 'os_glance.*', # Automatically injected at the top '^x_owner_.*', 'spl_create_prop', 'spl_read_prop', @@ -47,7 +48,40 @@ def create_context(policy, roles=None): policy_enforcer=policy) -class TestPropertyRulesWithRoles(base.IsolatedUnitTest): +class TestPropertyRules(): + def test_inject_os_glance(self): + '''Test that the os_glance.* section has been injected.''' + rules = {'x_foo': {'create': ['fake-role'], + 'read': ['member'], + 'update': ['fake-role'], + 'delete': ['fake-role']}, + 'os_glance_store': {'create': '!', + 'read': '!', + 'update': '!', + 'delete': '!'}} + self.set_property_protection_rules(rules) + self.rules_checker = property_utils.PropertyRules() + + # The x_foo section should still be here + self.assertTrue(self.rules_checker.check_property_rules('x_foo', + 'read', create_context(self.policy, ['member']))) + + # The os_glance_store section should not have been here (users must not + # specify any section that starts with 'os_glance'. Our 'os_glance.*' + # section should now be here. + self.assertIn('os_glance.*', property_utils.CONFIG.sections()) + self.assertNotIn('os_glance_store', property_utils.CONFIG.sections()) + + # And we should have the right permissions for all operations in the + # os_glance.* section. + context = create_context(self.policy, ['']) + for operation in ['create', 'read', 'update', 'delete']: + self.assertTrue( + self.rules_checker.check_property_rules( + 'os_glance.*', operation, context)) + + +class TestPropertyRulesWithRoles(base.IsolatedUnitTest, TestPropertyRules): def setUp(self): super(TestPropertyRulesWithRoles, self).setUp() @@ -321,7 +355,7 @@ class TestPropertyRulesWithRoles(base.IsolatedUnitTest): create_context(self.policy, ['member']))) -class TestPropertyRulesWithPolicies(base.IsolatedUnitTest): +class TestPropertyRulesWithPolicies(base.IsolatedUnitTest, TestPropertyRules): def setUp(self): super(TestPropertyRulesWithPolicies, self).setUp() diff --git a/releasenotes/notes/property-protection-file-import-plugin-e3e724a65e319a40.yaml b/releasenotes/notes/property-protection-file-import-plugin-e3e724a65e319a40.yaml new file mode 100644 index 0000000000..0e6caa592d --- /dev/null +++ b/releasenotes/notes/property-protection-file-import-plugin-e3e724a65e319a40.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + `Bug #2085321 `_: + Fixed an issue that prevented users from using property protection files + and image import or the inject metadata plugin. A valid "os_glance.*" + section is now automatically added to the property protection file.