plone.restapi/unify-expired-and-excluded-items-handling: Require lock token for updating a locked object
Thomas Buchberger <jenkins-z4DKO/[email protected]> Sat, 29 Jul 2017 16:32:14 -0700 (PDT)
| Newsgroups | gmane.comp.web.zope.plone.cvs |
|---|---|
| Message-ID | <[email protected]> |
Repository: plone.restapi Branch: refs/heads/unify-expired-and-excluded-items-handling Date: 2017-07-27T18:20:15+02:00 Author: Thomas Buchberger (buchi) <t.buchberger-/8aE7x569Db/[email protected]> Commit: https://github.com/plone/plone.restapi/commit/894e5ad749dd7cca3faf8f007c16171ecdf0f66f Require lock token for updating a locked object Files changed: M src/plone/restapi/services/content/update.py M src/plone/restapi/services/locking/locking.py M src/plone/restapi/tests/test_locking.py diff --git a/src/plone/restapi/services/content/update.py b/src/plone/restapi/services/content/update.py index d394bd4c..19d2eb31 100644 --- a/src/plone/restapi/services/content/update.py +++ b/src/plone/restapi/services/content/update.py @@ -2,6 +2,7 @@ from plone.restapi.exceptions import DeserializationError from plone.restapi.interfaces import IDeserializeFromJson from plone.restapi.services import Service +from plone.restapi.services.locking.locking import is_locked from zope.component import queryMultiAdapter @@ -10,6 +11,12 @@ class ContentPatch(Service): """ def reply(self): + + if is_locked(self.context, self.request): + self.request.response.setStatus(403) + return dict(error=dict( + type='Forbidden', message='Resource is locked.')) + deserializer = queryMultiAdapter((self.context, self.request), IDeserializeFromJson) if deserializer is None: diff --git a/src/plone/restapi/services/locking/locking.py b/src/plone/restapi/services/locking/locking.py index 1417d45f..9037879e 100644 --- a/src/plone/restapi/services/locking/locking.py +++ b/src/plone/restapi/services/locking/locking.py @@ -79,7 +79,7 @@ def reply(self): def lock_info(obj): """Returns lock information about the given object.""" - lockable = ILockable(obj) + lockable = ILockable(obj, None) if lockable is not None: info = { 'locked': lockable.locked(), @@ -101,7 +101,7 @@ def lock_info(obj): def webdav_lock(obj): """Returns the WebDAV LockItem""" - lockable = ILockable(obj) + lockable = ILockable(obj, None) if lockable is None: return @@ -109,3 +109,19 @@ def webdav_lock(obj): if len(lock_info) > 0: token = lock_info[0]['token'] return obj.wl_getLock(token) + + +def is_locked(obj, request): + """Returns true if the object is locked and the request doesn't contain + the lock token. + """ + lockable = ILockable(obj, None) + if lockable is None: + return False + if lockable.locked(): + token = request.getHeader('Lock-Token', '') + lock_info = lockable.lock_info() + if len(lock_info) > 0 and lock_info[0]['token'] == token: + return False + return True + return False diff --git a/src/plone/restapi/tests/test_locking.py b/src/plone/restapi/tests/test_locking.py index b2d9cd14..0e7b49fb 100644 --- a/src/plone/restapi/tests/test_locking.py +++ b/src/plone/restapi/tests/test_locking.py @@ -89,3 +89,24 @@ def test_lock_info_for_unlocked_object(self): self.assertEqual(response.status_code, 200) self.assertFalse(response.json()['locked']) + + def test_update_locked_object_without_token_fails(self): + lockable = ILockable(self.doc) + lockable.lock() + transaction.commit() + response = self.api_session.patch('/', json={'title': 'New Title'}) + transaction.commit() + self.assertEqual(response.status_code, 403) + self.assertEqual(self.doc.Title(), 'My Document') + + def test_update_locked_object_with_token_succeeds(self): + lockable = ILockable(self.doc) + lockable.lock() + transaction.commit() + response = self.api_session.patch( + '/', + headers={'Lock-Token': lockable.lock_info()[0]['token']}, + json={'title': 'New Title'}) + transaction.commit() + self.assertEqual(response.status_code, 204) + self.assertEqual(self.doc.Title(), 'New Title') ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot