summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorChris Yeoh <cyeoh@au1.ibm.com>2013-06-05 21:07:39 +0930
committerChris Yeoh <cyeoh@au1.ibm.com>2013-06-10 10:59:43 +0930
commit614b209d00cc369f5ea87a81c5a9eff47c29c43a (patch)
tree90cb28e0ab995d60d6b1e903b15a25d365d2d629 /nova
parentd5ae8d5667fee22ba4df4feea53224874a19d167 (diff)
downloadnova-614b209d00cc369f5ea87a81c5a9eff47c29c43a.tar.gz
nova-614b209d00cc369f5ea87a81c5a9eff47c29c43a.tar.xz
nova-614b209d00cc369f5ea87a81c5a9eff47c29c43a.zip
Adds v3 API extension discovery filtering
Adds ability for a v3 loaded extension to be visible in /v3/extensions to be dependent on the discoverable action policy for that extension. Note that this does not actually effect whether or not the functionality provided by the extension is accessible Implements blueprint nova-v3-api-filter Change-Id: I3f6ba7ea59e7abfa9d57b79ab18aa5b675e64118
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/compute/plugins/v3/extension_info.py23
-rw-r--r--nova/api/openstack/extensions.py4
-rw-r--r--nova/tests/api/openstack/compute/plugins/v3/test_extension_info.py108
3 files changed, 131 insertions, 4 deletions
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)