Fix the validation of flavor_extraspecs v2 API

"create flavor_extraspecs" v2 API does not validate the data type
of a request body. If invalid parameter is passed, an internal error
happens. If many invalid requests come, a log file would be occupied
with traceback.

In addition, it does not validate the lengths of both key and value of
extra_specs. extra_specs are stored into the instance_type_extra_specs
table, and the key and value are defined as String(255).

This patch fixes the validation code from the viewpoint of data type
and key/value length.

Closes-Bug: #1264220

Change-Id: I195bd5d45a896e9b26dd81dab1e49c9f939b4805
This commit is contained in:
Ken'ichi Ohmichi 2014-02-24 16:28:41 +09:00
parent ef3b1385cb
commit 8010c8faf9
2 changed files with 65 additions and 18 deletions

View File

@ -24,6 +24,7 @@ from nova.compute import flavors
from nova import db
from nova import exception
from nova.openstack.common.gettextutils import _
from nova import utils
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
@ -55,12 +56,25 @@ class FlavorExtraSpecsController(object):
expl = _('No Request Body')
raise exc.HTTPBadRequest(explanation=expl)
def _check_key_names(self, keys):
def _check_extra_specs(self, specs):
if type(specs) is not dict:
msg = _('Bad extra_specs provided')
raise exc.HTTPBadRequest(explanation=msg)
try:
flavors.validate_extra_spec_keys(keys)
flavors.validate_extra_spec_keys(specs.keys())
except exception.InvalidInput as error:
raise exc.HTTPBadRequest(explanation=error.format_message())
for key, value in specs.iteritems():
try:
utils.check_string_length(key, 'extra_specs key',
min_length=1, max_length=255)
utils.check_string_length(value, 'extra_specs value',
max_length=255)
except exception.InvalidInput as error:
raise exc.HTTPBadRequest(explanation=error.format_message())
@wsgi.serializers(xml=ExtraSpecsTemplate)
def index(self, req, flavor_id):
"""Returns the list of extra specs for a given flavor."""
@ -74,7 +88,7 @@ class FlavorExtraSpecsController(object):
authorize(context, action='create')
self._check_body(body)
specs = body.get('extra_specs')
self._check_key_names(specs.keys())
self._check_extra_specs(specs)
try:
db.flavor_extra_specs_update_or_create(context,
flavor_id,
@ -87,7 +101,7 @@ class FlavorExtraSpecsController(object):
def update(self, req, flavor_id, id, body):
context = req.environ['nova.context']
authorize(context, action='update')
self._check_body(body)
self._check_extra_specs(body)
if id not in body:
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)

View File

@ -142,7 +142,7 @@ class FlavorsExtraSpecsTest(test.TestCase):
self.assertRaises(exception.NotAuthorized, self.controller.create,
req, 1, body)
def test_create_empty_body(self):
def _test_create_bad_request(self, body):
self.stubs.Set(nova.db,
'flavor_extra_specs_update_or_create',
return_create_flavor_extra_specs)
@ -150,7 +150,27 @@ class FlavorsExtraSpecsTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs',
use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, 1, '')
req, 1, body)
def test_create_empty_body(self):
self._test_create_bad_request('')
def test_create_non_dict_extra_specs(self):
self._test_create_bad_request({"extra_specs": "non_dict"})
def test_create_non_string_value(self):
self._test_create_bad_request({"extra_specs": {"key1": None}})
def test_create_zero_length_key(self):
self._test_create_bad_request({"extra_specs": {"": "value1"}})
def test_create_long_key(self):
key = "a" * 256
self._test_create_bad_request({"extra_specs": {key: "value1"}})
def test_create_long_value(self):
value = "a" * 256
self._test_create_bad_request({"extra_specs": {"key1": value}})
@mock.patch('nova.db.flavor_extra_specs_update_or_create')
def test_create_invalid_specs_key(self, mock_flavor_extra_specs):
@ -199,27 +219,40 @@ class FlavorsExtraSpecsTest(test.TestCase):
self.assertRaises(exception.NotAuthorized, self.controller.update,
req, 1, 'key1', body)
def test_update_item_empty_body(self):
def _test_update_item_bad_request(self, body):
self.stubs.Set(nova.db,
'flavor_extra_specs_update_or_create',
return_create_flavor_extra_specs)
req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' +
'/key1', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
req, 1, 'key1', '')
def test_update_item_too_many_keys(self):
self.stubs.Set(nova.db,
'flavor_extra_specs_update_or_create',
return_create_flavor_extra_specs)
body = {"key1": "value1", "key2": "value2"}
req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' +
'/key1', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
req, 1, 'key1', body)
def test_update_item_empty_body(self):
self._test_update_item_bad_request('')
def test_update_item_too_many_keys(self):
body = {"key1": "value1", "key2": "value2"}
self._test_update_item_bad_request(body)
def test_update_item_non_dict_extra_specs(self):
self._test_update_item_bad_request("non_dict")
def test_update_item_non_string_value(self):
self._test_update_item_bad_request({"key1": None})
def test_update_item_zero_length_key(self):
self._test_update_item_bad_request({"": "value1"})
def test_update_item_long_key(self):
key = "a" * 256
self._test_update_item_bad_request({key: "value1"})
def test_update_item_long_value(self):
value = "a" * 256
self._test_update_item_bad_request({"key1": value})
def test_update_item_body_uri_mismatch(self):
self.stubs.Set(nova.db,
'flavor_extra_specs_update_or_create',