Skip to content

Commit

Permalink
Add ._pasteObjects() to be able to paste objects without limits.
Browse files Browse the repository at this point in the history
Fixes #217.
  • Loading branch information
Michael Howitz committed Apr 18, 2018
1 parent 10de0a9 commit 8bc27c2
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ https://github.com/zopefoundation/Zope/blob/4.0a6/CHANGES.rst
- Drop support for Python 3.4 because it was dropped by `AccessControl` on
which `Zope` depends.

- Add ``OFS.CopySupport.CopyContainer._pasteObjects()`` to be able to paste
objects no matter how many objects where cut or copied.
(`#217 <https://github.com/zopefoundation/Zope/issues/217>`_)

- Fix an edge case where the data which was set using ``response.write()`` was
not returned by ``publish_module`` (#256).

Expand Down
53 changes: 41 additions & 12 deletions src/OFS/CopySupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,24 +163,30 @@ def _get_id(self, id):
n = n + 1

security.declareProtected(view_management_screens, 'manage_pasteObjects')
def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
def _pasteObjects(self, cp, cb_maxsize=0):
"""Paste previously copied objects into the current object.
If calling manage_pasteObjects from python code, pass the result of a
``cp`` is the list of objects for paste as encoded by ``_cb_encode``.
If calling _pasteObjects from python code, pass the result of a
previous call to manage_cutObjects or manage_copyObjects as the first
argument.
Also sends IObjectCopiedEvent and IObjectClonedEvent
``cb_maxsize`` is the maximum size of the JSON representation of the
object list. Set it to a non-zero value to prevent DoS attacks with
huge object lists or zlib bombs.
This method sends IObjectCopiedEvent and IObjectClonedEvent
or IObjectWillBeMovedEvent and IObjectMovedEvent.
Returns tuple of (operator, list of {'id': orig_id, 'new_id': new_id}).
Where `operator` is 0 for a copy operation and 1 for a move operation.
"""
cp = cb_copy_data
if cp is None and REQUEST is not None and '__cp' in REQUEST:
cp = REQUEST['__cp']
if cp is None:
raise CopyError('No clipboard data found.')

try:
op, mdatas = _cb_decode(cp)
op, mdatas = _cb_decode(cp, cb_maxsize)
except Exception as e:
six.raise_from(CopyError('Clipboard Error'), e)

Expand Down Expand Up @@ -230,9 +236,6 @@ def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):

notify(ObjectClonedEvent(ob))

if REQUEST is not None:
return self.manage_main(self, REQUEST, cb_dataValid=1)

elif op == 1:
# Move operation
for ob in oblist:
Expand Down Expand Up @@ -293,13 +296,38 @@ def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
# try to make ownership implicit if possible
ob.manage_changeOwnershipType(explicit=0)

if REQUEST is not None:
return op, result

security.declareProtected(view_management_screens, 'manage_pasteObjects')
def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
"""Paste previously copied objects into the current object.
If calling manage_pasteObjects from python code, pass the result of a
previous call to manage_cutObjects or manage_copyObjects as the first
argument.
Also sends IObjectCopiedEvent and IObjectClonedEvent
or IObjectWillBeMovedEvent and IObjectMovedEvent.
If `REQUEST` is None it returns a
list of dicts {'id': orig_id, 'new_id': new_id} otherwise it renders
a HTML page.
"""
if cb_copy_data is None and REQUEST is not None and '__cp' in REQUEST:
cb_copy_data = REQUEST['__cp']
op, result = self._pasteObjects(cb_copy_data, cb_maxsize=8192)

if REQUEST is not None:
if op == 0:
cb_valid = 1
elif op == 1:
REQUEST['RESPONSE'].setCookie(
'__cp', 'deleted',
path='%s' % cookie_path(REQUEST),
expires='Wed, 31-Dec-97 23:59:59 GMT')
REQUEST['__cp'] = None
return self.manage_main(self, REQUEST, cb_dataValid=0)
cb_valid = 0
return self.manage_main(self, REQUEST, cb_dataValid=cb_valid)

return result

Expand Down Expand Up @@ -649,6 +677,7 @@ def _cb_decode(s, maxsize=8192):
"""Decode a list of IDs from storage in a cookie.
``s`` is text as encoded by ``_cb_encode``.
``maxsize`` is the maximum size of uncompressed data. ``0`` means no limit.
Return a list of text IDs.
"""
Expand Down
7 changes: 6 additions & 1 deletion src/OFS/tests/testCopySupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,16 @@ def make_data(lenght):
with self.assertRaises(CopyError) as err:
self.folder1.manage_pasteObjects(make_data(300))
self.assertEqual('Clipboard Error', str(err.exception))
# But not too much data is allowed
# But not too much data is allowed:
with self.assertRaises(CopyError) as err:
self.folder1.manage_pasteObjects(make_data(250))
self.assertEqual('Item Not Found', str(err.exception))

# _pasteObjects allows to paste without restriction:
with self.assertRaises(CopyError) as err:
self.folder1._pasteObjects(make_data(3000))
self.assertEqual('Item Not Found', str(err.exception))


class _SensitiveSecurityPolicy(object):

Expand Down

0 comments on commit 8bc27c2

Please sign in to comment.