diff options
-rw-r--r-- | etc/nova/policy.json | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/plugins/v3/extension_info.py | 23 | ||||
-rw-r--r-- | nova/api/openstack/extensions.py | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py | 108 |
4 files changed, 137 insertions, 4 deletions
diff --git a/etc/nova/policy.json b/etc/nova/policy.json index f33feb689..a528bc887 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -37,6 +37,7 @@ "compute_extension:cloudpipe": "rule:admin_api", "compute_extension:cloudpipe_update": "rule:admin_api", "compute_extension:console_output": "", + "compute_extension:v3:consoles:discoverable": "", "compute_extension:consoles": "", "compute_extension:coverage_ext": "rule:admin_api", "compute_extension:createserverext": "", @@ -49,7 +50,9 @@ "compute_extension:extended_ips": "", "compute_extension:extended_ips_mac": "", "compute_extension:extended_vif_net": "", + "compute_extension:v3:extension_info:discoverable": "", "compute_extension:fixed_ips": "rule:admin_api", + "compute_extension:v3:os-fixed-ips:discoverable": "", "compute_extension:v3:os-fixed-ips": "rule:admin_api", "compute_extension:flavor_access": "", "compute_extension:flavor_disabled": "", @@ -75,7 +78,9 @@ "compute_extension:instance_actions": "", "compute_extension:instance_actions:events": "rule:admin_api", "compute_extension:instance_usage_audit_log": "rule:admin_api", + "compute_extension:v3:ips:discoverable": "", "compute_extension:keypairs": "", + "compute_extension:v3:os-keypairs:discoverable": "", "compute_extension:v3:os-keypairs": "", "compute_extension:multinic": "", "compute_extension:networks": "rule:admin_api", @@ -92,6 +97,7 @@ "compute_extension:server_password": "", "compute_extension:server_usage": "", "compute_extension:services": "rule:admin_api", + "compute_extension:v3:servers:discoverable": "", "compute_extension:simple_tenant_usage:show": "rule:admin_or_owner", "compute_extension:simple_tenant_usage:list": "rule:admin_api", "compute_extension:users": "rule:admin_api", diff --git a/nova/api/openstack/compute/plugins/v3/extension_info.py b/nova/api/openstack/compute/plugins/v3/extension_info.py index 43b0551c7..c626f6104 100644 --- a/nova/api/openstack/compute/plugins/v3/extension_info.py +++ b/nova/api/openstack/compute/plugins/v3/extension_info.py @@ -19,6 +19,10 @@ import webob.exc from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil +from nova.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) def make_ext(elem): @@ -64,11 +68,25 @@ class ExtensionInfoController(object): ext_data['version'] = ext.version return ext_data + def _get_extensions(self, context): + """Filter extensions list based on policy""" + + discoverable_extensions = dict() + for alias, ext in self.extension_info.get_extensions().iteritems(): + authorize = extensions.soft_extension_authorizer( + 'compute', 'v3:' + alias) + if authorize(context, action='discoverable'): + discoverable_extensions[alias] = ext + else: + LOG.debug(_("Filter out extension %s from discover list"), alias) + return discoverable_extensions + @wsgi.serializers(xml=ExtensionsTemplate) def index(self, req): + context = req.environ['nova.context'] sorted_ext_list = sorted( - self.extension_info.get_extensions().iteritems()) + self._get_extensions(context).iteritems()) extensions = [] for _alias, ext in sorted_ext_list: @@ -77,9 +95,10 @@ class ExtensionInfoController(object): @wsgi.serializers(xml=ExtensionTemplate) def show(self, req, id): + context = req.environ['nova.context'] try: # NOTE(dprince): the extensions alias is used as the 'id' for show - ext = self.extension_info.get_extensions()[id] + ext = self._get_extensions(context)[id] except KeyError: raise webob.exc.HTTPNotFound() diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 6cbc5bb78..2f3494ca4 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -392,9 +392,9 @@ def extension_authorizer(api_name, extension_name): def soft_extension_authorizer(api_name, extension_name): hard_authorize = extension_authorizer(api_name, extension_name) - def authorize(context): + def authorize(context, action=None): try: - hard_authorize(context) + hard_authorize(context, action=action) return True except exception.NotAuthorized: return False diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py b/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py new file mode 100644 index 000000000..a19d28064 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py @@ -0,0 +1,108 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 IBM Corp. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova.api.openstack.compute import plugins +from nova.api.openstack.compute.plugins.v3 import extension_info +from nova import exception +from nova import policy +from nova import test +from nova.tests.api.openstack import fakes + + +class fake_extension(object): + def __init__(self, name, alias, description, namespace, version): + self.name = name + self.alias = alias + self.__doc__ = description + self.namespace = namespace + self.version = version + + +fake_extensions = { + 'ext1-alias': fake_extension('ext1', 'ext1-alias', 'ext1 description', + 'ext1 namespace', 1), + 'ext2-alias': fake_extension('ext2', 'ext2-alias', 'ext2 description', + 'ext2 namespace', 2), + 'ext3-alias': fake_extension('ext3', 'ext3-alias', 'ext3 description', + 'ext3 namespace', 1) +} + +def fake_policy_enforce(context, action, target, do_raise=True): + return True + +def fake_policy_enforce_selective(context, action, target, do_raise=True): + if action == 'compute_extension:v3:ext1-alias:discoverable': + raise exception.NotAuthorized + else: + return True + + +class ExtensionInfoTest(test.TestCase): + + def setUp(self): + super(ExtensionInfoTest, self).setUp() + ext_info = plugins.LoadedExtensionInfo() + ext_info.extensions = fake_extensions + self.controller = extension_info.ExtensionInfoController(ext_info) + + def test_extension_info_list(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce) + req = fakes.HTTPRequestV3.blank('/extensions') + res_dict = self.controller.index(req) + self.assertEqual(3, len(res_dict['extensions'])) + for e in res_dict['extensions']: + self.assertIn(e['alias'], fake_extensions) + self.assertEqual(e['name'], fake_extensions[e['alias']].name) + self.assertEqual(e['alias'], fake_extensions[e['alias']].alias) + self.assertEqual(e['description'], + fake_extensions[e['alias']].__doc__) + self.assertEqual(e['namespace'], + fake_extensions[e['alias']].namespace) + self.assertEqual(e['version'], + fake_extensions[e['alias']].version) + + def test_extension_info_show(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce) + req = fakes.HTTPRequestV3.blank('/extensions/ext1-alias') + res_dict = self.controller.show(req, 'ext1-alias') + self.assertEqual(1, len(res_dict)) + self.assertEqual(res_dict['extension']['name'], + fake_extensions['ext1-alias'].name) + self.assertEqual(res_dict['extension']['alias'], + fake_extensions['ext1-alias'].alias) + self.assertEqual(res_dict['extension']['description'], + fake_extensions['ext1-alias'].__doc__) + self.assertEqual(res_dict['extension']['namespace'], + fake_extensions['ext1-alias'].namespace) + self.assertEqual(res_dict['extension']['version'], + fake_extensions['ext1-alias'].version) + + def test_extension_info_list_not_all_discoverable(self): + self.stubs.Set(policy, 'enforce', fake_policy_enforce_selective) + req = fakes.HTTPRequestV3.blank('/extensions') + res_dict = self.controller.index(req) + self.assertEqual(2, len(res_dict['extensions'])) + for e in res_dict['extensions']: + self.assertNotEqual('ext1-alias', e['alias']) + self.assertIn(e['alias'], fake_extensions) + self.assertEqual(e['name'], fake_extensions[e['alias']].name) + self.assertEqual(e['alias'], fake_extensions[e['alias']].alias) + self.assertEqual(e['description'], + fake_extensions[e['alias']].__doc__) + self.assertEqual(e['namespace'], + fake_extensions[e['alias']].namespace) + self.assertEqual(e['version'], + fake_extensions[e['alias']].version) |