Enable enforce_scope and enforce_new_defaults by default
As per the RBAc goal timeline, we agreed to enable enforce_scope as well as the enforce_new_defaults by default - https://governance.openstack.org/tc/goals/selected/consistent-and-secure-rbac.html#id4 Also, marking enforce_scope deprecated and to be removed in 2025.2 (SLURP) cycle. Change-Id: I23ccdfd810ba8290aa49a47471eabdb65fea2128
This commit is contained in:
@@ -25,7 +25,14 @@ _option_group = 'oslo_policy'
|
|||||||
|
|
||||||
_options = [
|
_options = [
|
||||||
cfg.BoolOpt('enforce_scope',
|
cfg.BoolOpt('enforce_scope',
|
||||||
default=False,
|
default=True,
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason='This configuration was added temporarily '
|
||||||
|
'to facilitate a smooth transition to the '
|
||||||
|
'new RBAC. OpenStack will always enforce '
|
||||||
|
'scope checks. This configuration option '
|
||||||
|
'is deprecated and will be removed in the '
|
||||||
|
'2025.2 cycle.',
|
||||||
help=_('This option controls whether or not to enforce scope '
|
help=_('This option controls whether or not to enforce scope '
|
||||||
'when evaluating policies. If ``True``, the scope of '
|
'when evaluating policies. If ``True``, the scope of '
|
||||||
'the token used in the request is compared to the '
|
'the token used in the request is compared to the '
|
||||||
@@ -35,7 +42,7 @@ _options = [
|
|||||||
'logged informing operators that policies are being '
|
'logged informing operators that policies are being '
|
||||||
'invoked with mismatching scope.')),
|
'invoked with mismatching scope.')),
|
||||||
cfg.BoolOpt('enforce_new_defaults',
|
cfg.BoolOpt('enforce_new_defaults',
|
||||||
default=False,
|
default=True,
|
||||||
help=_('This option controls whether or not to use old '
|
help=_('This option controls whether or not to use old '
|
||||||
'deprecated defaults when evaluating policies. If '
|
'deprecated defaults when evaluating policies. If '
|
||||||
'``True``, the old deprecated defaults are not going '
|
'``True``, the old deprecated defaults are not going '
|
||||||
|
@@ -40,21 +40,21 @@ class OptsTestCase(test_base.BaseTestCase):
|
|||||||
|
|
||||||
def test_set_defaults_enforce_scope(self):
|
def test_set_defaults_enforce_scope(self):
|
||||||
opts._register(self.conf)
|
opts._register(self.conf)
|
||||||
self.assertEqual(False,
|
|
||||||
self.conf.oslo_policy.enforce_scope)
|
|
||||||
opts.set_defaults(self.conf, enforce_scope=True)
|
|
||||||
self.assertEqual(True,
|
self.assertEqual(True,
|
||||||
self.conf.oslo_policy.enforce_scope)
|
self.conf.oslo_policy.enforce_scope)
|
||||||
|
opts.set_defaults(self.conf, enforce_scope=False)
|
||||||
|
self.assertEqual(False,
|
||||||
|
self.conf.oslo_policy.enforce_scope)
|
||||||
|
|
||||||
def test_set_defaults_two_opts(self):
|
def test_set_defaults_two_opts(self):
|
||||||
opts._register(self.conf)
|
opts._register(self.conf)
|
||||||
self.assertEqual(False,
|
|
||||||
self.conf.oslo_policy.enforce_scope)
|
|
||||||
self.assertEqual(False,
|
|
||||||
self.conf.oslo_policy.enforce_new_defaults)
|
|
||||||
opts.set_defaults(self.conf, enforce_scope=True,
|
|
||||||
enforce_new_defaults=True)
|
|
||||||
self.assertEqual(True,
|
self.assertEqual(True,
|
||||||
self.conf.oslo_policy.enforce_scope)
|
self.conf.oslo_policy.enforce_scope)
|
||||||
self.assertEqual(True,
|
self.assertEqual(True,
|
||||||
self.conf.oslo_policy.enforce_new_defaults)
|
self.conf.oslo_policy.enforce_new_defaults)
|
||||||
|
opts.set_defaults(self.conf, enforce_scope=False,
|
||||||
|
enforce_new_defaults=False)
|
||||||
|
self.assertEqual(False,
|
||||||
|
self.conf.oslo_policy.enforce_scope)
|
||||||
|
self.assertEqual(False,
|
||||||
|
self.conf.oslo_policy.enforce_new_defaults)
|
||||||
|
@@ -1271,7 +1271,6 @@ class RuleDefaultTestCase(base.PolicyBaseTestCase):
|
|||||||
|
|
||||||
class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
||||||
|
|
||||||
@mock.patch('warnings.warn', new=mock.Mock())
|
|
||||||
def test_deprecate_a_policy_check_string(self):
|
def test_deprecate_a_policy_check_string(self):
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
@@ -1285,33 +1284,24 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
check_str='role:bang',
|
check_str='role:bang',
|
||||||
description='Create a bar.',
|
description='Create a bar.',
|
||||||
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
scope_types=['project'],
|
||||||
deprecated_rule=deprecated_rule,
|
deprecated_rule=deprecated_rule,
|
||||||
)]
|
)]
|
||||||
enforcer = policy.Enforcer(self.conf)
|
enforcer = policy.Enforcer(self.conf)
|
||||||
enforcer.register_defaults(rule_list)
|
enforcer.register_defaults(rule_list)
|
||||||
expected_msg = (
|
|
||||||
'Policy "foo:create_bar":"role:fizz" was deprecated in N in favor '
|
|
||||||
'of "foo:create_bar":"role:bang". Reason: "role:bang" is a better '
|
|
||||||
'default. Either ensure your deployment is ready for the new '
|
|
||||||
'default or copy/paste the deprecated policy into your policy '
|
|
||||||
'file and maintain it manually.'
|
|
||||||
)
|
|
||||||
|
|
||||||
with mock.patch('warnings.warn') as mock_warn:
|
enforcer.load_rules()
|
||||||
enforcer.load_rules()
|
|
||||||
mock_warn.assert_called_once_with(expected_msg)
|
|
||||||
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']})
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']})
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertFalse(
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']})
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']})
|
||||||
)
|
)
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']})
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']})
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('warnings.warn', new=mock.Mock())
|
|
||||||
def test_deprecate_an_empty_policy_check_string(self):
|
def test_deprecate_an_empty_policy_check_string(self):
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
@@ -1325,21 +1315,18 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
check_str='role:bang',
|
check_str='role:bang',
|
||||||
description='Create a bar.',
|
description='Create a bar.',
|
||||||
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
scope_types=['project'],
|
||||||
deprecated_rule=deprecated_rule,
|
deprecated_rule=deprecated_rule,
|
||||||
)]
|
)]
|
||||||
enforcer = policy.Enforcer(self.conf)
|
enforcer = policy.Enforcer(self.conf)
|
||||||
enforcer.register_defaults(rule_list)
|
enforcer.register_defaults(rule_list)
|
||||||
|
enforcer.load_rules()
|
||||||
with mock.patch('warnings.warn') as mock_warn:
|
|
||||||
enforcer.load_rules()
|
|
||||||
mock_warn.assert_called_once()
|
|
||||||
|
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']},
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']},
|
||||||
do_raise=True)
|
do_raise=True)
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']},
|
self.assertFalse(
|
||||||
do_raise=True)
|
self.enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']}))
|
||||||
|
|
||||||
@mock.patch('warnings.warn', new=mock.Mock())
|
|
||||||
def test_deprecate_replace_with_empty_policy_check_string(self):
|
def test_deprecate_replace_with_empty_policy_check_string(self):
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
@@ -1353,14 +1340,12 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
check_str='',
|
check_str='',
|
||||||
description='Create a bar.',
|
description='Create a bar.',
|
||||||
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
scope_types=['project'],
|
||||||
deprecated_rule=deprecated_rule,
|
deprecated_rule=deprecated_rule,
|
||||||
)]
|
)]
|
||||||
enforcer = policy.Enforcer(self.conf)
|
enforcer = policy.Enforcer(self.conf)
|
||||||
enforcer.register_defaults(rule_list)
|
enforcer.register_defaults(rule_list)
|
||||||
|
enforcer.load_rules()
|
||||||
with mock.patch('warnings.warn') as mock_warn:
|
|
||||||
enforcer.load_rules()
|
|
||||||
mock_warn.assert_called_once()
|
|
||||||
|
|
||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']},
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']},
|
||||||
do_raise=True)
|
do_raise=True)
|
||||||
@@ -1387,6 +1372,7 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
check_str='role:baz',
|
check_str='role:baz',
|
||||||
description='Create a bar.',
|
description='Create a bar.',
|
||||||
operations=[{'path': '/v1/bars/', 'method': 'POST'}],
|
operations=[{'path': '/v1/bars/', 'method': 'POST'}],
|
||||||
|
scope_types=['project'],
|
||||||
deprecated_rule=deprecated_rule,
|
deprecated_rule=deprecated_rule,
|
||||||
)]
|
)]
|
||||||
expected_msg = (
|
expected_msg = (
|
||||||
@@ -1459,6 +1445,8 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
mock_warn.assert_not_called()
|
mock_warn.assert_not_called()
|
||||||
|
|
||||||
def test_deprecate_check_str_suppress_does_not_log_warning(self):
|
def test_deprecate_check_str_suppress_does_not_log_warning(self):
|
||||||
|
self.conf.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
check_str='role:fizz',
|
check_str='role:fizz',
|
||||||
@@ -1481,6 +1469,8 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
mock_warn.assert_not_called()
|
mock_warn.assert_not_called()
|
||||||
|
|
||||||
def test_deprecate_name_suppress_does_not_log_warning(self):
|
def test_deprecate_name_suppress_does_not_log_warning(self):
|
||||||
|
self.conf.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:bar',
|
name='foo:bar',
|
||||||
check_str='role:baz',
|
check_str='role:baz',
|
||||||
@@ -1507,6 +1497,9 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
mock_warn.assert_not_called()
|
mock_warn.assert_not_called()
|
||||||
|
|
||||||
def test_deprecate_for_removal_suppress_does_not_log_warning(self):
|
def test_deprecate_for_removal_suppress_does_not_log_warning(self):
|
||||||
|
self.conf.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
|
|
||||||
rule_list = [policy.DocumentedRuleDefault(
|
rule_list = [policy.DocumentedRuleDefault(
|
||||||
name='foo:bar',
|
name='foo:bar',
|
||||||
check_str='role:baz',
|
check_str='role:baz',
|
||||||
@@ -1529,6 +1522,9 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
mock_warn.assert_not_called()
|
mock_warn.assert_not_called()
|
||||||
|
|
||||||
def test_suppress_default_change_warnings_flag_not_log_warning(self):
|
def test_suppress_default_change_warnings_flag_not_log_warning(self):
|
||||||
|
self.conf.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
|
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
check_str='role:fizz',
|
check_str='role:fizz',
|
||||||
@@ -1759,8 +1755,6 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
self.assertEqual('bang', self.enforcer.rules['new_rule'].match)
|
self.assertEqual('bang', self.enforcer.rules['new_rule'].match)
|
||||||
|
|
||||||
def test_enforce_new_defaults_no_old_check_string(self):
|
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(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
check_str='role:fizz',
|
check_str='role:fizz',
|
||||||
@@ -1791,6 +1785,43 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']})
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_disable_enforce_new_defaults_add_old_check_string(self):
|
||||||
|
self.conf.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:fizz',
|
||||||
|
deprecated_reason='"role:bang" is a better default',
|
||||||
|
deprecated_since='N',
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
)]
|
||||||
|
enforcer = policy.Enforcer(self.conf)
|
||||||
|
enforcer.register_defaults(rule_list)
|
||||||
|
enforcer.load_rules()
|
||||||
|
|
||||||
|
expected_check = policy.OrCheck([
|
||||||
|
_parser.parse_rule(cs) for cs in
|
||||||
|
[rule_list[0].check_str, deprecated_rule.check_str]
|
||||||
|
])
|
||||||
|
self.assertEqual(
|
||||||
|
str(enforcer.rules['foo:create_bar']), str(expected_check))
|
||||||
|
self.assertTrue(
|
||||||
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['bang']})
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['fizz']})
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
enforcer.enforce('foo:create_bar', {}, {'roles': ['baz']})
|
||||||
|
)
|
||||||
|
|
||||||
def test_deprecation_logic_is_only_performed_once_per_rule(self):
|
def test_deprecation_logic_is_only_performed_once_per_rule(self):
|
||||||
deprecated_rule = policy.DeprecatedRule(
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
name='foo:create_bar',
|
name='foo:create_bar',
|
||||||
@@ -1817,15 +1848,12 @@ class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
|||||||
enforcer.load_rules()
|
enforcer.load_rules()
|
||||||
|
|
||||||
# Loading the rules will store a version of the rule check string
|
# Loading the rules will store a version of the rule check string
|
||||||
# logically ORed with the check string of the deprecated value. Make
|
# but as enforce_new_defaults is True it will not logically ORed with
|
||||||
# sure this is happening but that the original rule check is unchanged
|
# the check string of the deprecated value. Make sure this is happening
|
||||||
expected_check = policy.OrCheck([
|
# and the original rule check is unchanged
|
||||||
_parser.parse_rule(cs) for cs in
|
|
||||||
[rule.check_str, deprecated_rule.check_str]
|
|
||||||
])
|
|
||||||
self.assertIn('foo:create_bar', enforcer.rules)
|
self.assertIn('foo:create_bar', enforcer.rules)
|
||||||
self.assertEqual(
|
self.assertEqual(rule.check_str,
|
||||||
str(enforcer.rules['foo:create_bar']), str(expected_check))
|
str(enforcer.rules['foo:create_bar']))
|
||||||
self.assertEqual(check, rule.check)
|
self.assertEqual(check, rule.check)
|
||||||
# Hacky way to check whether _handle_deprecated_rule was called again.
|
# Hacky way to check whether _handle_deprecated_rule was called again.
|
||||||
# If a second call to load_rules doesn't overwrite our dummy rule then
|
# If a second call to load_rules doesn't overwrite our dummy rule then
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The default value of config options ``enforce_scope`` and
|
||||||
|
``enforce_new_defaults`` have been changed to ``True``.
|
||||||
|
|
||||||
|
Most of the OpenStack services have enabled these options by
|
||||||
|
default, and now it's time to change the default in the oslo
|
||||||
|
policy itself.
|
||||||
|
|
||||||
|
If you want to disable them, modify the values of these config
|
||||||
|
options in the service configuration file (for example, "nova.conf")::
|
||||||
|
|
||||||
|
[oslo_policy]
|
||||||
|
enforce_new_defaults=False
|
||||||
|
enforce_scope=False
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
The ``enforce_scope`` configuration option was added temporarily
|
||||||
|
to facilitate a smooth transition to the new RBAC. OpenStack will
|
||||||
|
always enforce the scope checks, and they will not be configurable.
|
||||||
|
This configuration option is marked as deprecated and will be removed
|
||||||
|
in the 2025.2 cycle.
|
Reference in New Issue
Block a user