From c14af5c023f7c614bb17b03762ff50114bb5a9b0 Mon Sep 17 00:00:00 2001 From: Hieu LE Date: Wed, 4 Oct 2017 14:37:49 +0700 Subject: [PATCH] Register default quota policies in code This commit uses the existing policy-in-code module to move all default policies for quotas into code. This commit also adds helpful documentation about each API those policies protect, which will be generated in sample policy files. Co-authored-By: Dai Dang-Van Implements: blueprint policy-in-code Change-Id: I2fbd7577545ed08dee10064d321e8c6941324b5d --- etc/magnum/policy.json | 8 -- magnum/common/policies/__init__.py | 4 +- magnum/common/policies/quota.py | 80 +++++++++++++++++++ magnum/tests/fake_policy.py | 2 - .../api/controllers/v1/test_certificate.py | 8 +- .../unit/api/controllers/v1/test_quota.py | 40 +++++++--- 6 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 magnum/common/policies/quota.py diff --git a/etc/magnum/policy.json b/etc/magnum/policy.json index 58f633f5ff..ad008eb2ee 100644 --- a/etc/magnum/policy.json +++ b/etc/magnum/policy.json @@ -1,12 +1,4 @@ { - "default": "rule:admin_or_owner", - - "quotas:get": "rule:default", - "quotas:get_all": "rule:admin_api", - "quotas:create": "rule:admin_api", - "quotas:update": "rule:admin_api", - "quotas:delete": "rule:admin_api", - "certificate:rotate_ca": "rule:admin_or_owner", "certificate:create": "rule:admin_or_user or rule:cluster_user", "certificate:get": "rule:admin_or_user or rule:cluster_user", diff --git a/magnum/common/policies/__init__.py b/magnum/common/policies/__init__.py index e5f115bb5f..d191625c69 100644 --- a/magnum/common/policies/__init__.py +++ b/magnum/common/policies/__init__.py @@ -19,6 +19,7 @@ from magnum.common.policies import bay from magnum.common.policies import baymodel from magnum.common.policies import cluster from magnum.common.policies import cluster_template +from magnum.common.policies import quota def list_rules(): @@ -27,5 +28,6 @@ def list_rules(): bay.list_rules(), baymodel.list_rules(), cluster.list_rules(), - cluster_template.list_rules() + cluster_template.list_rules(), + quota.list_rules() ) diff --git a/magnum/common/policies/quota.py b/magnum/common/policies/quota.py new file mode 100644 index 0000000000..83e1deaaf3 --- /dev/null +++ b/magnum/common/policies/quota.py @@ -0,0 +1,80 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from oslo_policy import policy + +from magnum.common.policies import base + +QUOTA = 'quotas:%s' + +rules = [ + policy.DocumentedRuleDefault( + name=QUOTA % 'create', + check_str=base.RULE_ADMIN_API, + description='Create quota.', + operations=[ + { + 'path': '/v1/quotas', + 'method': 'POST' + } + ] + ), + policy.DocumentedRuleDefault( + name=QUOTA % 'delete', + check_str=base.RULE_ADMIN_API, + description='Delete quota for a given project_id and resource.', + operations=[ + { + 'path': '/v1/quotas/{project_id}/{resource}', + 'method': 'DELETE' + } + ] + ), + policy.DocumentedRuleDefault( + name=QUOTA % 'get', + check_str=base.RULE_ADMIN_OR_OWNER, + description='Retrieve Quota information for the given project_id.', + operations=[ + { + 'path': '/v1/quotas/{project_id}/{resource}', + 'method': 'GET' + } + ] + ), + policy.DocumentedRuleDefault( + name=QUOTA % 'get_all', + check_str=base.RULE_ADMIN_API, + description='Retrieve a list of quotas.', + operations=[ + { + 'path': '/v1/quotas', + 'method': 'GET' + } + ] + ), + policy.DocumentedRuleDefault( + name=QUOTA % 'update', + check_str=base.RULE_ADMIN_API, + description='Update quota for a given project_id.', + operations=[ + { + 'path': '/v1/quotas/{project_id}/{resource}', + 'method': 'PATCH' + } + ] + ) +] + + +def list_rules(): + return rules diff --git a/magnum/tests/fake_policy.py b/magnum/tests/fake_policy.py index 3fb36bc501..ed765c9f6a 100644 --- a/magnum/tests/fake_policy.py +++ b/magnum/tests/fake_policy.py @@ -15,8 +15,6 @@ policy_data = """ { - "default": "rule:admin_or_owner", - "certificate:create": "", "certificate:get": "", diff --git a/magnum/tests/unit/api/controllers/v1/test_certificate.py b/magnum/tests/unit/api/controllers/v1/test_certificate.py index cf84e0cb61..e65af08a4f 100644 --- a/magnum/tests/unit/api/controllers/v1/test_certificate.py +++ b/magnum/tests/unit/api/controllers/v1/test_certificate.py @@ -201,7 +201,9 @@ class TestRotateCaCertificate(api_base.FunctionalTest): self.conductor_api_class.return_value = self.conductor_api self.addCleanup(conductor_api_patcher.stop) - def test_rotate_ca_cert(self): + @mock.patch("magnum.common.policy.enforce") + def test_rotate_ca_cert(self, mock_policy): + mock_policy.return_value = True fake_cert = api_utils.cert_post_data() mock_cert = mock.MagicMock() mock_cert.as_dict.return_value = fake_cert @@ -227,7 +229,9 @@ class TestRotateCaCertificateNonTls(api_base.FunctionalTest): self.conductor_api_class.return_value = self.conductor_api self.addCleanup(conductor_api_patcher.stop) - def test_rotate_ca_cert_non_tls(self): + @mock.patch("magnum.common.policy.enforce") + def test_rotate_ca_cert_non_tls(self, mock_policy): + mock_policy.return_value = True fake_cert = api_utils.cert_post_data() mock_cert = mock.MagicMock() mock_cert.as_dict.return_value = fake_cert diff --git a/magnum/tests/unit/api/controllers/v1/test_quota.py b/magnum/tests/unit/api/controllers/v1/test_quota.py index b3d776e710..0fe1acd396 100644 --- a/magnum/tests/unit/api/controllers/v1/test_quota.py +++ b/magnum/tests/unit/api/controllers/v1/test_quota.py @@ -36,24 +36,32 @@ class TestQuota(api_base.FunctionalTest): def setUp(self): super(TestQuota, self).setUp() - def test_empty(self): + @mock.patch("magnum.common.policy.enforce") + def test_empty(self, mock_policy): + mock_policy.return_value = True response = self.get_json('/quotas') self.assertEqual([], response['quotas']) - def test_one(self): + @mock.patch("magnum.common.policy.enforce") + def test_one(self, mock_policy): + mock_policy.return_value = True quota = obj_utils.create_test_quota(self.context) response = self.get_json('/quotas') self.assertEqual(quota.project_id, response['quotas'][0]["project_id"]) self._verify_attrs(self._quota_attrs, response['quotas'][0]) - def test_get_one(self): + @mock.patch("magnum.common.policy.enforce") + def test_get_one(self, mock_policy): + mock_policy.return_value = True quota = obj_utils.create_test_quota(self.context) response = self.get_json('/quotas/%s/%s' % (quota['project_id'], quota['resource'])) self.assertEqual(quota.project_id, response['project_id']) self.assertEqual(quota.resource, response['resource']) - def test_get_one_not_found(self): + @mock.patch("magnum.common.policy.enforce") + def test_get_one_not_found(self, mock_policy): + mock_policy.return_value = True response = self.get_json( '/quotas/fake_project/invalid_res', expect_errors=True) @@ -174,7 +182,9 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(1, len(response['quotas'])) self.assertEqual('proj-id-1', response['quotas'][0]['project_id']) - def test_get_all_non_admin(self): + @mock.patch("magnum.common.policy.enforce") + def test_get_all_non_admin(self, mock_policy): + mock_policy.return_value = True quota_list = [] for i in range(4): quota = obj_utils.create_test_quota(self.context, @@ -186,8 +196,10 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(1, len(response['quotas'])) self.assertEqual('proj-id-2', response['quotas'][0]['project_id']) + @mock.patch("magnum.common.policy.enforce") @mock.patch.object(clients.OpenStackClients, 'keystone') - def test_create_quota(self, mock_keystone): + def test_create_quota(self, mock_keystone, mock_policy): + mock_policy.return_value = True quota_dict = apiutils.quota_post_data() response = self.post_json('/quotas', quota_dict) self.assertEqual('application/json', response.content_type) @@ -224,8 +236,10 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(400, response.status_int) self.assertTrue(response.json['errors']) + @mock.patch("magnum.common.policy.enforce") @mock.patch.object(clients.OpenStackClients, 'keystone') - def test_create_quota_no_project_id(self, mock_keystone): + def test_create_quota_no_project_id(self, mock_keystone, mock_policy): + mock_policy.return_value = True quota_dict = apiutils.quota_post_data() del quota_dict['project_id'] response = self.post_json('/quotas', quota_dict, expect_errors=True) @@ -233,8 +247,10 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(400, response.status_int) self.assertTrue(response.json['errors']) + @mock.patch("magnum.common.policy.enforce") @mock.patch.object(clients.OpenStackClients, 'keystone') - def test_patch_quota(self, mock_keystone): + def test_patch_quota(self, mock_keystone, mock_policy): + mock_policy.return_value = True quota_dict = apiutils.quota_post_data(hard_limit=5) response = self.post_json('/quotas', quota_dict) self.assertEqual('application/json', response.content_type) @@ -248,8 +264,10 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(202, response.status_int) self.assertEqual(20, response.json['hard_limit']) + @mock.patch("magnum.common.policy.enforce") @mock.patch.object(clients.OpenStackClients, 'keystone') - def test_patch_quota_not_found(self, mock_keystone): + def test_patch_quota_not_found(self, mock_keystone, mock_policy): + mock_policy.return_value = True quota_dict = apiutils.quota_post_data() response = self.post_json('/quotas', quota_dict) self.assertEqual('application/json', response.content_type) @@ -264,8 +282,10 @@ class TestQuota(api_base.FunctionalTest): self.assertEqual(404, response.status_int) self.assertTrue(response.json['errors']) + @mock.patch("magnum.common.policy.enforce") @mock.patch.object(clients.OpenStackClients, 'keystone') - def test_delete_quota(self, mock_keystone): + def test_delete_quota(self, mock_keystone, mock_policy): + mock_policy.return_value = True quota_dict = apiutils.quota_post_data() response = self.post_json('/quotas', quota_dict) self.assertEqual('application/json', response.content_type)