Add functionality to define requests without body
This allows functions that do not accept bodies to define this in the router file. As currently many requests will cause a 500 if a body is supplied when the API request does not expect it. This currently only affects the core parts of the v2 api, that is, calls to v2/images and v2/schemas. It does not cover the "tasks" API or the metadefs api as I was keeping this patch concise. As this does not affect the behaviour if not included this makes no change to the metadefs api behaviour. DocImpact Partial-Bug: 1475647 Change-Id: Ieb510e5516128078d40d39fd9b4f339ce64e10e7
This commit is contained in:
parent
32404fad45
commit
9b430f9951
|
@ -40,7 +40,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/schemas/image',
|
mapper.connect('/schemas/image',
|
||||||
controller=schemas_resource,
|
controller=schemas_resource,
|
||||||
action='image',
|
action='image',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/schemas/image',
|
mapper.connect('/schemas/image',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -48,7 +49,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/schemas/images',
|
mapper.connect('/schemas/images',
|
||||||
controller=schemas_resource,
|
controller=schemas_resource,
|
||||||
action='images',
|
action='images',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/schemas/images',
|
mapper.connect('/schemas/images',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -56,7 +58,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/schemas/member',
|
mapper.connect('/schemas/member',
|
||||||
controller=schemas_resource,
|
controller=schemas_resource,
|
||||||
action='member',
|
action='member',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/schemas/member',
|
mapper.connect('/schemas/member',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -65,7 +68,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/schemas/members',
|
mapper.connect('/schemas/members',
|
||||||
controller=schemas_resource,
|
controller=schemas_resource,
|
||||||
action='members',
|
action='members',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/schemas/members',
|
mapper.connect('/schemas/members',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -388,11 +392,13 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}',
|
mapper.connect('/images/{image_id}',
|
||||||
controller=images_resource,
|
controller=images_resource,
|
||||||
action='show',
|
action='show',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}',
|
mapper.connect('/images/{image_id}',
|
||||||
controller=images_resource,
|
controller=images_resource,
|
||||||
action='delete',
|
action='delete',
|
||||||
conditions={'method': ['DELETE']})
|
conditions={'method': ['DELETE']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}',
|
mapper.connect('/images/{image_id}',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -402,11 +408,13 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/actions/deactivate',
|
mapper.connect('/images/{image_id}/actions/deactivate',
|
||||||
controller=image_actions_resource,
|
controller=image_actions_resource,
|
||||||
action='deactivate',
|
action='deactivate',
|
||||||
conditions={'method': ['POST']})
|
conditions={'method': ['POST']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/actions/reactivate',
|
mapper.connect('/images/{image_id}/actions/reactivate',
|
||||||
controller=image_actions_resource,
|
controller=image_actions_resource,
|
||||||
action='reactivate',
|
action='reactivate',
|
||||||
conditions={'method': ['POST']})
|
conditions={'method': ['POST']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/actions/deactivate',
|
mapper.connect('/images/{image_id}/actions/deactivate',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -420,7 +428,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/file',
|
mapper.connect('/images/{image_id}/file',
|
||||||
controller=image_data_resource,
|
controller=image_data_resource,
|
||||||
action='download',
|
action='download',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/file',
|
mapper.connect('/images/{image_id}/file',
|
||||||
controller=image_data_resource,
|
controller=image_data_resource,
|
||||||
action='upload',
|
action='upload',
|
||||||
|
@ -434,11 +443,13 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
||||||
controller=image_tags_resource,
|
controller=image_tags_resource,
|
||||||
action='update',
|
action='update',
|
||||||
conditions={'method': ['PUT']})
|
conditions={'method': ['PUT']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
||||||
controller=image_tags_resource,
|
controller=image_tags_resource,
|
||||||
action='delete',
|
action='delete',
|
||||||
conditions={'method': ['DELETE']})
|
conditions={'method': ['DELETE']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
mapper.connect('/images/{image_id}/tags/{tag_value}',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
@ -448,7 +459,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/members',
|
mapper.connect('/images/{image_id}/members',
|
||||||
controller=image_members_resource,
|
controller=image_members_resource,
|
||||||
action='index',
|
action='index',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/members',
|
mapper.connect('/images/{image_id}/members',
|
||||||
controller=image_members_resource,
|
controller=image_members_resource,
|
||||||
action='create',
|
action='create',
|
||||||
|
@ -461,7 +473,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/members/{member_id}',
|
mapper.connect('/images/{image_id}/members/{member_id}',
|
||||||
controller=image_members_resource,
|
controller=image_members_resource,
|
||||||
action='show',
|
action='show',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/members/{member_id}',
|
mapper.connect('/images/{image_id}/members/{member_id}',
|
||||||
controller=image_members_resource,
|
controller=image_members_resource,
|
||||||
action='update',
|
action='update',
|
||||||
|
@ -469,7 +482,8 @@ class API(wsgi.Router):
|
||||||
mapper.connect('/images/{image_id}/members/{member_id}',
|
mapper.connect('/images/{image_id}/members/{member_id}',
|
||||||
controller=image_members_resource,
|
controller=image_members_resource,
|
||||||
action='delete',
|
action='delete',
|
||||||
conditions={'method': ['DELETE']})
|
conditions={'method': ['DELETE']},
|
||||||
|
body_reject=True)
|
||||||
mapper.connect('/images/{image_id}/members/{member_id}',
|
mapper.connect('/images/{image_id}/members/{member_id}',
|
||||||
controller=reject_method_resource,
|
controller=reject_method_resource,
|
||||||
action='reject',
|
action='reject',
|
||||||
|
|
|
@ -38,6 +38,7 @@ from oslo_concurrency import processutils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import strutils
|
||||||
import routes
|
import routes
|
||||||
import routes.middleware
|
import routes.middleware
|
||||||
import six
|
import six
|
||||||
|
@ -877,8 +878,13 @@ class Resource(object):
|
||||||
"""WSGI method that controls (de)serialization and method dispatch."""
|
"""WSGI method that controls (de)serialization and method dispatch."""
|
||||||
action_args = self.get_action_args(request.environ)
|
action_args = self.get_action_args(request.environ)
|
||||||
action = action_args.pop('action', None)
|
action = action_args.pop('action', None)
|
||||||
|
body_reject = strutils.bool_from_string(
|
||||||
|
action_args.pop('body_reject', None))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if body_reject and self.deserializer.has_body(request):
|
||||||
|
msg = _('A body is not expected with this request.')
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
deserialized_request = self.dispatch(self.deserializer,
|
deserialized_request = self.dispatch(self.deserializer,
|
||||||
action, request)
|
action, request)
|
||||||
action_args.update(deserialized_request)
|
action_args.update(deserialized_request)
|
||||||
|
|
|
@ -638,6 +638,79 @@ class TestImages(functional.FunctionalTest):
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
|
def test_methods_that_dont_accept_illegal_bodies(self):
|
||||||
|
# Check images can be reached
|
||||||
|
self.start_servers(**self.__dict__.copy())
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
response = requests.get(path, headers=self._headers())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
# Test all the schemas
|
||||||
|
schema_urls = [
|
||||||
|
'/v2/schemas/images',
|
||||||
|
'/v2/schemas/image',
|
||||||
|
'/v2/schemas/members',
|
||||||
|
'/v2/schemas/member',
|
||||||
|
]
|
||||||
|
for value in schema_urls:
|
||||||
|
path = self._url(value)
|
||||||
|
data = jsonutils.dumps(["body"])
|
||||||
|
response = requests.get(path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
# Create image for use with tests
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(201, response.status_code)
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
|
||||||
|
test_urls = [
|
||||||
|
('/v2/images/%s', 'get'),
|
||||||
|
('/v2/images/%s/actions/deactivate', 'post'),
|
||||||
|
('/v2/images/%s/actions/reactivate', 'post'),
|
||||||
|
('/v2/images/%s/tags/mytag', 'put'),
|
||||||
|
('/v2/images/%s/tags/mytag', 'delete'),
|
||||||
|
('/v2/images/%s/members', 'get'),
|
||||||
|
('/v2/images/%s/file', 'get'),
|
||||||
|
('/v2/images/%s', 'delete'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for link, method in test_urls:
|
||||||
|
path = self._url(link % image_id)
|
||||||
|
data = jsonutils.dumps(["body"])
|
||||||
|
response = getattr(requests, method)(
|
||||||
|
path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
# DELETE /images/imgid without legal json
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
data = '{"hello"]'
|
||||||
|
response = requests.delete(path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
# POST /images/imgid/members
|
||||||
|
path = self._url('/v2/images/%s/members' % image_id)
|
||||||
|
data = jsonutils.dumps({'member': TENANT3})
|
||||||
|
response = requests.post(path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
# GET /images/imgid/members/memid
|
||||||
|
path = self._url('/v2/images/%s/members/%s' % (image_id, TENANT3))
|
||||||
|
data = jsonutils.dumps(["body"])
|
||||||
|
response = requests.get(path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
# DELETE /images/imgid/members/memid
|
||||||
|
path = self._url('/v2/images/%s/members/%s' % (image_id, TENANT3))
|
||||||
|
data = jsonutils.dumps(["body"])
|
||||||
|
response = requests.delete(path, headers=self._headers(), data=data)
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
self.stop_servers()
|
||||||
|
|
||||||
def test_download_random_access(self):
|
def test_download_random_access(self):
|
||||||
self.start_servers(**self.__dict__.copy())
|
self.start_servers(**self.__dict__.copy())
|
||||||
# Create another image (with two deployer-defined properties)
|
# Create another image (with two deployer-defined properties)
|
||||||
|
|
Loading…
Reference in New Issue