Commit 346efe3b authored by Denis Laxalde's avatar Denis Laxalde
Browse files

Detect bad mime types upon data submission views

Previously, we did "request.json_body" in every data submission views
(like entity creation or update, with HTTP method being POST or PUT).
This instruction might fail with JSONDecodeError exception if the body
fails to JSON decode. This would result in a 500 error response. Rather
we want to return a 40x response indicating that this is a client error.

So we now raise a 415 Unsupported Media Type error through a
_json_body() helper function every times we need to decode JSON body of
incoming requests. Notice that since json module only has
JSONDecodeError exception on Python3+, we catch ValueError on Python2.

A functional test is added for entity creation view.
parent be00ec65b525
......@@ -16,10 +16,12 @@
"""cubicweb-jsonschema Pyramid views for the JSON API of entities."""
import json
import traceback
from pyramid import httpexceptions
from pyramid.view import view_config
import six
from cubicweb import (
ValidationError,
......@@ -54,6 +56,17 @@ def _created(request, resource):
return mapper.serialize()
def _json_body(request):
"""Return the JSON body found in `request` or raise "415 Unsupported Media
Type".
"""
exc_type = ValueError if six.PY2 else json.JSONDecodeError
try:
return request.json_body
except exc_type:
raise httpexceptions.HTTPUnsupportedMediaType()
@view_config(
route_name='cubicweb-jsonschema.entities',
context='cubicweb_jsonschema.resources.IRoot',
......@@ -129,7 +142,7 @@ def create_entity(context, request):
# entry as a "pointer", would require selection context to be an
# ETypeSchemaResource.
mapper = context.mapper(CREATION_ROLE)
instance = request.json_body
instance = _json_body(request)
values = mapper.values(instance)
entity = request.cw_cnx.create_entity(mapper.etype, **values)
request.cw_cnx.commit()
......@@ -147,7 +160,7 @@ def update_entity(context, request):
"""Update an entity from JSON data."""
entity = context.entity
mapper = context.mapper()
instance = request.json_body
instance = _json_body(request)
edited_props = list(instance)
values = mapper.values(instance)
entity.cw_set(**values)
......@@ -226,7 +239,7 @@ def post_related_entities(context, request):
entity=entity, rtype=rtype, role=role,
resource=context,
)
payload = request.json_body
payload = _json_body(request)
if not isinstance(payload, dict):
return json_problem(status=400, detail='expecting an object')
instance = {rtype: [payload]}
......@@ -275,7 +288,7 @@ def get_related_entity(context, request):
def update_related_entity(context, request):
"""Update a related entity from JSON data."""
mapper = context.mapper()
instance = request.json_body
instance = _json_body(request)
edited_props = list(instance)
values = mapper.values(instance)
entity = context.entity
......@@ -330,8 +343,9 @@ def get_entity_workflow_transitions(context, request):
)
def post_entity_workflow_transition(context, request):
"""Pass a workflow transition for an entity."""
trname = request.json_body['name']
comment = request.json_body.get('comment')
payload = _json_body(request)
trname = payload['name']
comment = payload.get('comment')
wfobj = context.for_entity.cw_adapt_to('IWorkflowable')
wfobj.fire_transition(trname, comment=comment)
request.cw_cnx.commit()
......
......@@ -140,6 +140,12 @@ class EntitiesTC(BaseTC):
jsonschema.validate(related, jschema)
self.assertEqual(related, expected)
def test_post_bad_mtype(self):
self.login()
url = '/book/'
self.webapp.post(url, 'this is not json', status=415,
headers={'Accept': 'application/json'})
def test_post_related_not_an_object(self):
with self.admin_access.cnx() as cnx:
book = cnx.create_entity(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment