diff --git a/etc/magnum/policy.json b/etc/magnum/policy.json index dbb42df472..2f4edb65cf 100644 --- a/etc/magnum/policy.json +++ b/etc/magnum/policy.json @@ -9,5 +9,12 @@ "bay:detail": "rule:default", "bay:get": "rule:default", "bay:get_all": "rule:default", - "bay:update": "rule:default" + "bay:update": "rule:default", + + "baymodel:create": "rule:default", + "baymodel:delete": "rule:default", + "baymodel:detail": "rule:default", + "baymodel:get": "rule:default", + "baymodel:get_all": "rule:default", + "baymodel:update": "rule:default" } diff --git a/magnum/api/controllers/v1/baymodel.py b/magnum/api/controllers/v1/baymodel.py index 09ef7b97f2..3200854369 100644 --- a/magnum/api/controllers/v1/baymodel.py +++ b/magnum/api/controllers/v1/baymodel.py @@ -28,6 +28,7 @@ from magnum.api.controllers.v1 import types from magnum.api.controllers.v1 import utils as api_utils from magnum.common import clients from magnum.common import exception +from magnum.common import policy from magnum import objects @@ -222,6 +223,7 @@ class BayModelsController(rest.RestController): except glanceclient.exc.HTTPForbidden: raise exception.ImageNotAuthorized(image_id=image_ident) + @policy.enforce_wsgi("baymodel") @wsme_pecan.wsexpose(BayModelCollection, types.uuid, types.uuid, int, wtypes.text, wtypes.text) def get_all(self, baymodel_uuid=None, marker=None, limit=None, @@ -236,6 +238,7 @@ class BayModelsController(rest.RestController): return self._get_baymodels_collection(marker, limit, sort_key, sort_dir) + @policy.enforce_wsgi("baymodel") @wsme_pecan.wsexpose(BayModelCollection, types.uuid, types.uuid, int, wtypes.text, wtypes.text) def detail(self, baymodel_uuid=None, marker=None, limit=None, @@ -260,6 +263,7 @@ class BayModelsController(rest.RestController): sort_key, sort_dir, expand, resource_url) + @policy.enforce_wsgi("baymodel", "get") @wsme_pecan.wsexpose(BayModel, types.uuid_or_name) def get_one(self, baymodel_ident): """Retrieve information about the given baymodel. @@ -272,6 +276,7 @@ class BayModelsController(rest.RestController): rpc_baymodel = api_utils.get_rpc_resource('BayModel', baymodel_ident) return BayModel.convert_with_links(rpc_baymodel) + @policy.enforce_wsgi("baymodel", "create") @wsme_pecan.wsexpose(BayModel, body=BayModel, status_code=201) def post(self, baymodel): """Create a new baymodel. @@ -299,6 +304,7 @@ class BayModelsController(rest.RestController): new_baymodel.uuid) return BayModel.convert_with_links(new_baymodel) + @policy.enforce_wsgi("baymodel", "update") @wsme.validate(types.uuid, [BayModelPatchType]) @wsme_pecan.wsexpose(BayModel, types.uuid, body=[BayModelPatchType]) def patch(self, baymodel_uuid, patch): @@ -335,6 +341,7 @@ class BayModelsController(rest.RestController): rpc_baymodel.save() return BayModel.convert_with_links(rpc_baymodel) + @policy.enforce_wsgi("baymodel") @wsme_pecan.wsexpose(None, types.uuid_or_name, status_code=204) def delete(self, baymodel_ident): """Delete a baymodel. diff --git a/magnum/tests/fake_policy.py b/magnum/tests/fake_policy.py index f26d3b5f74..b81aab0350 100644 --- a/magnum/tests/fake_policy.py +++ b/magnum/tests/fake_policy.py @@ -25,7 +25,14 @@ policy_data = """ "bay:detail": "", "bay:get": "", "bay:get_all": "", - "bay:update": "" + "bay:update": "", + + "baymodel:create": "", + "baymodel:delete": "", + "baymodel:detail": "", + "baymodel:get": "", + "baymodel:get_all": "", + "baymodel:update": "" } """ diff --git a/magnum/tests/unit/api/controllers/v1/test_baymodel.py b/magnum/tests/unit/api/controllers/v1/test_baymodel.py index cc7e14718d..84a808fab2 100644 --- a/magnum/tests/unit/api/controllers/v1/test_baymodel.py +++ b/magnum/tests/unit/api/controllers/v1/test_baymodel.py @@ -15,6 +15,7 @@ import datetime import contextlib import mock from oslo_config import cfg +from oslo_policy import policy from oslo_utils import timeutils from six.moves.urllib import parse as urlparse from webtest.app import AppError @@ -579,3 +580,45 @@ class TestDelete(api_base.FunctionalTest): self.assertEqual(409, response.status_int) self.assertEqual('application/json', response.content_type) self.assertTrue(response.json['error_message']) + + +class TestBayModelPolicyEnforcement(api_base.FunctionalTest): + + def setUp(self): + super(TestBayModelPolicyEnforcement, self).setUp() + + def _common_policy_check(self, rule, func, *arg, **kwarg): + self.policy.set_rules({rule: "project:non_fake"}) + exc = self.assertRaises(policy.PolicyNotAuthorized, + func, *arg, **kwarg) + self.assertTrue(exc.message.startswith(rule)) + self.assertTrue(exc.message.endswith("disallowed by policy")) + + def test_policy_disallow_get_all(self): + self._common_policy_check( + "baymodel:get_all", self.get_json, '/baymodels') + + def test_policy_disallow_get_one(self): + self._common_policy_check( + "baymodel:get", self.get_json, '/baymodels/111-222-333') + + def test_policy_disallow_update(self): + baymodel = obj_utils.create_test_baymodel(self.context, + name='example_A', + uuid="333-444-5555") + self._common_policy_check( + "baymodel:update", self.patch_json, + '/baymodels/%s' % baymodel.name, + [{'name': '/name', 'value': "new_name", 'op': 'replace'}]) + + def test_policy_disallow_create(self): + bdict = apiutils.bay_post_data(name='bay_example_A') + self._common_policy_check( + "baymodel:create", self.post_json, '/baymodels', bdict) + + def test_policy_disallow_delete(self): + baymodel = obj_utils.create_test_baymodel(self.context, + uuid='137-246-789') + self._common_policy_check( + "baymodel:delete", self.delete, + '/baymodels/%s' % baymodel.uuid)