Fix silently overwrite user specified content type
Previously, even user specified the response content type of api 'catalog/packages/<package_id>/ui', which will be silently replaced by text/plain. To improve it, add list 'specific_content_types' to make resource controller can specify different content type for each action. And it will raise exception when user specified content type is unsupported. Change-Id: I5d56461333d730f0f9fbe715e9bb066e5b004203 Closes-Bug: #1476543
This commit is contained in:
parent
788cd5aa7a
commit
1bc300dd36
@ -377,17 +377,12 @@ class Controller(object):
|
||||
db_api.category_delete(category_id)
|
||||
|
||||
|
||||
class PackageSerializer(wsgi.ResponseSerializer):
|
||||
def serialize(self, action_result, accept, action):
|
||||
if action == 'get_ui':
|
||||
accept = 'text/plain'
|
||||
elif action in ('download', 'get_logo', 'get_supplier_logo'):
|
||||
accept = 'application/octet-stream'
|
||||
return super(PackageSerializer, self).serialize(action_result,
|
||||
accept,
|
||||
action)
|
||||
|
||||
|
||||
def create_resource():
|
||||
serializer = PackageSerializer()
|
||||
return wsgi.Resource(Controller(), serializer=serializer)
|
||||
specific_content_types = {
|
||||
'get_ui': ['text/plain'],
|
||||
'download': ['application/octet-stream'],
|
||||
'get_logo': ['application/octet-stream'],
|
||||
'get_supplier_logo': ['application/octet-stream']}
|
||||
deserializer = wsgi.RequestDeserializer(
|
||||
specific_content_types=specific_content_types)
|
||||
return wsgi.Resource(Controller(), deserializer=deserializer)
|
||||
|
@ -295,7 +295,8 @@ class Request(webob.Request):
|
||||
'application/xml',
|
||||
'application/octet-stream')
|
||||
|
||||
def best_match_content_type(self, supported_content_types=None):
|
||||
def best_match_content_type(self, action, supported_content_types=None,
|
||||
specific_content_types=None):
|
||||
"""Determine the requested response content-type.
|
||||
|
||||
Based on the query extension then the Accept header.
|
||||
@ -311,7 +312,11 @@ class Request(webob.Request):
|
||||
if ctype in supported_content_types:
|
||||
return ctype
|
||||
|
||||
bm = self.accept.best_match(supported_content_types)
|
||||
if specific_content_types and action in specific_content_types:
|
||||
bm = self.accept.best_match(specific_content_types[action])
|
||||
else:
|
||||
bm = self.accept.best_match(supported_content_types)
|
||||
|
||||
if not bm:
|
||||
raise exceptions.UnsupportedContentType(content_type=self.accept)
|
||||
return bm
|
||||
@ -630,9 +635,10 @@ class RequestDeserializer(object):
|
||||
"""Break up a Request object into more useful pieces."""
|
||||
|
||||
def __init__(self, body_deserializers=None, headers_deserializer=None,
|
||||
supported_content_types=None):
|
||||
supported_content_types=None, specific_content_types=None):
|
||||
|
||||
self.supported_content_types = supported_content_types
|
||||
self.specific_content_types = specific_content_types
|
||||
|
||||
self.body_deserializers = {
|
||||
'application/xml': XMLDeserializer(),
|
||||
@ -660,7 +666,7 @@ class RequestDeserializer(object):
|
||||
action_args.update(self.deserialize_headers(request, action))
|
||||
action_args.update(self.deserialize_body(request, action))
|
||||
|
||||
accept = self.get_expected_content_type(request)
|
||||
accept = self.get_expected_content_type(request, action)
|
||||
|
||||
return (action, action_args, accept)
|
||||
|
||||
@ -697,8 +703,10 @@ class RequestDeserializer(object):
|
||||
except (KeyError, TypeError):
|
||||
raise exceptions.UnsupportedContentType(content_type=content_type)
|
||||
|
||||
def get_expected_content_type(self, request):
|
||||
return request.best_match_content_type(self.supported_content_types)
|
||||
def get_expected_content_type(self, request, action):
|
||||
return request.best_match_content_type(action,
|
||||
self.supported_content_types,
|
||||
self.specific_content_types)
|
||||
|
||||
def get_action_args(self, request_environment):
|
||||
"""Parse dictionary created by routes library."""
|
||||
|
@ -210,13 +210,28 @@ class MuranoClient(rest_client.RestClient):
|
||||
return self.delete('v1/catalog/packages/{0}'.format(id))
|
||||
|
||||
def download_package(self, id):
|
||||
return self.get('v1/catalog/packages/{0}/download'.format(id))
|
||||
headers = {
|
||||
'X-Auth-Token': self.auth_provider.get_token(),
|
||||
'content-type': 'application/octet-stream'
|
||||
}
|
||||
return self.get('v1/catalog/packages/{0}/download'.format(id),
|
||||
headers=headers)
|
||||
|
||||
def get_ui_definition(self, id):
|
||||
return self.get('v1/catalog/packages/{0}/ui'.format(id))
|
||||
headers = {
|
||||
'X-Auth-Token': self.auth_provider.get_token(),
|
||||
'content-type': 'text/plain'
|
||||
}
|
||||
return self.get('v1/catalog/packages/{0}/ui'.format(id),
|
||||
headers=headers)
|
||||
|
||||
def get_logo(self, id):
|
||||
return self.get('v1/catalog/packages/{0}/logo'.format(id))
|
||||
headers = {
|
||||
'X-Auth-Token': self.auth_provider.get_token(),
|
||||
'content-type': 'application/octet-stream'
|
||||
}
|
||||
return self.get('v1/catalog/packages/{0}/logo'.format(id),
|
||||
headers=headers)
|
||||
|
||||
def list_categories(self):
|
||||
resp, body = self.get('v1/catalog/packages/categories')
|
||||
|
@ -289,6 +289,74 @@ class TestCatalogApi(test_base.ControllerTest, test_base.MuranoApiTestCase):
|
||||
result = req.get_response(self.api)
|
||||
|
||||
self.assertEqual(415, result.status_code)
|
||||
self.assertTrue('Unsupported Content-Type' in result.body)
|
||||
|
||||
def test_get_ui_definition(self):
|
||||
self._set_policy_rules(
|
||||
{'get_package': '@'}
|
||||
)
|
||||
package_from_dir, package = self._test_package()
|
||||
|
||||
saved_package = db_catalog_api.package_upload(package, '')
|
||||
|
||||
self.expect_policy_check('get_package',
|
||||
{'package_id': saved_package.id})
|
||||
|
||||
req = self._get_with_accept('/catalog/packages/%s/ui'
|
||||
% saved_package.id,
|
||||
accept="text/plain")
|
||||
|
||||
result = req.get_response(self.api)
|
||||
|
||||
self.assertEqual(200, result.status_code)
|
||||
|
||||
def test_get_ui_definition_negative(self):
|
||||
package_from_dir, package = self._test_package()
|
||||
|
||||
saved_package = db_catalog_api.package_upload(package, '')
|
||||
|
||||
req = self._get_with_accept('/catalog/packages/%s/ui'
|
||||
% saved_package.id,
|
||||
accept='application/foo')
|
||||
|
||||
result = req.get_response(self.api)
|
||||
|
||||
self.assertEqual(415, result.status_code)
|
||||
self.assertTrue('Unsupported Content-Type' in result.body)
|
||||
|
||||
def test_get_logo(self):
|
||||
self._set_policy_rules(
|
||||
{'get_package': '@'}
|
||||
)
|
||||
package_from_dir, package = self._test_package()
|
||||
|
||||
saved_package = db_catalog_api.package_upload(package, '')
|
||||
|
||||
self.expect_policy_check('get_package',
|
||||
{'package_id': saved_package.id})
|
||||
|
||||
req = self._get_with_accept('/catalog/packages/%s/logo'
|
||||
% saved_package.id,
|
||||
accept="application/octet-stream")
|
||||
|
||||
result = req.get_response(self.api)
|
||||
|
||||
self.assertEqual(200, result.status_code)
|
||||
self.assertEqual(package['logo'], result.body)
|
||||
|
||||
def test_get_logo_negative(self):
|
||||
package_from_dir, package = self._test_package()
|
||||
|
||||
saved_package = db_catalog_api.package_upload(package, '')
|
||||
|
||||
req = self._get_with_accept('/catalog/packages/%s/logo'
|
||||
% saved_package.id,
|
||||
accept='application/foo')
|
||||
|
||||
result = req.get_response(self.api)
|
||||
|
||||
self.assertEqual(415, result.status_code)
|
||||
self.assertTrue('Unsupported Content-Type' in result.body)
|
||||
|
||||
def test_add_public_unauthorized(self):
|
||||
self._set_policy_rules({
|
||||
|
Loading…
x
Reference in New Issue
Block a user