Merge "policy: cache extracted parent fields for OwnerCheck"
This commit is contained in:
@@ -51,6 +51,19 @@ def _get_cache_region(conf):
|
|||||||
return region
|
return region
|
||||||
|
|
||||||
|
|
||||||
|
def _get_memory_cache_region(expiration_time=5):
|
||||||
|
conf = cfg.ConfigOpts()
|
||||||
|
register_oslo_configs(conf)
|
||||||
|
cache_conf_dict = {
|
||||||
|
'enabled': True,
|
||||||
|
'backend': 'oslo_cache.dict',
|
||||||
|
'expiration_time': expiration_time,
|
||||||
|
}
|
||||||
|
for k, v in cache_conf_dict.items():
|
||||||
|
conf.set_override(k, v, group='cache')
|
||||||
|
return _get_cache_region(conf)
|
||||||
|
|
||||||
|
|
||||||
def _get_cache_region_for_legacy(url):
|
def _get_cache_region_for_legacy(url):
|
||||||
parsed = parse.urlparse(url)
|
parsed = parse.urlparse(url)
|
||||||
backend = parsed.scheme
|
backend = parsed.scheme
|
||||||
@@ -65,17 +78,8 @@ def _get_cache_region_for_legacy(url):
|
|||||||
if not query and '?' in parsed.path:
|
if not query and '?' in parsed.path:
|
||||||
query = parsed.path.split('?', 1)[-1]
|
query = parsed.path.split('?', 1)[-1]
|
||||||
parameters = parse.parse_qs(query)
|
parameters = parse.parse_qs(query)
|
||||||
|
return _get_memory_cache_region(
|
||||||
conf = cfg.ConfigOpts()
|
expiration_time=int(parameters.get('default_ttl', [0])[0]))
|
||||||
register_oslo_configs(conf)
|
|
||||||
cache_conf_dict = {
|
|
||||||
'enabled': True,
|
|
||||||
'backend': 'oslo_cache.dict',
|
|
||||||
'expiration_time': int(parameters.get('default_ttl', [0])[0]),
|
|
||||||
}
|
|
||||||
for k, v in cache_conf_dict.items():
|
|
||||||
conf.set_override(k, v, group='cache')
|
|
||||||
return _get_cache_region(conf)
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(_('Old style configuration can use only memory '
|
raise RuntimeError(_('Old style configuration can use only memory '
|
||||||
'(dict) backend'))
|
'(dict) backend'))
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import six
|
|||||||
|
|
||||||
from neutron._i18n import _, _LE, _LW
|
from neutron._i18n import _, _LE, _LW
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
|
from neutron.common import cache_utils as cache
|
||||||
from neutron.common import constants as const
|
from neutron.common import constants as const
|
||||||
|
|
||||||
|
|
||||||
@@ -209,8 +210,35 @@ class OwnerCheck(policy.Check):
|
|||||||
raise exceptions.PolicyInitError(
|
raise exceptions.PolicyInitError(
|
||||||
policy="%s:%s" % (kind, match),
|
policy="%s:%s" % (kind, match),
|
||||||
reason=err_reason)
|
reason=err_reason)
|
||||||
|
self._cache = cache._get_memory_cache_region(expiration_time=5)
|
||||||
super(OwnerCheck, self).__init__(kind, match)
|
super(OwnerCheck, self).__init__(kind, match)
|
||||||
|
|
||||||
|
@cache.cache_method_results
|
||||||
|
def _extract(self, resource_type, resource_id, field):
|
||||||
|
# NOTE(salv-orlando): This check currently assumes the parent
|
||||||
|
# resource is handled by the core plugin. It might be worth
|
||||||
|
# having a way to map resources to plugins so to make this
|
||||||
|
# check more general
|
||||||
|
f = getattr(directory.get_plugin(), 'get_%s' % resource_type)
|
||||||
|
# f *must* exist, if not found it is better to let neutron
|
||||||
|
# explode. Check will be performed with admin context
|
||||||
|
context = importutils.import_module('neutron.context')
|
||||||
|
try:
|
||||||
|
data = f(context.get_admin_context(),
|
||||||
|
resource_id,
|
||||||
|
fields=[field])
|
||||||
|
except exceptions.NotFound as e:
|
||||||
|
# NOTE(kevinbenton): a NotFound exception can occur if a
|
||||||
|
# list operation is happening at the same time as one of
|
||||||
|
# the parents and its children being deleted. So we issue
|
||||||
|
# a RetryRequest so the API will redo the lookup and the
|
||||||
|
# problem items will be gone.
|
||||||
|
raise db_exc.RetryRequest(e)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception(_LE('Policy check error while calling %s!'), f)
|
||||||
|
return data[field]
|
||||||
|
|
||||||
def __call__(self, target, creds, enforcer):
|
def __call__(self, target, creds, enforcer):
|
||||||
if self.target_field not in target:
|
if self.target_field not in target:
|
||||||
# policy needs a plugin check
|
# policy needs a plugin check
|
||||||
@@ -248,30 +276,10 @@ class OwnerCheck(policy.Check):
|
|||||||
raise exceptions.PolicyCheckError(
|
raise exceptions.PolicyCheckError(
|
||||||
policy="%s:%s" % (self.kind, self.match),
|
policy="%s:%s" % (self.kind, self.match),
|
||||||
reason=err_reason)
|
reason=err_reason)
|
||||||
# NOTE(salv-orlando): This check currently assumes the parent
|
|
||||||
# resource is handled by the core plugin. It might be worth
|
target[self.target_field] = self._extract(
|
||||||
# having a way to map resources to plugins so to make this
|
parent_res, target[parent_foreign_key], parent_field)
|
||||||
# check more general
|
|
||||||
f = getattr(directory.get_plugin(), 'get_%s' % parent_res)
|
|
||||||
# f *must* exist, if not found it is better to let neutron
|
|
||||||
# explode. Check will be performed with admin context
|
|
||||||
context = importutils.import_module('neutron.context')
|
|
||||||
try:
|
|
||||||
data = f(context.get_admin_context(),
|
|
||||||
target[parent_foreign_key],
|
|
||||||
fields=[parent_field])
|
|
||||||
target[self.target_field] = data[parent_field]
|
|
||||||
except exceptions.NotFound as e:
|
|
||||||
# NOTE(kevinbenton): a NotFound exception can occur if a
|
|
||||||
# list operation is happening at the same time as one of
|
|
||||||
# the parents and its children being deleted. So we issue
|
|
||||||
# a RetryRequest so the API will redo the lookup and the
|
|
||||||
# problem items will be gone.
|
|
||||||
raise db_exc.RetryRequest(e)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.exception(_LE('Policy check error while calling %s!'),
|
|
||||||
f)
|
|
||||||
match = self.match % target
|
match = self.match % target
|
||||||
if self.kind in creds:
|
if self.kind in creds:
|
||||||
return match == six.text_type(creds[self.kind])
|
return match == six.text_type(creds[self.kind])
|
||||||
|
|||||||
@@ -685,6 +685,28 @@ class TestMl2DbOperationBoundsTenant(TestMl2DbOperationBounds):
|
|||||||
admin = False
|
admin = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestMl2DbOperationBoundsTenantRbac(TestMl2DbOperationBoundsTenant):
|
||||||
|
|
||||||
|
def make_port_in_shared_network(self):
|
||||||
|
context_ = self._get_context()
|
||||||
|
# create shared network owned by the tenant; we use direct driver call
|
||||||
|
# because default policy does not allow users to create shared networks
|
||||||
|
net = self.driver.create_network(
|
||||||
|
context.get_admin_context(),
|
||||||
|
{'network': {'name': 'net1',
|
||||||
|
'tenant_id': context_.tenant,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'shared': True}})
|
||||||
|
# create port that belongs to another tenant
|
||||||
|
return self._make_port(
|
||||||
|
self.fmt, net['id'],
|
||||||
|
set_context=True, tenant_id='fake_tenant')
|
||||||
|
|
||||||
|
def test_port_list_in_shared_network_queries_constant(self):
|
||||||
|
self._assert_object_list_queries_constant(
|
||||||
|
self.make_port_in_shared_network, 'ports')
|
||||||
|
|
||||||
|
|
||||||
class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
|
class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def test__port_provisioned_with_blocks(self):
|
def test__port_provisioned_with_blocks(self):
|
||||||
|
|||||||
@@ -570,6 +570,17 @@ class NeutronPolicyTestCase(base.BaseTestCase):
|
|||||||
oslo_policy.Rules.from_dict,
|
oslo_policy.Rules.from_dict,
|
||||||
{'test_policy': 'tenant_id:(wrong_stuff)'})
|
{'test_policy': 'tenant_id:(wrong_stuff)'})
|
||||||
|
|
||||||
|
def test_tenant_id_check_caches_extracted_fields(self):
|
||||||
|
|
||||||
|
plugin = directory.get_plugin()
|
||||||
|
with mock.patch.object(plugin, 'get_network',
|
||||||
|
return_value={'tenant_id': 'fake'}) as getter:
|
||||||
|
action = "create_port:mac"
|
||||||
|
for i in range(2):
|
||||||
|
target = {'network_id': 'whatever'}
|
||||||
|
policy.enforce(self.context, action, target)
|
||||||
|
self.assertEqual(1, getter.call_count)
|
||||||
|
|
||||||
def _test_enforce_tenant_id_raises(self, bad_rule):
|
def _test_enforce_tenant_id_raises(self, bad_rule):
|
||||||
self._set_rules(admin_or_owner=bad_rule)
|
self._set_rules(admin_or_owner=bad_rule)
|
||||||
# Trigger a policy with rule admin_or_owner
|
# Trigger a policy with rule admin_or_owner
|
||||||
|
|||||||
Reference in New Issue
Block a user