Merge "Creates config options for policy.json paths"
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index b532d63..e342dd8 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -50,3 +50,13 @@
# not found in the policy.json. Otherwise, they throw a
# skipException.
strict_policy_check = False
+
+ # The following config options set the location of the service's
+ # policy file. For services that have their policy in code (e.g.,
+ # Nova), this would be the location of a custom policy.json, if
+ # one exists.
+ cinder_policy_file = /etc/cinder/policy.json
+ glance_policy_file = /etc/glance/policy.json
+ keystone_policy_file = /etc/keystone/policy.json
+ neutron_policy_file = /etc/neutron/policy.json
+ nova_policy_file = /etc/nova/policy.json
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index dfb6cef..cb00269 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -30,5 +30,20 @@
default=False,
help="If true, throws RbacParsingException for"
" policies which don't exist. If false, "
- "throws skipException.")
+ "throws skipException."),
+ cfg.StrOpt('cinder_policy_file',
+ default='/etc/cinder/policy.json',
+ help="Location of the neutron policy file."),
+ cfg.StrOpt('glance_policy_file',
+ default='/etc/glance/policy.json',
+ help="Location of the glance policy file."),
+ cfg.StrOpt('keystone_policy_file',
+ default='/etc/keystone/policy.json',
+ help="Location of the keystone policy file."),
+ cfg.StrOpt('neutron_policy_file',
+ default='/etc/neutron/policy.json',
+ help="Location of the neutron policy file."),
+ cfg.StrOpt('nova_policy_file',
+ default='/etc/nova/policy.json',
+ help="Location of the nova policy file.")
]
diff --git a/patrole_tempest_plugin/rbac_policy_parser.py b/patrole_tempest_plugin/rbac_policy_parser.py
index 2686736..e68921f 100644
--- a/patrole_tempest_plugin/rbac_policy_parser.py
+++ b/patrole_tempest_plugin/rbac_policy_parser.py
@@ -40,7 +40,7 @@
each role, whether a given rule is allowed using oslo policy.
"""
- def __init__(self, project_id, user_id, service=None, path=None,
+ def __init__(self, project_id, user_id, service=None,
extra_target_data={}):
"""Initialization of Rbac Policy Parser.
@@ -76,7 +76,12 @@
# Use default path in /etc/<service_name/policy.json if no path
# is provided.
- self.path = path or os.path.join('/etc', service, 'policy.json')
+ path = getattr(CONF.rbac, '%s_policy_file' % str(service), None)
+ if not path:
+ LOG.info("No config option found for %s,"
+ " using default path" % str(service))
+ path = os.path.join('/etc', service, 'policy.json')
+ self.path = path
self.rules = policy.Rules.load(self._get_policy_data(service),
'default')
self.project_id = project_id
@@ -98,12 +103,18 @@
# Check whether policy file exists.
if os.path.isfile(self.path):
- with open(self.path, 'r') as policy_file:
- file_policy_data = policy_file.read()
try:
+ with open(self.path, 'r') as policy_file:
+ file_policy_data = policy_file.read()
file_policy_data = json.loads(file_policy_data)
- except ValueError:
- file_policy_data = None
+ except (IOError, ValueError) as e:
+ msg = "Failed to read policy file for service. "
+ if isinstance(e, IOError):
+ msg += "Please check that policy path exists."
+ else:
+ msg += "JSON may be improperly formatted."
+ LOG.debug(msg)
+ file_policy_data = {}
# Check whether policy actions are defined in code. Nova and Keystone,
# for example, define their default policy actions in code.
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
index 0906222..6889b44 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
@@ -32,6 +32,8 @@
super(RbacPolicyTest, self).setUp()
self.mock_admin_mgr = mock.patch.object(
rbac_policy_parser, 'credentials').start()
+ self.mock_path = mock.patch.object(
+ rbac_policy_parser, 'os').start()
current_directory = os.path.dirname(os.path.realpath(__file__))
self.custom_policy_file = os.path.join(current_directory,
@@ -88,8 +90,9 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
+ self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.custom_policy_file)
+ test_tenant_id, test_user_id, "test")
expected = {
'policy_action_1': ['two', 'four', 'six', 'eight'],
@@ -110,8 +113,9 @@
def test_admin_policy_file_with_admin_role(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
+ self.mock_path.path.join.return_value = self.admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.admin_policy_file)
+ test_tenant_id, test_user_id, "test")
role = 'admin'
allowed_rules = [
@@ -130,8 +134,9 @@
def test_admin_policy_file_with_member_role(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
+ self.mock_path.path.join.return_value = self.admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.admin_policy_file)
+ test_tenant_id, test_user_id, "test")
role = 'Member'
allowed_rules = [
@@ -151,8 +156,9 @@
def test_admin_policy_file_with_context_is_admin(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
+ self.mock_path.path.join.return_value = self.alt_admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.alt_admin_policy_file)
+ test_tenant_id, test_user_id, "test")
role = 'fake_admin'
allowed_rules = ['non_admin_rule']
@@ -187,8 +193,9 @@
"""
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
+ self.mock_path.path.join.return_value = self.tenant_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+ test_tenant_id, test_user_id, "test")
# Check whether Member role can perform expected actions.
allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
@@ -268,9 +275,9 @@
def test_invalid_policy_rule_throws_rbac_parsing_exception(self, m_log):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
-
+ self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.custom_policy_file)
+ test_tenant_id, test_user_id, "test")
fake_rule = 'fake_rule'
expected_message = "Policy action: {0} not found in policy file: {1}."\
@@ -285,9 +292,9 @@
def test_unknown_exception_throws_rbac_parsing_exception(self, m_log):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
-
+ self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.custom_policy_file)
+ test_tenant_id, test_user_id, "test")
parser.rules = mock.MagicMock(
**{'__getitem__.return_value.side_effect': Exception(
mock.sentinel.error)})
@@ -320,9 +327,9 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
-
+ self.mock_path.path.join.return_value = self.tenant_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+ test_tenant_id, test_user_id, "test")
policy_data = parser._get_policy_data('fake_service')
@@ -364,9 +371,9 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
-
+ self.mock_path.path.join.return_value = self.tenant_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+ test_tenant_id, test_user_id, "test")
policy_data = parser._get_policy_data('fake_service')
@@ -393,10 +400,10 @@
mock_creds.AdminManager.return_value.identity_services_v3_client.\
list_services.return_value = {
'services': [{'name': 'test_service'}]}
-
+ self.mock_path.path.join.return_value = '/etc/test_service/policy.json'
e = self.assertRaises(rbac_exceptions.RbacParsingException,
rbac_policy_parser.RbacPolicyParser,
- None, None, 'test_service', None)
+ None, None, 'test_service')
expected_error = \
'Policy file for {0} service neither found in code '\
@@ -405,14 +412,12 @@
self.assertIn(expected_error, str(e))
- @mock.patch.object(rbac_policy_parser, 'os', autospec=True)
@mock.patch.object(rbac_policy_parser, 'json', autospec=True)
@mock.patch.object(rbac_policy_parser, 'credentials', autospec=True)
@mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
def test_get_policy_data_without_valid_policy(self, mock_stevedore,
- mock_credentials, mock_json,
- mock_os):
- mock_os.path.isfile.return_value = False
+ mock_credentials, mock_json):
+ self.mock_path.path.isfile.return_value = False
test_policy_action = mock.Mock(check='rule:bar')
test_policy_action.configure_mock(name='foo')
@@ -432,7 +437,7 @@
e = self.assertRaises(rbac_exceptions.RbacParsingException,
rbac_policy_parser.RbacPolicyParser,
- None, None, 'test_service', None)
+ None, None, 'test_service')
expected_error = "Policy file for {0} service is invalid."\
.format("test_service")
@@ -459,11 +464,10 @@
}
mock_stevedore.named.NamedExtensionManager.return_value = None
mock_json.loads.side_effect = ValueError
-
+ self.mock_path.path.join.return_value = self.tenant_policy_file
e = self.assertRaises(rbac_exceptions.RbacParsingException,
rbac_policy_parser.RbacPolicyParser,
- None, None, 'test_service',
- self.tenant_policy_file)
+ None, None, 'test_service')
expected_error = 'Policy file for {0} service neither found in code '\
'nor at {1}.'.format('test_service',
diff --git a/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml b/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml
new file mode 100644
index 0000000..3e63c9d
--- /dev/null
+++ b/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Refactored framework to remove unused "path"
+ argument. Added config options to allow the path
+ to the policy.json files for Nova, Keystone, Cinder,
+ Neutron, and Glance to be configured without needing
+ to manually change code.