Repository: Kinto/kinto

Issue:
kinto-attachment fails with history plugin
kinto-attachment crashes with history enabled

```
http --check-status --form POST http://localhost:8888/v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment attachment@kinto-logo.png --auth user:pass

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" ? (? ms) u'/buckets/source/collections/source' agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=110 exception=Traceback (most recent call last):

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/tweens.py", line 22, in excview_tween

    response = handler(request)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 109, in tm_tween

    reraise(*exc_info)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 95, in tm_tween

    manager.commit()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_manager.py", line 123, in commit

    return self.get().commit()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 265, in commit

    self._callBeforeCommitHooks()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 336, in _callBeforeCommitHooks

    hook(*args, **kws)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/core/events.py", line 76, in _notify_resource_events_before

    request.registry.notify(event)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/registry.py", line 91, in notify

    [ _ for _ in self.subscribers(events, None) ]

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/registry.py", line 442, in subscribers

    return self.adapters.subscribers(objects, provided)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/adapter.py", line 596, in subscribers

    subscription(*objects)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 130, in subscriber_wrapper

    return derived_subscriber(*arg)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 103, in derived_subscriber

    return subscriber(arg[0])

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/plugins/history/listener.py", line 65, in on_resource_changed

    collection_perms = perms_by_object_id[collection_uri]

KeyError: u'/buckets/source/collections/source' lang=None uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" 500 (23 ms) request.summary agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=999 lang=None time=2016-09-30T10:45:31 uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

HTTP/1.1 500 Internal Server Error

Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff

Content-Length: 183

Content-Type: application/json; charset=UTF-8

Date: Fri, 30 Sep 2016 10:45:31 GMT

Server: waitress

{

    "code": 500, 

    "errno": 999, 

    "error": "Internal Server Error", 

    "info": "https://github.com/Kinto/kinto/issues/", 

    "message": "A programmatic error occured, developers have been informed."

}

```

Probably linked to #805 

kinto-attachment fails with history plugin
kinto-attachment crashes with history enabled

```
http --check-status --form POST http://localhost:8888/v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment attachment@kinto-logo.png --auth user:pass

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" ? (? ms) u'/buckets/source/collections/source' agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=110 exception=Traceback (most recent call last):

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/tweens.py", line 22, in excview_tween

    response = handler(request)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 109, in tm_tween

    reraise(*exc_info)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 95, in tm_tween

    manager.commit()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_manager.py", line 123, in commit

    return self.get().commit()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 265, in commit

    self._callBeforeCommitHooks()

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 336, in _callBeforeCommitHooks

    hook(*args, **kws)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/core/events.py", line 76, in _notify_resource_events_before

    request.registry.notify(event)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/registry.py", line 91, in notify

    [ _ for _ in self.subscribers(events, None) ]

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/registry.py", line 442, in subscribers

    return self.adapters.subscribers(objects, provided)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/adapter.py", line 596, in subscribers

    subscription(*objects)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 130, in subscriber_wrapper

    return derived_subscriber(*arg)

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 103, in derived_subscriber

    return subscriber(arg[0])

  File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/plugins/history/listener.py", line 65, in on_resource_changed

    collection_perms = perms_by_object_id[collection_uri]

KeyError: u'/buckets/source/collections/source' lang=None uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" 500 (23 ms) request.summary agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=999 lang=None time=2016-09-30T10:45:31 uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

HTTP/1.1 500 Internal Server Error

Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff

Content-Length: 183

Content-Type: application/json; charset=UTF-8

Date: Fri, 30 Sep 2016 10:45:31 GMT

Server: waitress

{

    "code": 500, 

    "errno": 999, 

    "error": "Internal Server Error", 

    "info": "https://github.com/Kinto/kinto/issues/", 

    "message": "A programmatic error occured, developers have been informed."

}

```

Probably linked to #805 



Discussion:
Cannot reproduce with memory backend locally.

Cannot reproduce with postgresql backend locally either :/ Running kinto-dist config with every plugins enabled :(

https://travis-ci.org/mozilla-services/kinto-dist/jobs/163976422

Cannot reproduce with memory backend locally.

Cannot reproduce with postgresql backend locally either :/ Running kinto-dist config with every plugins enabled :(

https://travis-ci.org/mozilla-services/kinto-dist/jobs/163976422


Resolving patch:
diff --git a/kinto/core/permission/__init__.py b/kinto/core/permission/__init__.py
--- a/kinto/core/permission/__init__.py
+++ b/kinto/core/permission/__init__.py
@@ -133,8 +133,7 @@ def check_permission(self, principals, bound_permissions):
         return len(authorized & principals) > 0
 
     def get_object_permissions(self, object_id, permissions=None):
-        perms = self.get_objects_permissions([object_id], permissions)
-        return perms[0] if perms else {}
+        return self.get_objects_permissions([object_id], permissions)[0]
 
     def get_objects_permissions(self, objects_ids, permissions=None):
         """Return a list of mapping, for each object id specified, with the
diff --git a/kinto/core/permission/postgresql/__init__.py b/kinto/core/permission/postgresql/__init__.py
--- a/kinto/core/permission/postgresql/__init__.py
+++ b/kinto/core/permission/postgresql/__init__.py
@@ -300,12 +300,13 @@ def get_objects_permissions(self, objects_ids, permissions=None):
             rows = result.fetchall()
 
         groupby_id = OrderedDict()
+        for object_id in objects_ids:
+            groupby_id[object_id] = {}
         for row in rows:
             object_id, permission, principal = (row['object_id'],
                                                 row['permission'],
                                                 row['principal'])
-            permissions = groupby_id.setdefault(object_id, {})
-            permissions.setdefault(permission, set()).add(principal)
+            groupby_id[object_id].setdefault(permission, set()).add(principal)
         return list(groupby_id.values())
 
     def replace_object_permissions(self, object_id, permissions):


Test patch:
diff --git a/kinto/core/permission/testing.py b/kinto/core/permission/testing.py
--- a/kinto/core/permission/testing.py
+++ b/kinto/core/permission/testing.py
@@ -357,6 +357,14 @@ def test_object_permissions_return_listed_object_acls(self):
             "read": {"user3"}
         })
 
+    def test_objects_permissions_returns_empty_if_unknown(self):
+        self.permission.add_principal_to_ace('/url/a/id/1', 'write', 'user1')
+        self.permission.add_principal_to_ace('/url/a/id/3', 'read', 'user3')
+        objects_permissions = self.permission.get_objects_permissions([
+            '/url/a/id/1', '/abc', '/url/a/id/3'])
+        self.assertEqual(objects_permissions, [
+            {"write": {"user1"}}, {}, {"read": {"user3"}}])
+
     def test_object_permissions_return_empty_dict(self):
         self.assertDictEqual(self.permission.get_object_permissions('abc'), {})
 
diff --git a/tests/plugins/test_history.py b/tests/plugins/test_history.py
--- a/tests/plugins/test_history.py
+++ b/tests/plugins/test_history.py
@@ -304,6 +304,17 @@ def test_tracks_multiple_records_delete(self):
         assert entry['action'] == 'delete'
         assert entry['target']['data']['id'] in (self.record['id'], rid)
 
+    def test_does_not_track_records_during_massive_deletion(self):
+        body = {'data': {'pim': 'pam'}}
+        records_uri = self.collection_uri + '/records'
+        self.app.post_json(records_uri, body, headers=self.headers)
+
+        self.app.delete(self.collection_uri, headers=self.headers)
+
+        resp = self.app.get(self.history_uri, headers=self.headers)
+        deletion_entries = [e for e in resp.json['data'] if e['action'] == 'delete']
+        assert len(deletion_entries) == 1
+
 
 class FilteringTest(HistoryWebTest):