diff options
author | Tzu-Mainn Chen <tzumainn@redhat.com> | 2019-10-14 17:55:41 +0000 |
---|---|---|
committer | Tzu-Mainn Chen <tzumainn@redhat.com> | 2019-11-14 15:49:50 +0000 |
commit | 8253826e86928cd722e1363ca3701f48b36b8dd0 (patch) | |
tree | 63d4bd4ad56bbbb6c8ed152660517341222014dc /ironic/tests/unit/api/controllers/v1/test_utils.py | |
parent | 630c85126bd272884bb987b70b7dfcfcdb5d6f50 (diff) | |
download | ironic-8253826e86928cd722e1363ca3701f48b36b8dd0.tar.gz |
Allow node owners to administer nodes
Introduce is_node_owner to policy, giving Ironic admins
the option of modifying the policy file to allow users
specified by a node's owner field to perform API actions
on that node.
Change-Id: If08586f3e9705dd38ff83e4b500d9ee3cd45bce3
Story: #2006506
Task: #37214
Diffstat (limited to 'ironic/tests/unit/api/controllers/v1/test_utils.py')
-rw-r--r-- | ironic/tests/unit/api/controllers/v1/test_utils.py | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/ironic/tests/unit/api/controllers/v1/test_utils.py b/ironic/tests/unit/api/controllers/v1/test_utils.py index d18fded4a..1044a6015 100644 --- a/ironic/tests/unit/api/controllers/v1/test_utils.py +++ b/ironic/tests/unit/api/controllers/v1/test_utils.py @@ -770,3 +770,207 @@ class TestPortgroupIdent(base.TestCase): self.assertRaises(exception.InvalidUuidOrName, utils.get_rpc_portgroup, self.invalid_name) + + +class TestCheckNodePolicyAndRetrieve(base.TestCase): + def setUp(self): + super(TestCheckNodePolicyAndRetrieve, self).setUp() + self.valid_node_uuid = uuidutils.generate_uuid() + self.node = test_api_utils.post_get_test_node() + self.node['owner'] = '12345' + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + @mock.patch.object(utils, 'get_rpc_node') + @mock.patch.object(utils, 'get_rpc_node_with_suffix') + def test_check_node_policy_and_retrieve( + self, mock_grnws, mock_grn, mock_authorize, mock_pr + ): + mock_pr.version.minor = 50 + mock_pr.context.to_policy_values.return_value = {} + mock_grn.return_value = self.node + + rpc_node = utils.check_node_policy_and_retrieve( + 'fake_policy', self.valid_node_uuid + ) + mock_grn.assert_called_once_with(self.valid_node_uuid) + mock_grnws.assert_not_called() + mock_authorize.assert_called_once_with( + 'fake_policy', {'node.owner': '12345'}, {}) + self.assertEqual(self.node, rpc_node) + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + @mock.patch.object(utils, 'get_rpc_node') + @mock.patch.object(utils, 'get_rpc_node_with_suffix') + def test_check_node_policy_and_retrieve_with_suffix( + self, mock_grnws, mock_grn, mock_authorize, mock_pr + ): + mock_pr.version.minor = 50 + mock_pr.context.to_policy_values.return_value = {} + mock_grnws.return_value = self.node + + rpc_node = utils.check_node_policy_and_retrieve( + 'fake_policy', self.valid_node_uuid, True + ) + mock_grn.assert_not_called() + mock_grnws.assert_called_once_with(self.valid_node_uuid) + mock_authorize.assert_called_once_with( + 'fake_policy', {'node.owner': '12345'}, {}) + self.assertEqual(self.node, rpc_node) + + @mock.patch.object(api, 'request', spec_set=["context"]) + @mock.patch.object(policy, 'authorize', spec=True) + @mock.patch.object(utils, 'get_rpc_node') + def test_check_node_policy_and_retrieve_no_node_policy_forbidden( + self, mock_grn, mock_authorize, mock_pr + ): + mock_pr.context.to_policy_values.return_value = {} + mock_authorize.side_effect = exception.HTTPForbidden(resource='fake') + mock_grn.side_effect = exception.NodeNotFound( + node=self.valid_node_uuid) + + self.assertRaises( + exception.HTTPForbidden, + utils.check_node_policy_and_retrieve, + 'fake-policy', + self.valid_node_uuid + ) + + @mock.patch.object(api, 'request', spec_set=["context"]) + @mock.patch.object(policy, 'authorize', spec=True) + @mock.patch.object(utils, 'get_rpc_node') + def test_check_node_policy_and_retrieve_no_node( + self, mock_grn, mock_authorize, mock_pr + ): + mock_pr.context.to_policy_values.return_value = {} + mock_grn.side_effect = exception.NodeNotFound( + node=self.valid_node_uuid) + + self.assertRaises( + exception.NodeNotFound, + utils.check_node_policy_and_retrieve, + 'fake-policy', + self.valid_node_uuid + ) + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + @mock.patch.object(utils, 'get_rpc_node') + def test_check_node_policy_and_retrieve_policy_forbidden( + self, mock_grn, mock_authorize, mock_pr + ): + mock_pr.version.minor = 50 + mock_pr.context.to_policy_values.return_value = {} + mock_authorize.side_effect = exception.HTTPForbidden(resource='fake') + mock_grn.return_value = self.node + + self.assertRaises( + exception.HTTPForbidden, + utils.check_node_policy_and_retrieve, + 'fake-policy', + self.valid_node_uuid + ) + + +class TestCheckNodeListPolicy(base.TestCase): + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy( + self, mock_authorize, mock_pr + ): + mock_pr.context.to_policy_values.return_value = { + 'project_id': '12345' + } + mock_pr.version.minor = 50 + + owner = utils.check_node_list_policy() + self.assertIsNone(owner) + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy_with_owner( + self, mock_authorize, mock_pr + ): + mock_pr.context.to_policy_values.return_value = { + 'project_id': '12345' + } + mock_pr.version.minor = 50 + + owner = utils.check_node_list_policy('12345') + self.assertEqual(owner, '12345') + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy_forbidden( + self, mock_authorize, mock_pr + ): + def mock_authorize_function(rule, target, creds): + raise exception.HTTPForbidden(resource='fake') + mock_authorize.side_effect = mock_authorize_function + mock_pr.context.to_policy_values.return_value = { + 'project_id': '12345' + } + mock_pr.version.minor = 50 + + self.assertRaises( + exception.HTTPForbidden, + utils.check_node_list_policy, + ) + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy_forbidden_no_project( + self, mock_authorize, mock_pr + ): + def mock_authorize_function(rule, target, creds): + if rule == 'baremetal:node:list_all': + raise exception.HTTPForbidden(resource='fake') + return True + mock_authorize.side_effect = mock_authorize_function + mock_pr.context.to_policy_values.return_value = {} + mock_pr.version.minor = 50 + + self.assertRaises( + exception.HTTPForbidden, + utils.check_node_list_policy, + ) + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy_non_admin( + self, mock_authorize, mock_pr + ): + def mock_authorize_function(rule, target, creds): + if rule == 'baremetal:node:list_all': + raise exception.HTTPForbidden(resource='fake') + return True + mock_authorize.side_effect = mock_authorize_function + mock_pr.context.to_policy_values.return_value = { + 'project_id': '12345' + } + mock_pr.version.minor = 50 + + owner = utils.check_node_list_policy() + self.assertEqual(owner, '12345') + + @mock.patch.object(api, 'request', spec_set=["context", "version"]) + @mock.patch.object(policy, 'authorize', spec=True) + def test_check_node_list_policy_non_admin_owner_proj_mismatch( + self, mock_authorize, mock_pr + ): + def mock_authorize_function(rule, target, creds): + if rule == 'baremetal:node:list_all': + raise exception.HTTPForbidden(resource='fake') + return True + mock_authorize.side_effect = mock_authorize_function + mock_pr.context.to_policy_values.return_value = { + 'project_id': '12345' + } + mock_pr.version.minor = 50 + + self.assertRaises( + exception.HTTPForbidden, + utils.check_node_list_policy, + '54321' + ) |