Validate PUT of reserved queue attributes metadata

We recently introduced reserved queue metadata feature in our API v2.
And because it's a feature of API v2, we decided not to change at all
our older APIs. But through our older APIs it's possible to PUT invalid
queue attributes, which can cause unstable work of API v2.

This patch makes API v1 restrict any metadata keys which names start
with "_", because they are probably reserved queue attributes.

This patch makes API v1.1 validate metadata like API v2 validates it now
to ensure that the reserved queue attributes have valid values, before
sending them to the database.

This patch also adds unit tests to APIs.

APIImpact
Closes-Bug: 1557247

Change-Id: Idcd063787c8d8da49a9c3d126441f9336c85e7cd
This commit is contained in:
Eva Balycheva 2016-03-15 05:49:37 +03:00
parent f3a4132684
commit 400fc6ec7d
6 changed files with 83 additions and 21 deletions

View File

@ -208,7 +208,7 @@ class TestQueueMetaData(base.V1FunctionalTestBase):
self.client.set_base_url(self.queue_metadata_url)
@ddt.data({},
{'_queue': 'Top Level field with _'},
{'@queue': 'Top Level field with @'},
annotated('test_insert_queue_metadata_unicode', {
u'\u6c49\u5b57': u'Unicode: \u6c49\u5b57'
}),

View File

@ -109,3 +109,19 @@ class TestValidation(base.V1Base):
self.addCleanup(self.simulate_delete, queue_path_with_v2,
self.project_id)
self.assertEqual(falcon.HTTP_201, self.srmock.status)
def test_queue_metadata_putting(self):
# Ensure setting reserved queue attributes (which names start with
# '_') is not allowed in API v1.
# Try set real _default_message_ttl queue attribute.
self.simulate_put(self.queue_path + '/metadata',
self.project_id,
body='{"_default_message_ttl": 60}')
self.assertEqual(falcon.HTTP_400, self.srmock.status)
# Try set a fictional queue attribute.
self.simulate_put(self.queue_path + '/metadata',
self.project_id,
body='{"_min_message_niceness": 9000}')
self.assertEqual(falcon.HTTP_400, self.srmock.status)

View File

@ -97,3 +97,42 @@ class TestValidation(base.V1_1Base):
self.project_id,
headers=empty_headers)
self.assertEqual(falcon.HTTP_400, self.srmock.status)
def test_queue_metadata_putting(self):
# Test _default_message_ttl
# TTL normal case
queue_1 = self.url_prefix + '/queues/queue1'
self.simulate_put(queue_1,
self.project_id,
body='{"_default_message_ttl": 60}')
self.addCleanup(self.simulate_delete, queue_1, self.project_id,
headers=self.headers)
self.assertEqual(falcon.HTTP_201, self.srmock.status)
# TTL under min
self.simulate_put(queue_1,
self.project_id,
body='{"_default_message_ttl": 59}')
self.assertEqual(falcon.HTTP_400, self.srmock.status)
# TTL over max
self.simulate_put(queue_1,
self.project_id,
body='{"_default_message_ttl": 1209601}')
self.assertEqual(falcon.HTTP_400, self.srmock.status)
# Test _max_messages_post_size
# Size normal case
queue_2 = self.url_prefix + '/queues/queue2'
self.simulate_put(queue_2,
self.project_id,
body='{"_max_messages_post_size": 255}')
self.addCleanup(self.simulate_delete, queue_2, self.project_id,
headers=self.headers)
self.assertEqual(falcon.HTTP_201, self.srmock.status)
# Size over max
self.simulate_put(queue_2,
self.project_id,
body='{"_max_messages_post_size": 257}')
self.assertEqual(falcon.HTTP_400, self.srmock.status)

View File

@ -61,14 +61,20 @@ class Resource(object):
try:
# Place JSON size restriction before parsing
self._validate.queue_metadata_length(req.content_length)
# Deserialize queue metadata
document = wsgi_utils.deserialize(req.stream, req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
# Restrict setting any reserved queue attributes
for key in metadata:
if key.startswith('_'):
description = _(u'Reserved queue attributes in metadata '
u'(which names start with "_") can not be '
u'set in API v1.')
raise validation.ValidationFailed(description)
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
# Deserialize queue metadata
document = wsgi_utils.deserialize(req.stream, req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
try:
self._queue_ctrl.set_metadata(queue_name,
metadata=metadata,

View File

@ -61,16 +61,20 @@ class ItemResource(object):
try:
# Place JSON size restriction before parsing
self._validate.queue_metadata_length(req.content_length)
# Deserialize queue metadata
metadata = None
if req.content_length:
document = wsgi_utils.deserialize(req.stream,
req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
# NOTE(Eva-i): reserved queue attributes is Zaqar's feature since
# API v2. But we have to ensure the bad data will not come from
# older APIs, so we validate metadata here.
self._validate.queue_metadata_putting(metadata)
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
# Deserialize queue metadata
metadata = None
if req.content_length:
document = wsgi_utils.deserialize(req.stream, req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
try:
created = self._queue_controller.create(queue_name,
metadata=metadata,

View File

@ -63,18 +63,18 @@ class ItemResource(object):
try:
# Place JSON size restriction before parsing
self._validate.queue_metadata_length(req.content_length)
# Deserialize queue metadata
metadata = None
if req.content_length:
document = wsgi_utils.deserialize(req.stream,
req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
self._validate.queue_metadata_putting(metadata)
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
# Deserialize queue metadata
metadata = None
if req.content_length:
document = wsgi_utils.deserialize(req.stream, req.content_length)
metadata = wsgi_utils.sanitize(document, spec=None)
try:
self._validate.queue_metadata_putting(metadata)
created = self._queue_controller.create(queue_name,
metadata=metadata,
project=project_id)
@ -82,9 +82,6 @@ class ItemResource(object):
except storage_errors.FlavorDoesNotExist as ex:
LOG.exception(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
description = _(u'Queue could not be created.')