Skip to content

Commit

Permalink
Fix schema validation when internal fields (fixes #1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Jun 13, 2017
1 parent 70c65a5 commit c6bf967
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ API is now at version **1.17**. See `API changelog`_.
- Fix requests output when running with make serve (fixes #1242)
- Fix pagination on permissions endpoint (fixes #1157)
- Fix pagination when max fetch storage is reached (fixes #1266)
- Fix schema validation when internal fields like ``id`` or ``last_modified`` are
marked as required (fixes #1244)

**Internal changes**

Expand Down
27 changes: 17 additions & 10 deletions kinto/views/records.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import copy

import jsonschema
from kinto.core import resource, utils
from kinto.core.errors import raise_invalid
Expand Down Expand Up @@ -55,23 +53,32 @@ def process_record(self, new, old=None):
if not schema or not asbool(settings.get(schema_validation)):
return new

collection_timestamp = self._collection[self.model.modified_field]

# Remove internal and auto-assigned fields from schema and record.
internal_fields = (self.model.id_field,
self.model.modified_field,
self.schema_field,
self.model.permissions_field)
required_fields = [f for f in schema.get('required', []) if f not in internal_fields]
if required_fields:
schema = {**schema, 'required': required_fields}
else:
schema = {f: v for f, v in new.items() if f != 'required'}
data = {f: v for f, v in new.items() if f not in internal_fields}

# Validate or fail with 400.
try:
stripped = copy.deepcopy(new)
stripped.pop(self.model.id_field, None)
stripped.pop(self.model.modified_field, None)
stripped.pop(self.model.permissions_field, None)
stripped.pop(self.schema_field, None)
jsonschema.validate(stripped, schema)
jsonschema.validate(data, schema)
except jsonschema_exceptions.ValidationError as e:
if e.validator_value:
field = e.validator_value[-1]
else:
field = e.schema_path[-1]
raise_invalid(self.request, name=field, description=e.message)

# Assign the schema version (collection object timestamp) to the record.
collection_timestamp = self._collection[self.model.modified_field]
new[self.schema_field] = collection_timestamp

return new

def collection_get(self):
Expand Down
26 changes: 26 additions & 0 deletions tests/test_views_collections_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,29 @@ def test_additional_properties_are_rejected(self):
headers=self.headers,
status=400)
assert "'extra' was unexpected)" in resp.json['message']


class InternalRequiredProperties(BaseWebTestWithSchema, unittest.TestCase):
def setUp(self):
super().setUp()
# See bug Kinto/kinto#1244
schema = {**SCHEMA, 'required': ['id', 'schema', 'last_modified']}
self.app.put_json(COLLECTION_URL,
{'data': {'schema': schema}},
headers=self.headers)

def test_record_can_be_validated_with_minimum_fields(self):
self.app.post_json(RECORDS_URL,
{'data': {}},
headers=self.headers)

def test_record_can_be_validated_with_every_fields(self):
self.app.post_json(RECORDS_URL,
{'data': {'id': 'abc', 'last_modified': 1234,
'schema': 42, 'title': 'b', 'name': 'n'}},
headers=self.headers)

def test_record_can_be_validated_with_id_and_last_modified(self):
self.app.post_json(RECORDS_URL,
{'data': {'id': 'abc', 'last_modified': 1234}},
headers=self.headers)

0 comments on commit c6bf967

Please sign in to comment.