diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index a31960078..194b92d49 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,6 +3,19 @@ Changelog
This document describes changes between each past release.
+3.2.1 (unreleased)
+==================
+
+**Bug fixes**
+
+- Fix HTTP API version number exposed (1.7)
+- Fix crash in authorization policy when requesting ``GET /buckets/collections`` (fixes #695)
+- Fix crash with PostgreSQL storage backend when provided id in POST is an integer (#688).
+ Regression introduced in 3.2.0 with #655.
+- Fix crash with PostgreSQL storage backend is configured as read-only and reaching
+ the records endpoint of an unknown collection (fixes #693, related #558)
+
+
3.2.0 (2016-06-14)
==================
@@ -10,7 +23,7 @@ This document describes changes between each past release.
- Allow record IDs to be any string instead of just UUIDs (fixes #655).
-Protocol is now at version **1.7**. See `API changelog `_.
+Protocol is now at version **1.7**. See `API changelog `_.
**New features**
@@ -52,7 +65,7 @@ Protocol is now at version **1.7**. See `API changelog `_.
+Protocol is now at version **1.6**. See `API changelog `_.
**Bug fixes**
@@ -161,7 +174,7 @@ Protocol is now at version **1.6**. See `API changelog `_.
+Protocol is now in version **1.5**. See `API changelog `_.
2.0.0 (2016-03-08)
@@ -184,7 +197,7 @@ Protocol is now in version **1.5**. See `API changelog `_.
+Protocol is now in version **1.4**. See `API changelog `_.
**Breaking changes**
@@ -214,7 +227,7 @@ Protocol is now in version **1.4**. See `API changelog `_.
+ `See more details `_.
- Track execution time on StatsD for each authentication sub-policy (mozilla-services/cliquet#639)
- Default console log renderer now has colours (mozilla-service/cliquet#671)
- Output Kinto version with ``kinto --version`` (thanks @ayusharma)
@@ -302,7 +315,7 @@ Protocol is now in version **1.4**. See `API changelog `_.
+Protocol is now version 1.3. See `API changelog `_.
**New features**
@@ -757,7 +770,7 @@ Settings
to use PostgreSQL instead of *Redis* (see default ``config/kinto.ini``)
- ``cliquet.basic_auth_enabled`` is now deprecated (`see *Cliquet*
docs to enable authentication backends
- `_)
+ `_)
**Internal changes**
diff --git a/docs/conf.py b/docs/conf.py
index 923c88d1c..aa5e9d04e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -74,7 +74,7 @@
# The short X.Y version.
version = '3.2'
# The full version, including alpha/beta/rc tags.
-release = '3.2.0'
+release = '3.2.1'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
diff --git a/kinto/__init__.py b/kinto/__init__.py
index 67c5f0661..75202fa24 100644
--- a/kinto/__init__.py
+++ b/kinto/__init__.py
@@ -12,7 +12,7 @@
__version__ = pkg_resources.get_distribution(__package__).version
# Implemented HTTP API Version
-HTTP_API_VERSION = '1.6'
+HTTP_API_VERSION = '1.7'
# Main kinto logger
logger = logging.getLogger(__name__)
diff --git a/kinto/authorization.py b/kinto/authorization.py
index 55d5a4289..904c975b2 100644
--- a/kinto/authorization.py
+++ b/kinto/authorization.py
@@ -1,7 +1,9 @@
-from kinto.core import authorization as core_authorization
+import re
+
from pyramid.security import IAuthorizationPolicy, Authenticated
from zope.interface import implementer
+from kinto.core import authorization as core_authorization
# Vocab really matters when you deal with permissions. Let's do a quick recap
# of the terms used here:
@@ -69,23 +71,15 @@
def get_object_type(object_uri):
"""Return the type of an object from its id."""
-
- obj_parts = object_uri.split('/')
- if len(obj_parts) % 2 == 0:
- object_uri = '/'.join(obj_parts[:-1])
-
- # Order matters here. More precise is tested first.
- if 'records' in object_uri:
- obj_type = 'record'
- elif 'collections' in object_uri:
- obj_type = 'collection'
- elif 'groups' in object_uri:
- obj_type = 'group'
- elif 'buckets' in object_uri:
- obj_type = 'bucket'
- else:
- obj_type = None
- return obj_type
+ if re.match(r'/buckets/(.+)/collections/(.+)/records/(.+)?', object_uri):
+ return 'record'
+ if re.match(r'/buckets/(.+)/collections/(.+)?', object_uri):
+ return 'collection'
+ if re.match(r'/buckets/(.+)/groups/(.+)?', object_uri):
+ return 'group'
+ if re.match(r'/buckets/(.+)?', object_uri):
+ return 'bucket'
+ return None
def build_permission_tuple(obj_type, unbound_permission, obj_parts):
diff --git a/kinto/core/resource/__init__.py b/kinto/core/resource/__init__.py
index 2572c92c6..40f600f94 100644
--- a/kinto/core/resource/__init__.py
+++ b/kinto/core/resource/__init__.py
@@ -717,7 +717,8 @@ def _raise_400_if_invalid_id(self, record_id):
:raises: :class:`pyramid.httpexceptions.HTTPBadRequest`
"""
- if not self.model.id_generator.match(six.text_type(record_id)):
+ is_string = isinstance(record_id, six.string_types)
+ if not is_string or not self.model.id_generator.match(record_id):
error_details = {
'location': 'path',
'description': "Invalid record id"
diff --git a/kinto/tests/core/resource/test_views.py b/kinto/tests/core/resource/test_views.py
index 56df58a4e..4fb1c6619 100644
--- a/kinto/tests/core/resource/test_views.py
+++ b/kinto/tests/core/resource/test_views.py
@@ -405,6 +405,13 @@ def test_id_is_validated_on_post(self):
headers=self.headers,
status=400)
+ with mock.patch.object(self.app.app.registry.id_generator, 'match',
+ return_value=True):
+ self.app.post_json(self.collection_url,
+ {'data': record},
+ headers=self.headers,
+ status=400)
+
def test_id_is_preserved_on_post(self):
record = MINIMALIST_RECORD.copy()
record_id = record['id'] = '472be9ec-26fe-461b-8282-9c4e4b207ab3'
diff --git a/kinto/tests/test_authorization.py b/kinto/tests/test_authorization.py
index 5753e53fc..4c8b4dac3 100644
--- a/kinto/tests/test_authorization.py
+++ b/kinto/tests/test_authorization.py
@@ -55,6 +55,11 @@ def test_build_perm_set_uri_can_construct_parents_set_uris(self):
'bucket', 'write', obj_parts),
(self.bucket_uri, 'write'))
+ def test_build_perm_set_supports_buckets_named_collections(self):
+ uri = '/buckets/collections'
+ self.assertEquals(build_permissions_set(uri, 'write'),
+ set([(uri, 'write')]))
+
def test_build_permission_tuple_fail_construct_children_set_uris(self):
obj_parts = self.bucket_uri.split('/')
# Cannot build record_uri from bucket obj_parts
diff --git a/kinto/tests/test_views_records.py b/kinto/tests/test_views_records.py
index 4994d2843..fe8cf9f62 100644
--- a/kinto/tests/test_views_records.py
+++ b/kinto/tests/test_views_records.py
@@ -37,6 +37,15 @@ def test_unknown_collection_raises_404(self):
other_collection = self.collection_url.replace('barley', 'pills')
self.app.get(other_collection, headers=self.headers, status=404)
+ def test_unknown_collection_does_not_query_timestamp(self):
+ other_collection = self.collection_url.replace('barley', 'pills')
+ patch = mock.patch.object(self.app.app.registry.storage,
+ 'collection_timestamp')
+ self.addCleanup(patch.stop)
+ mocked = patch.start()
+ self.app.get(other_collection, headers=self.headers, status=404)
+ self.assertFalse(mocked.called)
+
def test_parent_collection_is_fetched_only_once_in_batch(self):
batch = {'requests': []}
nb_create = 25
diff --git a/kinto/views/records.py b/kinto/views/records.py
index b0dca73de..0ff1bf493 100644
--- a/kinto/views/records.py
+++ b/kinto/views/records.py
@@ -26,23 +26,22 @@ class Record(resource.ShareableResource):
mapping = RecordSchema()
schema_field = 'schema'
- def __init__(self, *args, **kwargs):
- super(Record, self).__init__(*args, **kwargs)
-
- self.model.id_generator = RelaxedUUID()
-
+ def __init__(self, request, **kwargs):
+ # Before all, first check that the parent collection exists.
# Check if already fetched before (in batch).
- collections = self.request.bound_data.setdefault('collections', {})
- collection_uri = self.get_parent_id(self.request)
+ collections = request.bound_data.setdefault('collections', {})
+ collection_uri = self.get_parent_id(request)
if collection_uri not in collections:
# Unknown yet, fetch from storage.
collection_parent_id = '/buckets/%s' % self.bucket_id
- collection = object_exists_or_404(self.request,
+ collection = object_exists_or_404(request,
collection_id='collection',
parent_id=collection_parent_id,
object_id=self.collection_id)
collections[collection_uri] = collection
+ super(Record, self).__init__(request, **kwargs)
+ self.model.id_generator = RelaxedUUID()
self._collection = collections[collection_uri]
def get_parent_id(self, request):
diff --git a/setup.py b/setup.py
index 6504d4761..9a08ca77b 100644
--- a/setup.py
+++ b/setup.py
@@ -71,7 +71,7 @@ def read_file(filename):
setup(name='kinto',
- version='3.2.0',
+ version='3.2.1',
description='Kinto Web Service - Store, Sync, Share, and Self-Host.',
long_description=README + "\n\n" + CHANGELOG + "\n\n" + CONTRIBUTORS,
license='Apache License (2.0)',