Fixed the incorrect policy enforcement

In API controller, "@expose.expose" should be on top of
"@policy.enforce_wsgi". Otherwise, the output won't have the correct
format and status code. That is because "@expose.expose" will format
the exception before sending the response.

In "enforce_wsgi", use decorator module instead of "functools.wraps".
That is because decorator is signature preserving, which is
necessary for other decorator to work.

Also, added unit tests to ensure the correct error message and status
code will return if the request cannot pass the policy check.

Change-Id: I8b77ba95124c13dd1a46700bc60105bc7e33a579
Related-Bug: #1520311
This commit is contained in:
Hongbin Lu 2016-01-04 20:01:57 -05:00
parent 7b754ae390
commit 085631b71b
17 changed files with 167 additions and 129 deletions

View File

@ -219,9 +219,9 @@ class BaysController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("bay")
@expose.expose(BayCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("bay")
def get_all(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of bays.
@ -234,9 +234,9 @@ class BaysController(rest.RestController):
return self._get_bays_collection(marker, limit, sort_key,
sort_dir)
@policy.enforce_wsgi("bay")
@expose.expose(BayCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("bay")
def detail(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of bays with detail.
@ -257,8 +257,8 @@ class BaysController(rest.RestController):
sort_key, sort_dir, expand,
resource_url)
@policy.enforce_wsgi("bay", "get")
@expose.expose(Bay, types.uuid_or_name)
@policy.enforce_wsgi("bay", "get")
def get_one(self, bay_ident):
"""Retrieve information about the given bay.
@ -268,8 +268,8 @@ class BaysController(rest.RestController):
return Bay.convert_with_links(rpc_bay)
@policy.enforce_wsgi("bay", "create")
@expose.expose(Bay, body=Bay, status_code=201)
@policy.enforce_wsgi("bay", "create")
def post(self, bay):
"""Create a new bay.
@ -293,9 +293,9 @@ class BaysController(rest.RestController):
pecan.response.location = link.build_url('bays', res_bay.uuid)
return Bay.convert_with_links(res_bay)
@policy.enforce_wsgi("bay", "update")
@wsme.validate(types.uuid, [BayPatchType])
@expose.expose(Bay, types.uuid_or_name, body=[BayPatchType])
@policy.enforce_wsgi("bay", "update")
def patch(self, bay_ident, patch):
"""Update an existing bay.
@ -324,8 +324,8 @@ class BaysController(rest.RestController):
res_bay = pecan.request.rpcapi.bay_update(rpc_bay)
return Bay.convert_with_links(res_bay)
@policy.enforce_wsgi("bay", "delete")
@expose.expose(None, types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("bay", "delete")
def delete(self, bay_ident):
"""Delete a bay.

View File

@ -242,9 +242,9 @@ class BayModelsController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("baymodel")
@expose.expose(BayModelCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("baymodel")
def get_all(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of baymodels.
@ -257,9 +257,9 @@ class BayModelsController(rest.RestController):
return self._get_baymodels_collection(marker, limit, sort_key,
sort_dir)
@policy.enforce_wsgi("baymodel")
@expose.expose(BayModelCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("baymodel")
def detail(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of baymodels with detail.
@ -280,8 +280,8 @@ class BayModelsController(rest.RestController):
sort_key, sort_dir, expand,
resource_url)
@policy.enforce_wsgi("baymodel", "get")
@expose.expose(BayModel, types.uuid_or_name)
@policy.enforce_wsgi("baymodel", "get")
def get_one(self, baymodel_ident):
"""Retrieve information about the given baymodel.
@ -290,8 +290,8 @@ 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")
@expose.expose(BayModel, body=BayModel, status_code=201)
@policy.enforce_wsgi("baymodel", "create")
@validation.enforce_network_driver_types_create()
def post(self, baymodel):
"""Create a new baymodel.
@ -320,9 +320,9 @@ class BayModelsController(rest.RestController):
new_baymodel.uuid)
return BayModel.convert_with_links(new_baymodel)
@policy.enforce_wsgi("baymodel", "update")
@wsme.validate(types.uuid_or_name, [BayModelPatchType])
@expose.expose(BayModel, types.uuid_or_name, body=[BayModelPatchType])
@policy.enforce_wsgi("baymodel", "update")
@validation.enforce_network_driver_types_update()
def patch(self, baymodel_ident, patch):
"""Update an existing baymodel.
@ -361,8 +361,8 @@ class BayModelsController(rest.RestController):
rpc_baymodel.save()
return BayModel.convert_with_links(rpc_baymodel)
@policy.enforce_wsgi("baymodel")
@expose.expose(None, types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("baymodel")
def delete(self, baymodel_ident):
"""Delete a baymodel.

View File

@ -124,8 +124,8 @@ class CertificateController(rest.RestController):
'detail': ['GET'],
}
@policy.enforce_wsgi("certificate", "get")
@wsme_pecan.wsexpose(Certificate, types.uuid_or_name)
@policy.enforce_wsgi("certificate", "get")
def get_one(self, bay_ident):
"""Retrieve information about the given certificate.
@ -136,8 +136,8 @@ class CertificateController(rest.RestController):
certificate = pecan.request.rpcapi.get_ca_certificate(rpc_bay)
return Certificate.convert_with_links(certificate)
@policy.enforce_wsgi("certificate", "create")
@wsme_pecan.wsexpose(Certificate, body=Certificate, status_code=201)
@policy.enforce_wsgi("certificate", "create")
def post(self, certificate):
"""Create a new certificate.

View File

@ -312,9 +312,9 @@ class ContainersController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("container")
@expose.expose(ContainerCollection, types.uuid, int,
wtypes.text, wtypes.text, types.uuid_or_name)
@policy.enforce_wsgi("container")
def get_all(self, marker=None, limit=None, sort_key='id',
sort_dir='asc', bay_ident=None):
"""Retrieve a list of containers.
@ -328,9 +328,9 @@ class ContainersController(rest.RestController):
return self._get_containers_collection(marker, limit, sort_key,
sort_dir, bay_ident=bay_ident)
@policy.enforce_wsgi("container")
@expose.expose(ContainerCollection, types.uuid, int,
wtypes.text, wtypes.text)
@policy.enforce_wsgi("container")
def detail(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of containers with detail.
@ -350,8 +350,8 @@ class ContainersController(rest.RestController):
sort_key, sort_dir, expand,
resource_url)
@policy.enforce_wsgi("container", "get")
@expose.expose(Container, types.uuid_or_name)
@policy.enforce_wsgi("container", "get")
def get_one(self, container_ident):
"""Retrieve information about the given container.
@ -362,8 +362,8 @@ class ContainersController(rest.RestController):
res_container = pecan.request.rpcapi.container_show(rpc_container.uuid)
return Container.convert_with_links(res_container)
@policy.enforce_wsgi("container", "create")
@expose.expose(Container, body=Container, status_code=201)
@policy.enforce_wsgi("container", "create")
@validation.enforce_bay_types('swarm')
def post(self, container):
"""Create a new container.
@ -383,10 +383,10 @@ class ContainersController(rest.RestController):
res_container.uuid)
return Container.convert_with_links(res_container)
@policy.enforce_wsgi("container", "update")
@wsme.validate(types.uuid, [ContainerPatchType])
@expose.expose(Container, types.uuid_or_name,
body=[ContainerPatchType])
@policy.enforce_wsgi("container", "update")
def patch(self, container_ident, patch):
"""Update an existing container.
@ -417,8 +417,8 @@ class ContainersController(rest.RestController):
rpc_container.save()
return Container.convert_with_links(rpc_container)
@policy.enforce_wsgi("container")
@expose.expose(None, types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("container")
def delete(self, container_ident):
"""Delete a container.

View File

@ -147,9 +147,9 @@ class NodesController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("node")
@expose.expose(NodeCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("node")
def get_all(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of nodes.
@ -162,9 +162,9 @@ class NodesController(rest.RestController):
return self._get_nodes_collection(marker, limit, sort_key,
sort_dir)
@policy.enforce_wsgi("node")
@expose.expose(NodeCollection, types.uuid, int, wtypes.text,
wtypes.text)
@policy.enforce_wsgi("node")
def detail(self, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of nodes with detail.
@ -185,8 +185,8 @@ class NodesController(rest.RestController):
sort_key, sort_dir, expand,
resource_url)
@policy.enforce_wsgi("node", "get")
@expose.expose(Node, types.uuid)
@policy.enforce_wsgi("node", "get")
def get_one(self, node_uuid):
"""Retrieve information about the given node.
@ -195,8 +195,8 @@ class NodesController(rest.RestController):
rpc_node = objects.Node.get_by_uuid(pecan.request.context, node_uuid)
return Node.convert_with_links(rpc_node)
@policy.enforce_wsgi("node", "create")
@expose.expose(Node, body=Node, status_code=201)
@policy.enforce_wsgi("node", "create")
def post(self, node):
"""Create a new node.
@ -212,9 +212,9 @@ class NodesController(rest.RestController):
pecan.response.location = link.build_url('nodes', new_node.uuid)
return Node.convert_with_links(new_node)
@policy.enforce_wsgi("node", "update")
@wsme.validate(types.uuid, [NodePatchType])
@expose.expose(Node, types.uuid, body=[NodePatchType])
@policy.enforce_wsgi("node", "update")
def patch(self, node_uuid, patch):
"""Update an existing node.
@ -243,8 +243,8 @@ class NodesController(rest.RestController):
rpc_node.save()
return Node.convert_with_links(rpc_node)
@policy.enforce_wsgi("node", "delete")
@expose.expose(None, types.uuid, status_code=204)
@policy.enforce_wsgi("node", "delete")
def delete(self, node_uuid):
"""Delete a node.

View File

@ -190,9 +190,9 @@ class PodsController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("pod")
@expose.expose(PodCollection, types.uuid, types.uuid_or_name, int,
wtypes.text, wtypes.text)
@policy.enforce_wsgi("pod")
@validation.enforce_bay_types('kubernetes')
def get_all(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -208,9 +208,9 @@ class PodsController(rest.RestController):
return self._get_pods_collection(marker, limit, sort_key,
sort_dir, bay_ident)
@policy.enforce_wsgi("pod")
@expose.expose(PodCollection, types.uuid, types.uuid_or_name, int,
wtypes.text, wtypes.text)
@policy.enforce_wsgi("pod")
@validation.enforce_bay_types('kubernetes')
def detail(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -234,9 +234,9 @@ class PodsController(rest.RestController):
bay_ident, expand,
resource_url)
@policy.enforce_wsgi("pod", "get")
@expose.expose(Pod, types.uuid_or_name,
types.uuid_or_name)
@policy.enforce_wsgi("pod", "get")
@validation.enforce_bay_types('kubernetes')
def get_one(self, pod_ident, bay_ident):
"""Retrieve information about the given pod.
@ -249,8 +249,8 @@ class PodsController(rest.RestController):
return Pod.convert_with_links(rpc_pod)
@policy.enforce_wsgi("pod", "create")
@expose.expose(Pod, body=Pod, status_code=201)
@policy.enforce_wsgi("pod", "create")
@validation.enforce_bay_types('kubernetes')
def post(self, pod):
"""Create a new pod.
@ -268,10 +268,10 @@ class PodsController(rest.RestController):
pecan.response.location = link.build_url('pods', new_pod.uuid)
return Pod.convert_with_links(new_pod)
@policy.enforce_wsgi("pod", "update")
@wsme.validate(types.uuid, [PodPatchType])
@expose.expose(Pod, types.uuid_or_name,
types.uuid_or_name, body=[PodPatchType])
@policy.enforce_wsgi("pod", "update")
@validation.enforce_bay_types('kubernetes')
def patch(self, pod_ident, bay_ident, patch):
"""Update an existing pod.
@ -294,9 +294,9 @@ class PodsController(rest.RestController):
pod.manifest)
return Pod.convert_with_links(rpc_pod)
@policy.enforce_wsgi("pod")
@expose.expose(None, types.uuid_or_name,
types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("pod")
@validation.enforce_bay_types('kubernetes')
def delete(self, pod_ident, bay_ident):
"""Delete a pod.

View File

@ -219,9 +219,9 @@ class ReplicationControllersController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("rc")
@expose.expose(ReplicationControllerCollection, types.uuid,
types.uuid_or_name, int, wtypes.text, wtypes.text)
@policy.enforce_wsgi("rc")
@validation.enforce_bay_types('kubernetes')
def get_all(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -236,9 +236,9 @@ class ReplicationControllersController(rest.RestController):
return self._get_rcs_collection(marker, limit, sort_key,
sort_dir, bay_ident)
@policy.enforce_wsgi("rc")
@expose.expose(ReplicationControllerCollection, types.uuid,
types.uuid_or_name, int, wtypes.text, wtypes.text)
@policy.enforce_wsgi("rc")
@validation.enforce_bay_types('kubernetes')
def detail(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -262,9 +262,9 @@ class ReplicationControllersController(rest.RestController):
bay_ident, expand,
resource_url)
@policy.enforce_wsgi("rc", "get")
@expose.expose(ReplicationController, types.uuid_or_name,
types.uuid_or_name)
@policy.enforce_wsgi("rc", "get")
@validation.enforce_bay_types('kubernetes')
def get_one(self, rc_ident, bay_ident):
"""Retrieve information about the given ReplicationController.
@ -276,9 +276,9 @@ class ReplicationControllersController(rest.RestController):
rpc_rc = pecan.request.rpcapi.rc_show(context, rc_ident, bay_ident)
return ReplicationController.convert_with_links(rpc_rc)
@policy.enforce_wsgi("rc", "create")
@expose.expose(ReplicationController, body=ReplicationController,
status_code=201)
@policy.enforce_wsgi("rc", "create")
@validation.enforce_bay_types('kubernetes')
def post(self, rc):
"""Create a new ReplicationController.
@ -299,10 +299,10 @@ class ReplicationControllersController(rest.RestController):
pecan.response.location = link.build_url('rcs', new_rc.uuid)
return ReplicationController.convert_with_links(new_rc)
@policy.enforce_wsgi("rc", "update")
@wsme.validate(types.uuid, [ReplicationControllerPatchType])
@expose.expose(ReplicationController, types.uuid_or_name,
types.uuid_or_name, body=[ReplicationControllerPatchType])
@policy.enforce_wsgi("rc", "update")
@validation.enforce_bay_types('kubernetes')
def patch(self, rc_ident, bay_ident, patch):
"""Update an existing rc.
@ -327,9 +327,9 @@ class ReplicationControllersController(rest.RestController):
rc.manifest)
return ReplicationController.convert_with_links(rpc_rc)
@policy.enforce_wsgi("rc")
@expose.expose(None, types.uuid_or_name,
types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("rc")
@validation.enforce_bay_types('kubernetes')
def delete(self, rc_ident, bay_ident):
"""Delete a ReplicationController.

View File

@ -198,9 +198,9 @@ class ServicesController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@policy.enforce_wsgi("service")
@expose.expose(ServiceCollection, types.uuid, types.uuid_or_name, int,
wtypes.text, wtypes.text)
@policy.enforce_wsgi("service")
@validation.enforce_bay_types('kubernetes')
def get_all(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -215,9 +215,9 @@ class ServicesController(rest.RestController):
return self._get_services_collection(marker, limit, sort_key,
sort_dir, bay_ident)
@policy.enforce_wsgi("service")
@expose.expose(ServiceCollection, types.uuid, types.uuid_or_name, int,
wtypes.text, wtypes.text)
@policy.enforce_wsgi("service")
@validation.enforce_bay_types('kubernetes')
def detail(self, marker=None, bay_ident=None, limit=None, sort_key='id',
sort_dir='asc'):
@ -241,9 +241,9 @@ class ServicesController(rest.RestController):
bay_ident, expand,
resource_url)
@policy.enforce_wsgi("service", "get")
@expose.expose(Service, types.uuid_or_name,
types.uuid_or_name)
@policy.enforce_wsgi("service", "get")
@validation.enforce_bay_types('kubernetes')
def get_one(self, service_ident, bay_ident):
"""Retrieve information about the given service.
@ -257,8 +257,8 @@ class ServicesController(rest.RestController):
bay_ident)
return Service.convert_with_links(rpc_service)
@policy.enforce_wsgi("service", "create")
@expose.expose(Service, body=Service, status_code=201)
@policy.enforce_wsgi("service", "create")
@validation.enforce_bay_types('kubernetes')
def post(self, service):
"""Create a new service.
@ -279,10 +279,10 @@ class ServicesController(rest.RestController):
pecan.response.location = link.build_url('services', new_service.uuid)
return Service.convert_with_links(new_service)
@policy.enforce_wsgi("service", "update")
@wsme.validate(types.uuid, [ServicePatchType])
@expose.expose(Service, types.uuid_or_name,
types.uuid_or_name, body=[ServicePatchType])
@policy.enforce_wsgi("service", "update")
@validation.enforce_bay_types('kubernetes')
def patch(self, service_ident, bay_ident, patch):
"""Update an existing service.
@ -306,9 +306,9 @@ class ServicesController(rest.RestController):
service.manifest)
return Service.convert_with_links(rpc_service)
@policy.enforce_wsgi("service")
@expose.expose(None, types.uuid_or_name,
types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("service")
@validation.enforce_bay_types('kubernetes')
def delete(self, service_ident, bay_ident):
"""Delete a service.

View File

@ -15,7 +15,8 @@
"""Policy Engine For magnum."""
import functools
import decorator
import logging
from oslo_config import cfg
from oslo_policy import policy
import pecan
@ -26,6 +27,8 @@ from magnum.common import exception
_ENFORCER = None
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# we can get a policy enforcer by this init.
# oslo policy support change policy rule dynamically.
@ -109,13 +112,10 @@ def enforce_wsgi(api_name, act=None):
def delete(self, bay_ident):
...
"""
def wrapper(fn):
@decorator.decorator
def wrapper(fn, *args, **kwargs):
action = "%s:%s" % (api_name, (act or fn.__name__))
@functools.wraps(fn)
def handle(self, *args, **kwargs):
enforce(pecan.request.context, action,
exc=exception.PolicyNotAuthorized, action=action)
return fn(self, *args, **kwargs)
return handle
enforce(pecan.request.context, action,
exc=exception.PolicyNotAuthorized, action=action)
return fn(*args, **kwargs)
return wrapper

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -709,11 +710,12 @@ class TestBayPolicyEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
@ -721,11 +723,13 @@ class TestBayPolicyEnforcement(api_base.FunctionalTest):
def test_policy_disallow_get_one(self):
self._common_policy_check(
"bay:get", self.get_json, '/bays/111-222-333', expect_errors=True)
"bay:get", self.get_json, '/bays/%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
"bay:detail", self.get_json, '/bays/111-222-333/detail',
"bay:detail", self.get_json,
'/bays/%s/detail' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -813,11 +814,12 @@ class TestBayModelPolicyEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
@ -826,22 +828,24 @@ class TestBayModelPolicyEnforcement(api_base.FunctionalTest):
def test_policy_disallow_get_one(self):
self._common_policy_check(
"baymodel:get", self.get_json, '/baymodels/111-222-333',
"baymodel:get", self.get_json,
'/baymodels/%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
"baymodel:detail", self.get_json, '/baymodels/111-222-333/detail',
"baymodel:detail", self.get_json,
'/baymodels/%s/detail' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
baymodel = obj_utils.create_test_baymodel(self.context,
name='example_A',
uuid="333-444-5555")
uuid=utils.generate_uuid())
self._common_policy_check(
"baymodel:update", self.patch_json,
'/baymodels/%s' % baymodel.name,
[{'name': '/name', 'value': "new_name", 'op': 'replace'}],
[{'path': '/name', 'value': "new_name", 'op': 'replace'}],
expect_errors=True)
def test_policy_disallow_create(self):

View File

@ -10,10 +10,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import mock
from magnum.api.controllers.v1 import certificate as api_cert
from magnum.common import exception
from magnum.common import utils
from magnum.tests import base
from magnum.tests.unit.api import base as api_base
@ -169,11 +169,12 @@ class TestCertPolicyEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_one(self):
self._common_policy_check(
@ -182,7 +183,8 @@ class TestCertPolicyEnforcement(api_base.FunctionalTest):
expect_errors=True)
def test_policy_disallow_create(self):
cert = apiutils.cert_post_data()
bay = obj_utils.create_test_bay(self.context)
cert = apiutils.cert_post_data(bay_uuid=bay.uuid)
self._common_policy_check(
"certificate:create", self.post_json, '/certificates', cert,
expect_errors=True)

View File

@ -10,7 +10,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from magnum.common import exception
import json
import mock
from mock import patch
from webtest.app import AppError
from magnum.common import utils as comm_utils
from magnum import objects
from magnum.objects import fields
@ -18,10 +22,6 @@ from magnum.tests.unit.api import base as api_base
from magnum.tests.unit.db import utils
from magnum.tests.unit.objects import utils as obj_utils
import mock
from mock import patch
from webtest.app import AppError
class TestContainerController(api_base.FunctionalTest):
def setUp(self):
@ -636,11 +636,12 @@ class TestContainerEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: 'project:non_fake'})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
@ -649,14 +650,15 @@ class TestContainerEnforcement(api_base.FunctionalTest):
def test_policy_disallow_get_one(self):
self._common_policy_check(
'container:get', self.get_json, '/containers/111-222-333',
'container:get', self.get_json,
'/containers/%s' % comm_utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
'container:detail',
self.get_json,
'/containers/111-222-333/detail',
'/containers/%s/detail' % comm_utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
@ -670,13 +672,15 @@ class TestContainerEnforcement(api_base.FunctionalTest):
'/v1/containers/%s' % container_uuid, params,
expect_errors=True)
def test_policy_disallow_create(self):
params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",'
@patch('magnum.objects.Bay.get_by_uuid')
def test_policy_disallow_create(self, mock_get_by_uuid):
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
self._common_policy_check(
'container:create', self.app.post, '/v1/containers', params,
'container:create', self.app.post, '/v1/containers', params=params,
content_type='application/json',
expect_errors=True)
def test_policy_disallow_delete(self):

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -19,7 +20,6 @@ from six.moves.urllib import parse as urlparse
from wsme import types as wtypes
from magnum.api.controllers.v1 import node as api_node
from magnum.common import exception
from magnum.common import utils
from magnum.tests import base
from magnum.tests.unit.api import base as api_base
@ -295,11 +295,12 @@ class TestNodePolicyEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
@ -307,22 +308,24 @@ class TestNodePolicyEnforcement(api_base.FunctionalTest):
def test_policy_disallow_get_one(self):
self._common_policy_check(
"node:get", self.get_json, '/nodes/111-222-333',
"node:get", self.get_json,
'/nodes/%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
"node:detail", self.get_json, '/nodes/111-222-333/detail',
"node:detail", self.get_json,
'/nodes/%s/detail' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
node = obj_utils.create_test_node(self.context,
type='type_A',
uuid="333-444-5555")
uuid=utils.generate_uuid())
self._common_policy_check(
"node:update", self.patch_json,
'/nodes/%s' % node.uuid,
[{'type': '/type', 'value': "new_type", 'op': 'replace'}],
[{'path': '/type', 'value': "new_type", 'op': 'replace'}],
expect_errors=True)
def test_policy_disallow_create(self):
@ -332,7 +335,7 @@ class TestNodePolicyEnforcement(api_base.FunctionalTest):
def test_policy_disallow_delete(self):
node = obj_utils.create_test_node(self.context,
uuid='137-246-789')
uuid=utils.generate_uuid())
self._common_policy_check(
"node:delete", self.delete,
'/nodes/%s' % node.uuid, expect_errors=True)

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -18,7 +19,6 @@ from six.moves.urllib import parse as urlparse
from wsme import types as wtypes
from magnum.api.controllers.v1 import pod as api_pod
from magnum.common import exception
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.common import utils
from magnum.conductor import api as rpcapi
@ -576,23 +576,29 @@ class TestPodPolicyEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: 'project:non_fake'})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
'pod:get_all', self.get_json, '/pods', expect_errors=True)
'pod:get_all', self.get_json,
'/pods?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_get_one(self):
self._common_policy_check(
'pod:get', self.get_json, '/pods/111-222-333', expect_errors=True)
'pod:get', self.get_json,
'/pods/?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
'pod:detail', self.get_json, '/pods/111-222-333/detail',
'pod:detail', self.get_json,
'/pods/detail?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
@ -602,12 +608,13 @@ class TestPodPolicyEnforcement(api_base.FunctionalTest):
self._common_policy_check(
'pod:update', self.patch_json,
'/pods/%s' % pod.uuid,
'/pods/%s/%s' % (pod.uuid, utils.generate_uuid()),
[{'path': '/desc', 'value': 'new test pod', 'op': 'replace'}],
expect_errors=True)
def test_policy_disallow_create(self):
pdict = apiutils.pod_post_data()
bay = obj_utils.create_test_bay(self.context)
pdict = apiutils.pod_post_data(bay_uuid=bay.uuid)
self._common_policy_check(
'pod:create', self.post_json, '/pods', pdict, expect_errors=True)
@ -617,4 +624,5 @@ class TestPodPolicyEnforcement(api_base.FunctionalTest):
uuid=utils.generate_uuid())
self._common_policy_check(
'pod:delete', self.delete,
'/pods/%s' % pod.uuid, expect_errors=True)
'/pods/%s/%s' % (pod.uuid, utils.generate_uuid()),
expect_errors=True)

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -18,7 +19,6 @@ from six.moves.urllib import parse as urlparse
from wsme import types as wtypes
from magnum.api.controllers.v1 import replicationcontroller as api_rc
from magnum.common import exception
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.common import utils
from magnum.conductor import api as rpcapi
@ -586,11 +586,12 @@ class TestRCEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: 'project:non_fake'})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
@ -598,11 +599,14 @@ class TestRCEnforcement(api_base.FunctionalTest):
def test_policy_disallow_get_one(self):
self._common_policy_check(
'rc:get', self.get_json, '/rcs/111-222-333', expect_errors=True)
'rc:get', self.get_json,
'/rcs/?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
'rc:detail', self.get_json, '/rcs/111-222-333/detail',
'rc:detail', self.get_json,
'/rcs/detail?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
@ -613,12 +617,13 @@ class TestRCEnforcement(api_base.FunctionalTest):
new_image = 'rc_example_B_image'
self._common_policy_check(
'rc:update', self.patch_json,
'/rcs/%s' % rc.uuid,
'/rcs/%s/%s' % (rc.uuid, utils.generate_uuid()),
[{'path': '/images/0', 'value': new_image, 'op': 'replace'}],
expect_errors=True)
def test_policy_disallow_create(self):
pdict = apiutils.rc_post_data()
bay = obj_utils.create_test_bay(self.context)
pdict = apiutils.rc_post_data(bay_uuid=bay.uuid)
self._common_policy_check(
'rc:create', self.post_json, '/rcs', pdict, expect_errors=True)
@ -628,4 +633,5 @@ class TestRCEnforcement(api_base.FunctionalTest):
uuid=utils.generate_uuid())
self._common_policy_check(
'rc:delete', self.delete,
'/rcs/%s' % rc.uuid, expect_errors=True)
'/rcs/%s/%s' % (rc.uuid, utils.generate_uuid()),
expect_errors=True)

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import json
import mock
from oslo_config import cfg
@ -18,7 +19,6 @@ from six.moves.urllib import parse as urlparse
from wsme import types as wtypes
from magnum.api.controllers.v1 import service as api_service
from magnum.common import exception
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.common import utils
from magnum.conductor import api as rpcapi
@ -598,24 +598,29 @@ class TestServiceEnforcement(api_base.FunctionalTest):
def _common_policy_check(self, rule, func, *arg, **kwarg):
self.policy.set_rules({rule: 'project:non_fake'})
exc = self.assertRaises(exception.PolicyNotAuthorized,
func, *arg, **kwarg)
self.assertEqual(
response = func(*arg, **kwarg)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
exc.format_message())
json.loads(response.json['error_message'])['faultstring'])
def test_policy_disallow_get_all(self):
self._common_policy_check(
'service:get_all', self.get_json, '/services', expect_errors=True)
'service:get_all', self.get_json,
'/services?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_get_one(self):
self._common_policy_check(
'service:get', self.get_json, '/services/111-222-333',
'service:get', self.get_json,
'/services/?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_detail(self):
self._common_policy_check(
'service:detail', self.get_json, '/services/111-222-333/detail',
'service:detail', self.get_json,
'/services/detail?bay_ident=%s' % utils.generate_uuid(),
expect_errors=True)
def test_policy_disallow_update(self):
@ -625,13 +630,14 @@ class TestServiceEnforcement(api_base.FunctionalTest):
self._common_policy_check(
'service:update', self.patch_json,
'/services/%s' % service.uuid,
'/services/%s/%s' % (service.uuid, utils.generate_uuid()),
[{'path': '/bay_uuid',
'value': utils.generate_uuid(),
'op': 'replace'}], expect_errors=True)
def test_policy_disallow_create(self):
pdict = apiutils.service_post_data()
bay = obj_utils.create_test_bay(self.context)
pdict = apiutils.service_post_data(bay_uuid=bay.uuid)
self._common_policy_check(
'service:create', self.post_json, '/services', pdict,
expect_errors=True)
@ -642,4 +648,5 @@ class TestServiceEnforcement(api_base.FunctionalTest):
uuid=utils.generate_uuid())
self._common_policy_check(
'service:delete', self.delete,
'/services/%s' % service.uuid, expect_errors=True)
'/services/%s/%s' % (service.uuid, utils.generate_uuid()),
expect_errors=True)