Merge "Pass target in enforce"

This commit is contained in:
Jenkins 2016-03-10 22:10:36 +00:00 committed by Gerrit Code Review
commit a7b12b02a7
6 changed files with 206 additions and 69 deletions

View File

@ -223,7 +223,6 @@ class BaysController(rest.RestController):
@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.
@ -233,12 +232,14 @@ class BaysController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
context = pecan.request.context
policy.enforce(context, 'bay:get_all',
action='bay:get_all')
return self._get_bays_collection(marker, limit, sort_key,
sort_dir)
@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.
@ -248,6 +249,10 @@ class BaysController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
context = pecan.request.context
policy.enforce(context, 'bay:detail',
action='bay:detail')
# NOTE(lucasagomes): /detail should only work against collections
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "bays":
@ -260,24 +265,27 @@ class BaysController(rest.RestController):
resource_url)
@expose.expose(Bay, types.uuid_or_name)
@policy.enforce_wsgi("bay", "get")
def get_one(self, bay_ident):
"""Retrieve information about the given bay.
:param bay_ident: UUID of a bay or logical name of the bay.
"""
context = pecan.request.context
bay = api_utils.get_resource('Bay', bay_ident)
policy.enforce(context, 'bay:get', bay,
action='bay:get')
return Bay.convert_with_links(bay)
@expose.expose(Bay, body=Bay, status_code=201)
@policy.enforce_wsgi("bay", "create")
def post(self, bay):
"""Create a new bay.
:param bay: a bay within the request body.
"""
context = pecan.request.context
policy.enforce(context, 'bay:create',
action='bay:create')
baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id)
attr_validator.validate_os_resources(context, baymodel.as_dict())
bay_dict = bay.as_dict()
@ -296,14 +304,16 @@ class BaysController(rest.RestController):
@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.
:param bay_ident: UUID or logical name of a bay.
:param patch: a json PATCH document to apply to this bay.
"""
context = pecan.request.context
bay = api_utils.get_resource('Bay', bay_ident)
policy.enforce(context, 'bay:update', bay,
action='bay:update')
try:
bay_dict = bay.as_dict()
new_bay = Bay(**api_utils.apply_jsonpatch(bay_dict, patch))
@ -330,12 +340,14 @@ class BaysController(rest.RestController):
return Bay.convert_with_links(res_bay)
@expose.expose(None, types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("bay", "delete")
def delete(self, bay_ident):
"""Delete a bay.
:param bay_ident: UUID of a bay or logical name of the bay.
"""
rpc_bay = api_utils.get_resource('Bay', bay_ident)
context = pecan.request.context
bay = api_utils.get_resource('Bay', bay_ident)
policy.enforce(context, 'bay:delete', bay,
action='bay:delete')
pecan.request.rpcapi.bay_delete(rpc_bay.uuid)
pecan.request.rpcapi.bay_delete(bay.uuid)

View File

@ -244,7 +244,6 @@ class BayModelsController(rest.RestController):
@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.
@ -254,12 +253,14 @@ class BayModelsController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
context = pecan.request.context
policy.enforce(context, 'baymodel:get_all',
action='baymodel:get_all')
return self._get_baymodels_collection(marker, limit, sort_key,
sort_dir)
@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.
@ -269,6 +270,10 @@ class BayModelsController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
context = pecan.request.context
policy.enforce(context, 'baymodel:detail',
action='baymodel:detail')
# NOTE(lucasagomes): /detail should only work against collections
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "baymodels":
@ -281,17 +286,19 @@ class BayModelsController(rest.RestController):
resource_url)
@expose.expose(BayModel, types.uuid_or_name)
@policy.enforce_wsgi("baymodel", "get")
def get_one(self, baymodel_ident):
"""Retrieve information about the given baymodel.
:param baymodel_ident: UUID or logical name of a baymodel.
"""
context = pecan.request.context
baymodel = api_utils.get_resource('BayModel', baymodel_ident)
policy.enforce(context, 'baymodel:get', baymodel,
action='baymodel:get')
return BayModel.convert_with_links(baymodel)
@expose.expose(BayModel, body=BayModel, status_code=201)
@policy.enforce_wsgi("baymodel", "create")
@validation.enforce_network_driver_types_create()
@validation.enforce_volume_driver_types_create()
def post(self, baymodel):
@ -299,6 +306,9 @@ class BayModelsController(rest.RestController):
:param baymodel: a baymodel within the request body.
"""
context = pecan.request.context
policy.enforce(context, 'baymodel:create',
action='baymodel:create')
baymodel_dict = baymodel.as_dict()
context = pecan.request.context
cli = clients.OpenStackClients(context)
@ -323,7 +333,6 @@ class BayModelsController(rest.RestController):
@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()
@validation.enforce_volume_driver_types_update()
def patch(self, baymodel_ident, patch):
@ -334,6 +343,8 @@ class BayModelsController(rest.RestController):
"""
context = pecan.request.context
baymodel = api_utils.get_resource('BayModel', baymodel_ident)
policy.enforce(context, 'baymodel:update', baymodel,
action='baymodel:update')
try:
baymodel_dict = baymodel.as_dict()
new_baymodel = BayModel(**api_utils.apply_jsonpatch(
@ -364,11 +375,13 @@ class BayModelsController(rest.RestController):
return BayModel.convert_with_links(baymodel)
@expose.expose(None, types.uuid_or_name, status_code=204)
@policy.enforce_wsgi("baymodel")
def delete(self, baymodel_ident):
"""Delete a baymodel.
:param baymodel_ident: UUID or logical name of a baymodel.
"""
rpc_baymodel = api_utils.get_resource('BayModel', baymodel_ident)
rpc_baymodel.destroy()
context = pecan.request.context
baymodel = api_utils.get_resource('BayModel', baymodel_ident)
policy.enforce(context, 'baymodel:delete', baymodel,
action='baymodel:delete')
baymodel.destroy()

View File

@ -168,18 +168,23 @@ class ContainerCollection(collection.Collection):
return sample
def check_policy_on_container(container, action):
context = pecan.request.context
policy.enforce(context, action, container, action=action)
class StartController(object):
@expose.expose(types.uuid_or_name, wtypes.text)
def _default(self, container_ident):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:start")
LOG.debug('Calling conductor.container_start with %s',
container_uuid)
return pecan.request.rpcapi.container_start(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_start(container.uuid)
class StopController(object):
@ -188,11 +193,12 @@ class StopController(object):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:stop")
LOG.debug('Calling conductor.container_stop with %s' %
container_uuid)
return pecan.request.rpcapi.container_stop(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_stop(container.uuid)
class RebootController(object):
@ -201,11 +207,12 @@ class RebootController(object):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:reboot")
LOG.debug('Calling conductor.container_reboot with %s' %
container_uuid)
return pecan.request.rpcapi.container_reboot(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_reboot(container.uuid)
class PauseController(object):
@ -214,11 +221,12 @@ class PauseController(object):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:pause")
LOG.debug('Calling conductor.container_pause with %s' %
container_uuid)
return pecan.request.rpcapi.container_pause(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_pause(container.uuid)
class UnpauseController(object):
@ -227,11 +235,12 @@ class UnpauseController(object):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:unpause")
LOG.debug('Calling conductor.container_unpause with %s' %
container_uuid)
return pecan.request.rpcapi.container_unpause(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_unpause(container.uuid)
class LogsController(object):
@ -240,11 +249,12 @@ class LogsController(object):
if pecan.request.method != 'GET':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:logs")
LOG.debug('Calling conductor.container_logs with %s' %
container_uuid)
return pecan.request.rpcapi.container_logs(container_uuid)
container.uuid)
return pecan.request.rpcapi.container_logs(container.uuid)
class ExecuteController(object):
@ -253,11 +263,18 @@ class ExecuteController(object):
if pecan.request.method != 'PUT':
pecan.abort(405, ('HTTP method %s is not allowed'
% pecan.request.method))
container_uuid = api_utils.get_resource('Container',
container_ident).uuid
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:execute")
LOG.debug('Calling conductor.container_exec with %s command %s'
% (container_uuid, command))
return pecan.request.rpcapi.container_exec(container_uuid, command)
% (container.uuid, command))
return pecan.request.rpcapi.container_exec(container.uuid, command)
def check_policy_on_bay(bay_id, action):
context = pecan.request.context
bay = api_utils.get_resource('Bay', bay_id)
policy.enforce(context, action, bay, action=action)
class ContainersController(rest.RestController):
@ -319,7 +336,6 @@ class ContainersController(rest.RestController):
@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.
@ -330,12 +346,14 @@ class ContainersController(rest.RestController):
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param bay_indent: UUID or logical name of bay.
"""
context = pecan.request.context
policy.enforce(context, "container:get_all",
action="container:get_all")
return self._get_containers_collection(marker, limit, sort_key,
sort_dir, bay_ident=bay_ident)
@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.
@ -345,6 +363,10 @@ class ContainersController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
context = pecan.request.context
policy.enforce(context, "container:detail",
action="container:detail")
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "containers":
raise exception.HTTPNotFound
@ -356,7 +378,6 @@ class ContainersController(rest.RestController):
resource_url)
@expose.expose(Container, types.uuid_or_name)
@policy.enforce_wsgi("container", "get")
def get_one(self, container_ident):
"""Retrieve information about the given container.
@ -364,6 +385,7 @@ class ContainersController(rest.RestController):
"""
container = api_utils.get_resource('Container',
container_ident)
check_policy_on_container(container, "container:get")
res_container = pecan.request.rpcapi.container_show(container.uuid)
return Container.convert_with_links(res_container)
@ -375,10 +397,8 @@ class ContainersController(rest.RestController):
:param container: a container within the request body.
"""
container_dict = container.as_dict()
check_policy_on_bay(container_dict['bay_uuid'], "container:create")
context = pecan.request.context
bay = api_utils.get_resource('Bay', container_dict['bay_uuid'])
policy.enforce(context, 'container:create', bay,
action='container:create')
container_dict['project_id'] = context.project_id
container_dict['user_id'] = context.user_id
new_container = objects.Container(context, **container_dict)
@ -401,10 +421,7 @@ class ContainersController(rest.RestController):
"""
container = api_utils.get_resource('Container',
container_ident)
context = pecan.request.context
bay = api_utils.get_resource('Bay', container.bay_uuid)
policy.enforce(context, 'container:update', bay,
action='container:update')
check_policy_on_container(container, "container:update")
try:
container_dict = container.as_dict()
new_container = Container(**api_utils.apply_jsonpatch(
@ -435,9 +452,6 @@ class ContainersController(rest.RestController):
"""
container = api_utils.get_resource('Container',
container_ident)
context = pecan.request.context
bay = api_utils.get_resource('Bay', container.bay_uuid)
policy.enforce(context, 'container:delete', bay,
action='container:delete')
check_policy_on_container(container, "container:delete")
pecan.request.rpcapi.container_delete(container.uuid)
container.destroy()

View File

@ -706,8 +706,9 @@ class TestBayPolicyEnforcement(api_base.FunctionalTest):
"bay:get_all", self.get_json, '/bays', expect_errors=True)
def test_policy_disallow_get_one(self):
self.bay = obj_utils.create_test_bay(self.context)
self._common_policy_check(
"bay:get", self.get_json, '/bays/%s' % utils.generate_uuid(),
"bay:get", self.get_json, '/bays/%s' % self.bay.uuid,
expect_errors=True)
def test_policy_disallow_detail(self):
@ -739,5 +740,33 @@ class TestBayPolicyEnforcement(api_base.FunctionalTest):
self.mock_bay_delete = p.start()
self.mock_bay_delete.side_effect = self._simulate_rpc_bay_delete
self.addCleanup(p.stop)
self.bay = obj_utils.create_test_bay(self.context)
self._common_policy_check(
"bay:delete", self.delete, '/bays/test_bay', expect_errors=True)
"bay:delete", self.delete, '/bays/%s' % self.bay.uuid,
expect_errors=True)
def _owner_check(self, rule, func, *args, **kwargs):
self.policy.set_rules({rule: "user_id:%(user_id)s"})
response = func(*args, **kwargs)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
response.json['errors'][0]['detail'])
def test_policy_only_owner_get_one(self):
bay = obj_utils.create_test_bay(self.context, user_id='another')
self._owner_check("bay:get", self.get_json, '/bays/%s' % bay.uuid,
expect_errors=True)
def test_policy_only_owner_update(self):
bay = obj_utils.create_test_bay(self.context, user_id='another')
self._owner_check(
"bay:update", self.patch_json, '/bays/%s' % bay.uuid,
[{'path': '/name', 'value': "new_name", 'op': 'replace'}],
expect_errors=True)
def test_policy_only_owner_delete(self):
bay = obj_utils.create_test_bay(self.context, user_id='another')
self._owner_check("bay:delete", self.delete, '/bays/%s' % bay.uuid,
expect_errors=True)

View File

@ -857,9 +857,10 @@ class TestBayModelPolicyEnforcement(api_base.FunctionalTest):
expect_errors=True)
def test_policy_disallow_get_one(self):
baymodel = obj_utils.create_test_baymodel(self.context)
self._common_policy_check(
"baymodel:get", self.get_json,
'/baymodels/%s' % utils.generate_uuid(),
'/baymodels/%s' % baymodel.uuid,
expect_errors=True)
def test_policy_disallow_detail(self):
@ -885,8 +886,38 @@ class TestBayModelPolicyEnforcement(api_base.FunctionalTest):
expect_errors=True)
def test_policy_disallow_delete(self):
baymodel = obj_utils.create_test_baymodel(self.context,
uuid='137-246-789')
baymodel = obj_utils.create_test_baymodel(self.context)
self._common_policy_check(
"baymodel:delete", self.delete,
'/baymodels/%s' % baymodel.uuid, expect_errors=True)
def _owner_check(self, rule, func, *args, **kwargs):
self.policy.set_rules({rule: "user_id:%(user_id)s"})
response = func(*args, **kwargs)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
response.json['errors'][0]['detail'])
def test_policy_only_owner_get_one(self):
baymodel = obj_utils.create_test_baymodel(self.context,
user_id='another')
self._owner_check("baymodel:get", self.get_json,
'/baymodels/%s' % baymodel.uuid, expect_errors=True)
def test_policy_only_owner_update(self):
baymodel = obj_utils.create_test_baymodel(self.context,
user_id='another')
self._owner_check(
"baymodel:update", self.patch_json,
'/baymodels/%s' % baymodel.uuid,
[{'path': '/name', 'value': "new_name", 'op': 'replace'}],
expect_errors=True)
def test_policy_only_owner_delete(self):
baymodel = obj_utils.create_test_baymodel(self.context,
user_id='another')
self._owner_check(
"baymodel:delete", self.delete, '/baymodels/%s' % baymodel.uuid,
expect_errors=True)

View File

@ -79,11 +79,13 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create,
mock_container_show):
mock_container_create.side_effect = lambda x: x
bay = obj_utils.create_test_bay(self.context)
# Create a container with a command
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e",'
'"environment": {"key1": "val1", "key2": "val2"}}')
'"bay_uuid": "%s",'
'"environment": {"key1": "val1", "key2": "val2"}}' %
bay.uuid)
response = self.app.post('/v1/containers',
params=params,
content_type='application/json')
@ -161,11 +163,13 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create,
mock_container_show):
mock_container_create.side_effect = lambda x: x
bay = obj_utils.create_test_bay(self.context)
# Create a container with a command
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e",'
'"environment": {"key1": "val1", "key2": "val2"}}')
'"bay_uuid": "%s",'
'"environment": {"key1": "val1", "key2": "val2"}}' %
bay.uuid)
response = self.app.post('/v1/containers',
params=params,
content_type='application/json')
@ -648,9 +652,10 @@ class TestContainerEnforcement(api_base.FunctionalTest):
expect_errors=True)
def test_policy_disallow_get_one(self):
container = obj_utils.create_test_container(self.context)
self._common_policy_check(
'container:get', self.get_json,
'/containers/%s' % comm_utils.generate_uuid(),
'/containers/%s' % container.uuid,
expect_errors=True)
def test_policy_disallow_detail(self):
@ -693,3 +698,36 @@ class TestContainerEnforcement(api_base.FunctionalTest):
'container:delete', self.app.delete,
'/v1/containers/%s' % container.uuid,
expect_errors=True)
def _owner_check(self, rule, func, *args, **kwargs):
self.policy.set_rules({rule: "user_id:%(user_id)s"})
response = func(*args, **kwargs)
self.assertEqual(403, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(
"Policy doesn't allow %s to be performed." % rule,
response.json['errors'][0]['detail'])
def test_policy_only_owner_get_one(self):
container = obj_utils.create_test_container(self.context,
user_id='another')
self._owner_check("container:get", self.get_json,
'/containers/%s' % container.uuid,
expect_errors=True)
def test_policy_only_owner_update(self):
container = obj_utils.create_test_container(self.context,
user_id='another')
self._owner_check(
"container:update", self.patch_json,
'/containers/%s' % container.uuid,
[{'path': '/name', 'value': "new_name", 'op': 'replace'}],
expect_errors=True)
def test_policy_only_owner_delete(self):
container = obj_utils.create_test_container(self.context,
user_id='another')
self._owner_check(
"container:delete", self.delete,
'/containers/%s' % container.uuid,
expect_errors=True)