use payloads for RBAC_POLICY events

This patch switches over to the payload style kwargs for RBAC_POLICY
callback events.

NeutronLibImpact

Change-Id: Ibf39013bfec7f03f76be7decf63000df3f0f6ad3
This commit is contained in:
Boden R 2019-04-25 13:15:24 -06:00
parent 1c048e30ed
commit ce0764286d
6 changed files with 105 additions and 48 deletions

View File

@ -175,10 +175,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
@registry.receives(resources.RBAC_POLICY, [events.BEFORE_CREATE,
events.BEFORE_UPDATE,
events.BEFORE_DELETE])
@db_api.retry_if_session_inactive()
def validate_network_rbac_policy_change(self, resource, event, trigger,
context, object_type, policy,
**kwargs):
payload=None):
return self._validate_network_rbac_policy_change(
resource, event, trigger, payload.context, payload)
@db_api.retry_if_session_inactive()
def _validate_network_rbac_policy_change(self, resource, event, trigger,
context, payload):
"""Validates network RBAC policy changes.
On creation, verify that the creator is an admin or that it owns the
@ -187,6 +191,10 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
On update and delete, make sure the tenant losing access does not have
resources that depend on that access.
"""
object_type = payload.metadata.get('object_type')
policy = (payload.request_body if event == events.BEFORE_CREATE
else payload.latest_state)
if object_type != 'network' or policy['action'] != 'access_as_shared':
# we only care about shared network policies
return
@ -206,7 +214,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
if self_sharing:
return
if event == events.BEFORE_UPDATE:
new_tenant = kwargs['policy_update']['target_tenant']
new_tenant = payload.request_body['target_tenant']
if policy['target_tenant'] != new_tenant:
tenant_to_check = policy['target_tenant']

View File

@ -151,8 +151,12 @@ class External_net_db_mixin(object):
return nets[0]['id'] if nets else None
@registry.receives(resources.RBAC_POLICY, [events.BEFORE_CREATE])
def _process_ext_policy_create(self, resource, event, trigger, context,
object_type, policy, **kwargs):
def _process_ext_policy_create(self, resource, event, trigger,
payload=None):
object_type = payload.metadata.get('object_type')
policy = payload.request_body
context = payload.context
if (object_type != 'network' or
policy['action'] != 'access_as_external'):
return
@ -168,8 +172,12 @@ class External_net_db_mixin(object):
allow_all=False)
@registry.receives(resources.RBAC_POLICY, [events.AFTER_DELETE])
def _process_ext_policy_delete(self, resource, event, trigger, context,
object_type, policy, **kwargs):
def _process_ext_policy_delete(self, resource, event, trigger,
payload=None):
object_type = payload.metadata.get('object_type')
policy = payload.latest_state
context = payload.context
if (object_type != 'network' or
policy['action'] != 'access_as_external'):
return
@ -185,14 +193,17 @@ class External_net_db_mixin(object):
@registry.receives(resources.RBAC_POLICY, (events.BEFORE_UPDATE,
events.BEFORE_DELETE))
def _validate_ext_not_in_use_by_tenant(self, resource, event, trigger,
context, object_type, policy,
**kwargs):
payload=None):
object_type = payload.metadata.get('object_type')
policy = payload.latest_state
context = payload.context
if (object_type != 'network' or
policy['action'] != 'access_as_external'):
return
new_project = None
if event == events.BEFORE_UPDATE:
new_project = kwargs['policy_update']['target_tenant']
new_project = payload.request_body['target_tenant']
if new_project == policy['target_tenant']:
# nothing to validate if the tenant didn't change
return

View File

@ -37,9 +37,10 @@ class RbacPluginMixin(object):
def create_rbac_policy(self, context, rbac_policy):
e = rbac_policy['rbac_policy']
try:
registry.notify(resources.RBAC_POLICY, events.BEFORE_CREATE, self,
context=context, object_type=e['object_type'],
policy=e)
registry.publish(resources.RBAC_POLICY, events.BEFORE_CREATE, self,
payload=events.DBEventPayload(
context, request_body=e,
metadata={'object_type': e['object_type']}))
except c_exc.CallbackFailure as e:
raise n_exc.InvalidInput(error_message=e)
rbac_class = (
@ -68,9 +69,11 @@ class RbacPluginMixin(object):
entry = self._get_rbac_policy(context, id)
object_type = entry.db_model.object_type
try:
registry.notify(resources.RBAC_POLICY, events.BEFORE_UPDATE, self,
context=context, policy=entry,
object_type=object_type, policy_update=pol)
registry.publish(resources.RBAC_POLICY, events.BEFORE_UPDATE, self,
payload=events.DBEventPayload(
context, request_body=pol,
states=(entry,), resource_id=id,
metadata={'object_type': object_type}))
except c_exc.CallbackFailure as ex:
raise ext_rbac.RbacPolicyInUse(object_id=entry.object_id,
details=ex)
@ -83,9 +86,10 @@ class RbacPluginMixin(object):
entry = self._get_rbac_policy(context, id)
object_type = entry.db_model.object_type
try:
registry.notify(resources.RBAC_POLICY, events.BEFORE_DELETE, self,
context=context, object_type=object_type,
policy=entry)
registry.publish(resources.RBAC_POLICY, events.BEFORE_DELETE, self,
payload=events.DBEventPayload(
context, states=(entry,), resource_id=id,
metadata={'object_type': object_type}))
except c_exc.CallbackFailure as ex:
raise ext_rbac.RbacPolicyInUse(object_id=entry.object_id,
details=ex)
@ -93,9 +97,10 @@ class RbacPluginMixin(object):
# object_id link to network
entry_dict = entry.to_dict()
entry.delete()
registry.notify(resources.RBAC_POLICY, events.AFTER_DELETE, self,
context=context, object_type=object_type,
policy=entry_dict)
registry.publish(resources.RBAC_POLICY, events.AFTER_DELETE, self,
payload=events.DBEventPayload(
context, states=(entry_dict,), resource_id=id,
metadata={'object_type': object_type}))
self.object_type_cache.pop(id, None)
def _get_rbac_policy(self, context, id):

View File

@ -165,12 +165,15 @@ class RbacNeutronDbObjectMixin(rbac_db_mixin.RbacPluginMixin,
raise_policy_in_use()
@classmethod
def validate_rbac_policy_delete(cls, resource, event, trigger, context,
object_type, policy, **kwargs):
def validate_rbac_policy_delete(cls, resource, event, trigger,
payload=None):
"""Callback to handle RBAC_POLICY, BEFORE_DELETE callback.
:raises: RbacPolicyInUse -- in case the policy is in use.
"""
context = payload.context
policy = payload.latest_state
if policy['action'] != models.ACCESS_SHARED:
return
target_tenant = policy['target_tenant']
@ -183,29 +186,36 @@ class RbacNeutronDbObjectMixin(rbac_db_mixin.RbacPluginMixin,
target_tenant=target_tenant)
@classmethod
def validate_rbac_policy_update(cls, resource, event, trigger, context,
object_type, policy, **kwargs):
def validate_rbac_policy_update(cls, resource, event, trigger,
payload=None):
"""Callback to handle RBAC_POLICY, BEFORE_UPDATE callback.
:raises: RbacPolicyInUse -- in case the update is forbidden.
"""
policy = payload.latest_state
prev_tenant = policy['target_tenant']
new_tenant = kwargs['policy_update']['target_tenant']
new_tenant = payload.request_body['target_tenant']
if prev_tenant == new_tenant:
return
if new_tenant != '*':
return cls.validate_rbac_policy_delete(
resource, event, trigger, context, object_type, policy)
resource, event, trigger, payload=payload)
@classmethod
def validate_rbac_policy_change(cls, resource, event, trigger, context,
object_type, policy, **kwargs):
"""Callback to validate RBAC_POLICY changes.
def validate_rbac_policy_change(cls, resource, event, trigger,
payload=None):
"""Callback to validate changes.
This is the dispatching function for create, update and delete
callbacks. On creation and update, verify that the creator is an admin
or owns the resource being shared.
"""
object_type = payload.metadata.get('object_type')
context = payload.context
policy = (payload.request_body if event == events.BEFORE_CREATE
else payload.latest_state)
# TODO(hdaniel): As this code was shamelessly stolen from
# NeutronDbPluginV2.validate_network_rbac_policy_change(), those pieces
# should be synced and contain the same bugs, until Network RBAC logic
@ -223,8 +233,8 @@ class RbacNeutronDbObjectMixin(rbac_db_mixin.RbacPluginMixin,
callback_map = {events.BEFORE_UPDATE: cls.validate_rbac_policy_update,
events.BEFORE_DELETE: cls.validate_rbac_policy_delete}
if event in callback_map:
return callback_map[event](resource, event, trigger, context,
object_type, policy, **kwargs)
return callback_map[event](resource, event, trigger,
payload=payload)
def attach_rbac(self, obj_id, project_id, target_tenant='*'):
obj_type = self.rbac_db_cls.db_model.object_type

View File

@ -208,15 +208,17 @@ class NetworkRbacTestcase(test_plugin.NeutronDbPluginV2TestCase):
'tenant_id': net_owner,
'project_id': net_owner}}
policy = self._make_networkrbac(net, net_owner)['rbac_policy']
kwargs = {}
with mock.patch.object(db_plugin_v2, '_get_network') as get_net,\
mock.patch.object(db_plugin_v2,
'ensure_no_tenant_ports_on_network') as ensure:
get_net.return_value = net['network']
payload = events.DBEventPayload(
self.context, states=(policy,),
metadata={'object_type': 'network'})
self.plugin.validate_network_rbac_policy_change(
None, events.BEFORE_DELETE, None,
self.context, 'network', policy, **kwargs)
payload=payload)
self.assertEqual(0, ensure.call_count)
def test_update_self_share_networkrbac(self):
@ -228,15 +230,18 @@ class NetworkRbacTestcase(test_plugin.NeutronDbPluginV2TestCase):
'tenant_id': net_owner,
'project_id': net_owner}}
policy = self._make_networkrbac(net, net_owner)['rbac_policy']
kwargs = {'policy_update': {'target_tenant': 'new-target-tenant'}}
with mock.patch.object(db_plugin_v2, '_get_network') as get_net,\
mock.patch.object(db_plugin_v2,
'ensure_no_tenant_ports_on_network') as ensure:
get_net.return_value = net['network']
payload = events.DBEventPayload(
self.context, states=(policy,),
request_body={'target_tenant': 'new-target-tenant'},
metadata={'object_type': 'network'})
self.plugin.validate_network_rbac_policy_change(
None, events.BEFORE_UPDATE, None,
self.context, 'network', policy, **kwargs)
payload=payload)
self.assertEqual(0, ensure.call_count)
def _create_rbac_obj(self, _class):

View File

@ -129,8 +129,14 @@ class RbacNeutronDbObjectTestCase(test_rbac.RBACBaseObjectIfaceTestCase,
context, object_type, policy,
event_list):
for event in event_list:
payload = events.DBEventPayload(
context, states=(policy,),
metadata={'object_type': object_type})
if event == events.BEFORE_CREATE:
payload.states = []
payload.request_body = policy
self._test_class.validate_rbac_policy_change(
resource, event, trigger, context, object_type, policy)
resource, event, trigger, payload=payload)
@mock.patch.object(_test_class, 'validate_rbac_policy_update')
def test_validate_rbac_policy_change_handles_only_object_type(
@ -175,11 +181,15 @@ class RbacNeutronDbObjectTestCase(test_rbac.RBACBaseObjectIfaceTestCase,
@mock.patch.object(_test_class, '_validate_rbac_policy_delete')
def _test_validate_rbac_policy_delete_handles_policy(
self, policy, mock_validate_delete):
payload = events.DBEventPayload(
n_context.get_admin_context(),
states=(policy,),
metadata={
'object_type':
self._test_class.rbac_db_cls.db_model.object_type})
self._test_class.validate_rbac_policy_delete(
resource=mock.Mock(), event=events.BEFORE_DELETE,
trigger='dummy_trigger', context=n_context.get_admin_context(),
object_type=self._test_class.rbac_db_cls.db_model.object_type,
policy=policy)
trigger='dummy_trigger', payload=payload)
mock_validate_delete.assert_not_called()
def test_validate_rbac_policy_delete_handles_shared_action(self):
@ -211,15 +221,19 @@ class RbacNeutronDbObjectTestCase(test_rbac.RBACBaseObjectIfaceTestCase,
'_get_db_obj_rbac_entries') as target_tenants_mock:
filter_mock = target_tenants_mock.return_value.filter
filter_mock.return_value.count.return_value = 0
payload = events.DBEventPayload(
context,
states=(policy,),
metadata={
'object_type':
self._test_class.rbac_db_cls.db_model.object_type})
self.assertRaises(
ext_rbac.RbacPolicyInUse,
self._test_class.validate_rbac_policy_delete,
resource=None,
event=events.BEFORE_DELETE,
trigger='dummy_trigger',
context=context,
object_type=self._test_class.rbac_db_cls.db_model.object_type,
policy=policy)
payload=payload)
def test_validate_rbac_policy_delete_not_bound_tenant_success(self):
context = mock.Mock()
@ -252,6 +266,12 @@ class RbacNeutronDbObjectTestCase(test_rbac.RBACBaseObjectIfaceTestCase,
'tenant_id': 'object_owner_tenant_id',
'object_id': 'fake_obj_id'}
context = mock.Mock()
payload = events.DBEventPayload(
context,
states=(policy,),
metadata={
'object_type':
self._test_class.rbac_db_cls.db_model.object_type})
with mock.patch.object(obj_db_api, 'get_object'):
self.assertRaises(
ext_rbac.RbacPolicyInUse,
@ -259,9 +279,7 @@ class RbacNeutronDbObjectTestCase(test_rbac.RBACBaseObjectIfaceTestCase,
resource=mock.Mock(),
event=events.BEFORE_DELETE,
trigger='dummy_trigger',
context=context,
object_type=self._test_class.rbac_db_cls.db_model.object_type,
policy=policy)
payload=payload)
@mock.patch.object(_test_class, 'attach_rbac')
@mock.patch.object(obj_db_api, 'get_object',