Merge "Fix the misspelling of "available""
diff --git a/.zuul.yaml b/.zuul.yaml
index bed19c4..5203990 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -84,16 +84,19 @@
- job:
name: patrole-member-rocky
+ nodeset: openstack-single-node-xenial
parent: patrole-member
override-checkout: stable/rocky
- job:
name: patrole-member-queens
+ nodeset: openstack-single-node-xenial
parent: patrole-member
override-checkout: stable/queens
- job:
name: patrole-member-pike
+ nodeset: openstack-single-node-xenial
parent: patrole-member
override-checkout: stable/pike
diff --git a/HACKING.rst b/HACKING.rst
index 3379292..cd85d84 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -41,7 +41,7 @@
code that is more maintainable and easier to read
- [P104] RBAC `extension test class`_ names must end in 'ExtRbacTest'
-.. _extension test class: https://github.com/openstack/patrole/tree/master/patrole_tempest_plugin/tests/api/network#neutron-extension-rbac-tests
+.. _extension test class: https://git.openstack.org/cgit/openstack/patrole/plain/patrole_tempest_plugin/tests/api/network/README.rst
Supported OpenStack Components
------------------------------
diff --git a/README.rst b/README.rst
index 713756a..dfba71f 100644
--- a/README.rst
+++ b/README.rst
@@ -75,8 +75,8 @@
.. _Tempest plugin: https://docs.openstack.org/tempest/latest/plugin.html
.. _Tempest design principles: https://docs.openstack.org/tempest/latest/overview.html#design-principles
.. _policy in code: https://specs.openstack.org/openstack/oslo-specs/specs/newton/policy-in-code.html
-.. _Nova repository: https://github.com/openstack/nova/tree/master/nova/policies
-.. _Keystone repository: https://github.com/openstack/keystone/tree/master/keystone/common/policies
+.. _Nova repository: https://git.openstack.org/cgit/openstack/nova/tree/nova/policies
+.. _Keystone repository: https://git.openstack.org/cgit/openstack/keystone/tree/keystone/common/policies
Features
--------
@@ -174,7 +174,7 @@
the Patrole repository. To configure Patrole's logging, see the
`Patrole Configuration Guide <https://docs.openstack.org/patrole/latest/configuration.html#patrole-configuration>`_.
-.. _Tempest: https://github.com/openstack/tempest
+.. _Tempest: https://git.openstack.org/cgit/openstack/tempest
.. _Tempest_quickstart: https://docs.openstack.org/tempest/latest/overview.html#quickstart
.. _tempest_run: https://docs.openstack.org/tempest/latest/run.html
.. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
diff --git a/devstack/README.rst b/devstack/README.rst
index 053afd4..490f833 100644
--- a/devstack/README.rst
+++ b/devstack/README.rst
@@ -22,4 +22,4 @@
3. Run ``stack.sh`` found in the DevStack repo.
-.. _README file: https://github.com/openstack-dev/devstack/blob/master/README.rst
+.. _README file: https://git.openstack.org/cgit/openstack-dev/devstack/plain/README.rst
diff --git a/doc/source/multi-policy-validation.rst b/doc/source/multi-policy-validation.rst
index d38b31e..576fd68 100644
--- a/doc/source/multi-policy-validation.rst
+++ b/doc/source/multi-policy-validation.rst
@@ -20,7 +20,7 @@
Multi-policy support allows Patrole to more accurately offer RBAC tests for API
endpoints that enforce multiple policy actions.
-.. _this spec: https://github.com/openstack/qa-specs/blob/master/specs/patrole/rbac-testing-multiple-policies.rst
+.. _this spec: http://specs.openstack.org/openstack/qa-specs/specs/patrole/rbac-testing-multiple-policies.html
Scope
-----
diff --git a/doc/source/rbac-overview.rst b/doc/source/rbac-overview.rst
index cc47f75..747eab8 100644
--- a/doc/source/rbac-overview.rst
+++ b/doc/source/rbac-overview.rst
@@ -271,8 +271,8 @@
Related term: :term:`hard authorization`.
-.. _Nova repository: https://github.com/openstack/nova/tree/master/nova/policies
-.. _Keystone repository: https://github.com/openstack/keystone/tree/master/keystone/common/policies
+.. _Nova repository: https://git.openstack.org/cgit/openstack/nova/tree/nova/policies
+.. _Keystone repository: https://git.openstack.org/cgit/openstack/keystone/tree/keystone/common/policies
.. _governance goal: https://governance.openstack.org/tc/goals/queens/policy-in-code.html
.. _scope types: https://docs.openstack.org/keystone/latest/admin/identity-tokens.html#authorization-scopes
.. _policy.yaml: https://docs.openstack.org/ocata/config-reference/policy-yaml-file.html
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 62337f7..63f0a8a 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -96,6 +96,11 @@
* add_image
allowed_role = the ``oslo.policy`` role that is allowed to perform the API.
+"""),
+ cfg.BoolOpt('validate_deprecated_rules', default=True,
+ help="""Some of the policy rules have deprecated version,
+Patrole should be able to run check against default and deprecated rules,
+otherwise the result of the tests may not be correct.
""")
]
diff --git a/patrole_tempest_plugin/policy_authority.py b/patrole_tempest_plugin/policy_authority.py
index e0a26a3..9c25e5f 100644
--- a/patrole_tempest_plugin/policy_authority.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -169,6 +169,27 @@
is_admin=is_admin_context)
return is_allowed
+ def _handle_deprecated_rule(self, default):
+ deprecated_rule = default.deprecated_rule
+ deprecated_msg = (
+ 'Policy "%(old_name)s":"%(old_check_str)s" was deprecated in '
+ '%(release)s in favor of "%(name)s":"%(check_str)s". Reason: '
+ '%(reason)s. Either ensure your deployment is ready for the new '
+ 'default or copy/paste the deprecated policy into your policy '
+ 'file and maintain it manually.' % {
+ 'old_name': deprecated_rule.name,
+ 'old_check_str': deprecated_rule.check_str,
+ 'release': default.deprecated_since,
+ 'name': default.name,
+ 'check_str': default.check_str,
+ 'reason': default.deprecated_reason
+ }
+ )
+ LOG.warn(deprecated_msg)
+ check_str = '(%s) or (%s)' % (default.check_str,
+ deprecated_rule.check_str)
+ return policy.RuleDefault(default.name, check_str)
+
def get_rules(self):
rules = policy.Rules()
# Check whether policy file exists and attempt to read it.
@@ -203,6 +224,12 @@
if self.service in policy_generator:
for rule in policy_generator[self.service]:
if rule.name not in rules:
+ if CONF.patrole.validate_deprecated_rules:
+ # NOTE (sergey.vilgelm):
+ # The `DocumentedRuleDefault` object has no
+ # `deprecated_rule` attribute in Pike
+ if getattr(rule, 'deprecated_rule', False):
+ rule = self._handle_deprecated_rule(rule)
rules[rule.name] = rule.check
elif str(rule.check) != str(rules[rule.name]):
msg = ("The same policy name: %s was found in the "
@@ -231,20 +258,24 @@
whether the given role is contained in context_is_admin. If it is not
in the policy file, then default to context_is_admin: admin.
"""
- if 'context_is_admin' in self.rules.keys():
+ if 'context_is_admin' in self.rules:
return self._allowed(
access=self._get_access_token(roles),
apply_rule='context_is_admin')
return CONF.identity.admin_role in roles
def _get_access_token(self, roles):
+ roles = {r.lower() for r in roles if r}
+
+ # Extend roles for an user with admin or member role
+ if 'admin' in roles:
+ roles.add('member')
+ if 'member' in roles:
+ roles.add('reader')
+
access_token = {
"token": {
- "roles": [
- {
- "name": role
- } for role in roles
- ],
+ "roles": [{'name': r} for r in roles],
"project_id": self.project_id,
"tenant_id": self.project_id,
"user_id": self.user_id
@@ -266,7 +297,7 @@
# than hard-coding it to True. is_admin_project cannot be determined
# from the role, but rather from project and domain names. For more
# information, see:
- # https://github.com/openstack/keystone/blob/37ce5417418f8acbd27f3dacb70c605b0fe48301/keystone/token/providers/common.py#L150
+ # https://git.openstack.org/cgit/openstack/keystone/tree/keystone/token/providers/common.py?id=37ce5417418f8acbd27f3dacb70c605b0fe48301#n150
access_data['is_admin_project'] = True
class Object(object):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
index 64e1300..5e38970 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
@@ -505,7 +505,7 @@
TODO(felipemonteiro): Once multiple policy testing is supported, this
test should also check for additional policies mentioned here:
- https://github.com/openstack/nova/blob/master/nova/policies/server_usage.py
+ https://git.openstack.org/cgit/openstack/nova/tree/nova/policies/server_usage.py?h=17.0.0
"""
expected_attrs = ('OS-SRV-USG:launched_at',
'OS-SRV-USG:terminated_at')
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
index a63192f..a8cc767 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
@@ -23,7 +23,7 @@
"""Tests the APIs that enforce the auth policy actions.
For more information about the auth policy actions, see:
- https://github.com/openstack/keystone/blob/master/keystone/common/policies/auth.py
+ https://git.openstack.org/cgit/openstack/keystone/tree/keystone/common/policies/auth.py
"""
# TODO(felipemonteiro): Add tests for identity:get_auth_catalog
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
index 4fa3937..35154eb 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
@@ -28,7 +28,7 @@
"""RBAC tests for domain configuration client.
Provides coverage for the following policy actions:
- https://github.com/openstack/keystone/blob/master/keystone/common/policies/domain_config.py
+ https://git.openstack.org/cgit/openstack/keystone/tree/keystone/common/policies/domain_config.py
"""
identity = {"driver": "ldap"}
diff --git a/patrole_tempest_plugin/tests/api/network/README.rst b/patrole_tempest_plugin/tests/api/network/README.rst
index 352af8a..6eaae08 100644
--- a/patrole_tempest_plugin/tests/api/network/README.rst
+++ b/patrole_tempest_plugin/tests/api/network/README.rst
@@ -41,7 +41,7 @@
possible (such as ``neutron_tempest_plugin.api.clients`` for the service
clients) because the module is not a `stable interface`_.
-.. _policy.json file: https://github.com/openstack/neutron/blob/master/etc/policy.json
-.. _Zuul jobs: https://github.com/openstack/patrole/blob/master/.zuul.yaml
-.. _neutron-tempest-plugin: https://github.com/openstack/neutron-tempest-plugin
-.. _stable interface: https://github.com/openstack/neutron-tempest-plugin/tree/master/neutron_tempest_plugin#warning
+.. _policy.json file: https://git.openstack.org/cgit/openstack/neutron/tree/etc/policy.json?h=12.0.0
+.. _Zuul jobs: https://git.openstack.org/cgit/openstack/patrole/tree/.zuul.yaml
+.. _neutron-tempest-plugin: https://git.openstack.org/cgit/openstack/neutron-tempest-plugin
+.. _stable interface: https://git.openstack.org/cgit/openstack/neutron-tempest-plugin/plain/neutron_tempest_plugin/README.rst
diff --git a/patrole_tempest_plugin/tests/unit/resources/admin_rbac_policy.json b/patrole_tempest_plugin/tests/unit/resources/admin_rbac_policy.json
index 7828921..d6d9605 100644
--- a/patrole_tempest_plugin/tests/unit/resources/admin_rbac_policy.json
+++ b/patrole_tempest_plugin/tests/unit/resources/admin_rbac_policy.json
@@ -2,5 +2,6 @@
"admin_rule": "role:admin",
"is_admin_rule": "is_admin:True",
"alt_admin_rule": "is_admin:True or (role:admin and is_project_admin:True)",
- "non_admin_rule": "role:Member"
+ "member_rule": "role:member",
+ "reader_rule": "role:reader"
}
diff --git a/patrole_tempest_plugin/tests/unit/test_policy_authority.py b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
index 6a4d219..90e45f9 100644
--- a/patrole_tempest_plugin/tests/unit/test_policy_authority.py
+++ b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
@@ -83,9 +83,29 @@
for name, check in rules.items():
fake_rule = mock.Mock(check=check, __name__='foo')
fake_rule.name = name
+ fake_rule.deprecated_rule = False
fake_rules.append(fake_rule)
return fake_rules
+ def _test_policy_file(self, roles, allowed_rules,
+ disallowed_rules, authority=None, service=None):
+ if authority is None:
+ self.assertIsNotNone(service)
+ test_tenant_id = mock.sentinel.tenant_id
+ test_user_id = mock.sentinel.user_id
+ authority = policy_authority.PolicyAuthority(
+ test_tenant_id, test_user_id, service)
+
+ for rule in allowed_rules:
+ allowed = authority.allowed(rule, roles)
+ self.assertTrue(allowed)
+
+ for rule in disallowed_rules:
+ allowed = authority.allowed(rule, roles)
+ self.assertFalse(allowed)
+
+ return authority
+
@mock.patch.object(policy_authority, 'LOG', autospec=True)
def _test_custom_policy(self, *args):
default_roles = ['zero', 'one', 'two', 'three', 'four',
@@ -145,23 +165,14 @@
self.assertFalse(authority.allowed(rule, test_roles))
def test_empty_rbac_test_roles(self):
- test_tenant_id = mock.sentinel.tenant_id
- test_user_id = mock.sentinel.user_id
- authority = policy_authority.PolicyAuthority(
- test_tenant_id, test_user_id, "custom_rbac_policy")
-
- disallowed_for_empty_roles = ['policy_action_1', 'policy_action_2',
- 'policy_action_3', 'policy_action_4',
- 'policy_action_6']
-
- # Due to "policy_action_5": "rule:all_rule" / "all_rule": ""
- allowed_for_empty_roles = ['policy_action_5']
-
- for rule in disallowed_for_empty_roles:
- self.assertFalse(authority.allowed(rule, []))
-
- for rule in allowed_for_empty_roles:
- self.assertTrue(authority.allowed(rule, []))
+ self._test_policy_file(
+ roles=[],
+ allowed_rules=['policy_action_5'],
+ disallowed_rules=['policy_action_1', 'policy_action_2',
+ 'policy_action_3', 'policy_action_4',
+ 'policy_action_6'],
+ service="custom_rbac_policy"
+ )
def test_custom_policy_json(self):
# The CONF.patrole.custom_policy_files has a path to JSON file by
@@ -184,75 +195,47 @@
self._test_custom_multi_roles_policy()
def test_admin_policy_file_with_admin_role(self):
- test_tenant_id = mock.sentinel.tenant_id
- test_user_id = mock.sentinel.user_id
- authority = policy_authority.PolicyAuthority(
- test_tenant_id, test_user_id, "admin_rbac_policy")
-
- roles = ['admin']
- allowed_rules = [
- 'admin_rule', 'is_admin_rule', 'alt_admin_rule'
- ]
- disallowed_rules = ['non_admin_rule']
-
- for rule in allowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertTrue(allowed)
-
- for rule in disallowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertFalse(allowed)
+ # admin role implies member and reader roles
+ self._test_policy_file(
+ roles=['admin'],
+ allowed_rules=['admin_rule', 'is_admin_rule', 'alt_admin_rule',
+ 'member_rule', 'reader_rule'],
+ disallowed_rules=[],
+ service="admin_rbac_policy"
+ )
def test_admin_policy_file_with_member_role(self):
- test_tenant_id = mock.sentinel.tenant_id
- test_user_id = mock.sentinel.user_id
- authority = policy_authority.PolicyAuthority(
- test_tenant_id, test_user_id, "admin_rbac_policy")
+ # member role implies reader role
+ self._test_policy_file(
+ roles=['member'],
+ allowed_rules=['member_rule', 'reader_rule'],
+ disallowed_rules=['admin_rule', 'is_admin_rule', 'alt_admin_rule'],
+ service="admin_rbac_policy"
+ )
- roles = ['Member']
- allowed_rules = [
- 'non_admin_rule'
- ]
- disallowed_rules = [
- 'admin_rule', 'is_admin_rule', 'alt_admin_rule']
-
- for rule in allowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertTrue(allowed)
-
- for rule in disallowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertFalse(allowed)
+ def test_admin_policy_file_with_reader_role(self):
+ self._test_policy_file(
+ roles=['reader'],
+ allowed_rules=['reader_rule'],
+ disallowed_rules=['admin_rule', 'is_admin_rule', 'alt_admin_rule',
+ 'member_rule'],
+ service="admin_rbac_policy"
+ )
def test_alt_admin_policy_file_with_context_is_admin(self):
- test_tenant_id = mock.sentinel.tenant_id
- test_user_id = mock.sentinel.user_id
- authority = policy_authority.PolicyAuthority(
- test_tenant_id, test_user_id, "alt_admin_rbac_policy")
+ authority = self._test_policy_file(
+ roles=['fake_admin'],
+ allowed_rules=['non_admin_rule'],
+ disallowed_rules=['admin_rule'],
+ service="alt_admin_rbac_policy"
+ )
- roles = ['fake_admin']
- allowed_rules = ['non_admin_rule']
- disallowed_rules = ['admin_rule']
-
- for rule in allowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertTrue(allowed)
-
- for rule in disallowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertFalse(allowed)
-
- roles = ['super_admin']
- allowed_rules = ['admin_rule']
- disallowed_rules = ['non_admin_rule']
-
- for rule in allowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertTrue(allowed)
-
- for rule in disallowed_rules:
- allowed = authority.allowed(rule, roles)
- self.assertFalse(allowed)
+ self._test_policy_file(
+ roles=['super_admin'],
+ allowed_rules=['admin_rule'],
+ disallowed_rules=['non_admin_rule'],
+ authority=authority
+ )
def test_tenant_user_policy(self):
"""Test whether rules with format tenant_id/user_id formatting work.
@@ -261,26 +244,25 @@
network:tenant_id pass. And test whether Nova rules that contain
user_id pass.
"""
- test_tenant_id = mock.sentinel.tenant_id
- test_user_id = mock.sentinel.user_id
- authority = policy_authority.PolicyAuthority(
- test_tenant_id, test_user_id, "tenant_rbac_policy")
+ allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
+ disallowed_rules = ['admin_tenant_rule', 'admin_user_rule']
# Check whether Member role can perform expected actions.
- allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
- for rule in allowed_rules:
- allowed = authority.allowed(rule, ['Member'])
- self.assertTrue(allowed)
-
- disallowed_rules = ['admin_tenant_rule', 'admin_user_rule']
- for disallowed_rule in disallowed_rules:
- self.assertFalse(authority.allowed(disallowed_rule, ['Member']))
+ authority = self._test_policy_file(
+ roles=['member'],
+ allowed_rules=allowed_rules,
+ disallowed_rules=disallowed_rules,
+ service="tenant_rbac_policy",
+ )
# Check whether admin role can perform expected actions.
allowed_rules.extend(disallowed_rules)
- for rule in allowed_rules:
- allowed = authority.allowed(rule, ['admin'])
- self.assertTrue(allowed)
+ self._test_policy_file(
+ roles=['admin'],
+ allowed_rules=allowed_rules,
+ disallowed_rules=[],
+ authority=authority
+ )
# Check whether _try_rule is called with the correct target dictionary.
with mock.patch.object(
@@ -295,7 +277,7 @@
}
expected_access_data = {
- "roles": ['Member'],
+ "roles": sorted(['member', 'reader']),
"is_admin": False,
"is_admin_project": True,
"user_id": mock.sentinel.user_id,
@@ -304,8 +286,11 @@
}
for rule in allowed_rules:
- allowed = authority.allowed(rule, ['Member'])
+ allowed = authority.allowed(rule, ['member'])
self.assertTrue(allowed)
+ # for sure that roles are in same order
+ mock_try_rule.call_args[0][2]["roles"] = sorted(
+ mock_try_rule.call_args[0][2]["roles"])
mock_try_rule.assert_called_once_with(
rule, expected_target, expected_access_data, mock.ANY)
mock_try_rule.reset_mock()
diff --git a/releasenotes/notes/support-deprecated-roles-eae9dc742cb4fa33.yaml b/releasenotes/notes/support-deprecated-roles-eae9dc742cb4fa33.yaml
new file mode 100644
index 0000000..0d581a0
--- /dev/null
+++ b/releasenotes/notes/support-deprecated-roles-eae9dc742cb4fa33.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Patrole will validate the deprecated policy rules (if applicable) alongside
+ the current policy rule.
+ Add ``[patrole] validate_deprecated_rules`` enabled by default to validate
+ the deprecated rules.