From 99012db14bd0e6f87aa2b79c272c7f5951972d41 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Mon, 6 Apr 2020 17:17:20 -0500 Subject: [PATCH] Add new config to enforce the new defaults When policy change their default check_str and not override by operator then old defaults check_str are added with OrCheck to the new default check_str so that old defaults keep working. If operators want to enforce the new defaults with no old defaults then they have to overwrite the policy rule in poicy file with new default value. This is not expected and very painful for them especially when all policies are switching to new defaults. For example: - https://review.opendev.org/#/q/topic:bp/policy-defaults-refresh+(status:open+OR+status:merged) This commit adds a new config options to control the new defaults enforcement. If True then old defaults will not be supported and also no warning will be logged. New config option is default to False so no change in behaviour for old users. Change-Id: I3c2c889af25b723f1eedbe6167d614c6a4bc6cd2 --- oslo_policy/opts.py | 11 +++++++ oslo_policy/policy.py | 5 ++- oslo_policy/tests/test_policy.py | 33 +++++++++++++++++++ ...enforce_new_defaults-6ae17d8b8d166a2c.yaml | 11 +++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/enforce_new_defaults-6ae17d8b8d166a2c.yaml diff --git a/oslo_policy/opts.py b/oslo_policy/opts.py index e0e1dd6c..16e3696b 100644 --- a/oslo_policy/opts.py +++ b/oslo_policy/opts.py @@ -34,6 +34,17 @@ _options = [ 'will be raised. If ``False``, a message will be ' 'logged informing operators that policies are being ' 'invoked with mismatching scope.')), + cfg.BoolOpt('enforce_new_defaults', + default=False, + help=_('This option controls whether or not to use old ' + 'deprecated defaults when evaluating policies. If ' + '``True``, the old deprecated defaults are not going ' + 'to be evaluated which mean if any existing token ' + 'allowed for old defaults but disallowed for new ' + 'defaults will be disallowed. It is encouraged to ' + 'enable this flag along with ``enforce_scope`` flag ' + 'so that you can get benefits of new defaults and ' + '``scope_type`` together')), cfg.StrOpt('policy_file', default='policy.json', help=_('The relative or absolute path of a file that maps ' diff --git a/oslo_policy/policy.py b/oslo_policy/policy.py index 61a3597d..b0193150 100644 --- a/oslo_policy/policy.py +++ b/oslo_policy/policy.py @@ -696,7 +696,10 @@ class Enforcer(object): # messages telling them stuff is going to change if they don't maintain # the policy manually or add infrastructure to their deployment to # support the new policy. - if (deprecated_rule.check_str != default.check_str + # If flag enforce_new_defaults is true then do not add OrCheck + # the old check_str and enforce only new defaults. + if (not self.conf.oslo_policy.enforce_new_defaults + and deprecated_rule.check_str != default.check_str and default.name not in self.file_rules): default.check = OrCheck([_parser.parse_rule(cs) for cs in diff --git a/oslo_policy/tests/test_policy.py b/oslo_policy/tests/test_policy.py index f3f75b0b..35ae9dff 100644 --- a/oslo_policy/tests/test_policy.py +++ b/oslo_policy/tests/test_policy.py @@ -1619,6 +1619,39 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase): # Verify that we didn't overwrite the new rule. self.assertEqual('bang', self.enforcer.rules['new_rule'].match) + def test_enforce_new_defaults_no_old_check_string(self): + self.conf.set_override('enforce_new_defaults', True, + group='oslo_policy') + deprecated_rule = policy.DeprecatedRule( + name='foo:create_bar', + check_str='role:fizz' + ) + + rule_list = [policy.DocumentedRuleDefault( + name='foo:create_bar', + check_str='role:bang', + description='Create a bar.', + operations=[{'path': '/v1/bars', 'method': 'POST'}], + deprecated_rule=deprecated_rule, + deprecated_reason='"role:bang" is a better default', + deprecated_since='N' + )] + enforcer = policy.Enforcer(self.conf) + enforcer.register_defaults(rule_list) + + with mock.patch('warnings.warn') as mock_warn: + enforcer.load_rules() + mock_warn.assert_not_called() + self.assertTrue( + enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']}) + ) + self.assertFalse( + enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']}) + ) + self.assertFalse( + enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']}) + ) + class DocumentedRuleDefaultTestCase(base.PolicyBaseTestCase): diff --git a/releasenotes/notes/enforce_new_defaults-6ae17d8b8d166a2c.yaml b/releasenotes/notes/enforce_new_defaults-6ae17d8b8d166a2c.yaml new file mode 100644 index 00000000..8ff851f2 --- /dev/null +++ b/releasenotes/notes/enforce_new_defaults-6ae17d8b8d166a2c.yaml @@ -0,0 +1,11 @@ +features: + - | + A new configuration option ``enforce_new_defaults`` has been + added to the ``[oslo_policy]`` group to control whether or not to + use the old deprecated defaults. If ``True``, the old deprecated + defaults are not going to be evaluated which mean if any existing + token allowed for old defaults but disallowed for new defaults + will be disallowed. It is encouraged to enable this flag along + with ``enforce_scope`` flag so that you can get benefits of new + defaults and ``scope_type`` together. This way operators can switch + to new defaults without overwriting the rule in policy file.