[CMF-checkins] CVS: Products/CMFCore/tests -
test_PortalFolder.py:1.21.4.9
Tres Seaver
tseaver at zope.com
Mon Aug 9 12:33:14 EDT 2004
Update of /cvs-repository/Products/CMFCore/tests
In directory cvs.zope.org:/tmp/cvs-serv29799/CMFCore/tests
Modified Files:
Tag: CMF-1_4-branch
test_PortalFolder.py
Log Message:
- CMFCore.PortalFolder: Enforce check of "Delete objects" permission
during cut + paste. (http://zope.org/Collectors/259)
N.B. This fix depends on an update to the underlying Zope software,
e.g., Zope 2.7.3 or later. Two new unit tests fail on
Zope 2.7.2 and earlier.
=== Products/CMFCore/tests/test_PortalFolder.py 1.21.4.8 => 1.21.4.9 ===
--- Products/CMFCore/tests/test_PortalFolder.py:1.21.4.8 Wed Jul 28 13:05:37 2004
+++ Products/CMFCore/tests/test_PortalFolder.py Mon Aug 9 12:33:14 2004
@@ -7,21 +7,29 @@
# for Zope versions before 2.6.1
pass
+import cStringIO
+
+from AccessControl import SecurityManager
+from Acquisition import Implicit
+from Acquisition import aq_base
from DateTime import DateTime
+from OFS.Application import Application
+from OFS.Image import manage_addFile
+from OFS.tests.testCopySupport import makeConnection
+from Testing.makerequest import makerequest
from Products.CMFCore.tests.base.dummy import DummyContent
from Products.CMFCore.tests.base.dummy import DummyFactory
from Products.CMFCore.tests.base.dummy import DummyFTI
from Products.CMFCore.tests.base.testcase import SecurityTest
from Products.CMFCore.tests.base.testcase import newSecurityManager
+from Products.CMFCore.tests.base.testcase import noSecurityManager
from Products.CMFCore.tests.base.utils import has_path
from Products.CMFCore.tests.base.security import OmnipotentUser
from Products.CMFCore.TypesTool import TypesTool
from Products.CMFCore.TypesTool import FactoryTypeInformation as FTI
from Products.CMFCore.CatalogTool import CatalogTool
-from Products.CMFCore.PortalFolder import PortalFolder
-from Products.CMFCore.PortalFolder import ContentFilter
from Products.CMFCore.interfaces.Dynamic import DynamicType as IDynamicType
from webdav.WriteLockInterface import WriteLockInterface
@@ -37,6 +45,7 @@
class PortalFolderFactoryTests( SecurityTest ):
def setUp( self ):
+ from Products.CMFCore.PortalFolder import PortalFolder
SecurityTest.setUp( self )
self.root._setObject( 'portal_types', TypesTool() )
@@ -53,6 +62,7 @@
types_tool._setObject( 'Dummy Content', DummyFTI )
def _makeOne( self, id ):
+ from Products.CMFCore.PortalFolder import PortalFolder
return PortalFolder( id ).__of__( self.root )
def test_invokeFactory( self ):
@@ -92,6 +102,7 @@
class PortalFolderTests( SecurityTest ):
def setUp( self ):
+ from Products.CMFCore.PortalFolder import PortalFolder
SecurityTest.setUp(self)
root = self.root
@@ -166,6 +177,8 @@
# is not being uncatalogued. Try creating a subfolder with
# content object, and test.
#
+ from Products.CMFCore.PortalFolder import PortalFolder
+
test = self.root.test
self.root._setObject( 'portal_types', TypesTool() )
@@ -199,6 +212,8 @@
#
# Does the catalog stay synched when folders are moved?
#
+ from Products.CMFCore.PortalFolder import PortalFolder
+
test = self.root.test
self.root._setObject( 'portal_types', TypesTool() )
@@ -260,6 +275,8 @@
#
# Does MKDIR/MKCOL intercept work?
#
+ from Products.CMFCore.PortalFolder import PortalFolder
+
test = self.root.test
test._setPortalTypeName( 'Folder' )
self.root.reindexObject = lambda: 0
@@ -324,6 +341,8 @@
#
# Does copy / paste work?
#
+ from Products.CMFCore.PortalFolder import PortalFolder
+
test = self.root.test
self.root._setObject( 'portal_types', TypesTool() )
@@ -397,6 +416,8 @@
#
# Does _verifyObjectPaste check allowed content types?
#
+ from Products.CMFCore.PortalFolder import PortalFolder
+
test = self.root.test
self.root._setObject( 'portal_types', TypesTool() )
@@ -456,6 +477,9 @@
self.dummy=DummyContent('Dummy')
def test_empty( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter()
dummy = self.dummy
assert cfilter( dummy )
@@ -464,6 +488,9 @@
assert not lines
def test_Type( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Type='foo' )
dummy = self.dummy
assert not cfilter( dummy )
@@ -486,6 +513,9 @@
assert lines[0] == 'Type: Dummy Content Title, something else'
def test_portal_type( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( portal_type='some_pt' )
dummy = self.dummy
assert not cfilter( dummy )
@@ -503,6 +533,9 @@
assert lines[0] == 'Portal Type: some_pt'
def test_Title( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Title='foo' )
dummy = self.dummy
assert not cfilter( dummy )
@@ -518,6 +551,9 @@
assert lines[0] == 'Title: foo'
def test_Creator( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Creator='moe' )
dummy = self.dummy
assert not cfilter( dummy )
@@ -533,6 +569,9 @@
self.assertEqual(lines[0],'Creator: moe')
def test_Description( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Description='funny' )
dummy = self.dummy
assert not cfilter( dummy )
@@ -548,6 +587,9 @@
assert lines[0] == 'Description: funny'
def test_Subject( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Subject=('foo',) )
dummy = self.dummy
assert not cfilter( dummy )
@@ -564,6 +606,9 @@
def test_Subject2( self ):
# Now test with mutli-valued
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( Subject=('foo', 'bar' ) )
dummy = self.dummy
assert not cfilter( dummy )
@@ -581,6 +626,9 @@
assert lines[0] == 'Subject: foo, bar'
def test_created( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( created=DateTime( '2001/01/01' )
, created_usage='range:min' )
dummy = self.dummy
@@ -598,6 +646,8 @@
def test_created2( self ):
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( created=DateTime( '2001/01/01' )
, created_usage='range:max' )
@@ -615,6 +665,9 @@
assert lines[0] == 'Created before: 2001/01/01'
def test_modified( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( modified=DateTime( '2001/01/01' )
, modified_usage='range:min' )
dummy = self.dummy
@@ -631,6 +684,9 @@
assert lines[0] == 'Modified since: 2001/01/01'
def test_modified2( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( modified=DateTime( '2001/01/01' )
, modified_usage='range:max' )
dummy = self.dummy
@@ -647,6 +703,9 @@
assert lines[0] == 'Modified before: 2001/01/01'
def test_mixed( self ):
+
+ from Products.CMFCore.PortalFolder import ContentFilter
+
cfilter = ContentFilter( created=DateTime( '2001/01/01' )
, created_usage='range:max'
, Title='foo'
@@ -677,11 +736,344 @@
assert 'Created before: 2001/01/01' in lines
assert 'Title: foo' in lines
+#------------------------------------------------------------------------------
+# Tests for security-related CopySupport lifted from the Zope 2.7
+# / head OFS.tests.testCopySupport (see Collector #259).
+#------------------------------------------------------------------------------
+ADD_IMAGES_AND_FILES = 'Add images and files'
+FILE_META_TYPES = ( { 'name' : 'File'
+ , 'action' : 'manage_addFile'
+ , 'permission' : ADD_IMAGES_AND_FILES
+ }
+ ,
+ )
+class _SensitiveSecurityPolicy:
+
+ def __init__( self, validate_lambda, checkPermission_lambda ):
+ self._lambdas = ( validate_lambda, checkPermission_lambda )
+
+ def validate( self, *args, **kw ):
+ return self._lambdas[ 0 ]( *args, **kw )
+
+ def checkPermission( self, *args, **kw ) :
+ return self._lambdas[ 1 ]( *args, **kw )
+
+class _AllowedUser( Implicit ):
+
+ def __init__( self, allowed_lambda ):
+ self._lambdas = ( allowed_lambda, )
+
+ def getId( self ):
+ return 'unit_tester'
+
+ getUserName = getId
+
+ def allowed( self, object, object_roles=None ):
+ return self._lambdas[ 0 ]( object, object_roles )
+
+class PortalFolderCopySupportTests( TestCase ):
+
+ _old_policy = None
+
+ def setUp( self ):
+ self._scrubSecurity()
+
+ def tearDown( self ):
+
+ self._scrubSecurity()
+ self._cleanApp()
+
+ def _initFolders( self ):
+ from Products.CMFCore.PortalFolder import PortalFolder
+
+ self.connection = makeConnection()
+ try:
+ r = self.connection.root()
+ a = Application()
+ r['Application'] = a
+ self.root = a
+ responseOut = self.responseOut = cStringIO.StringIO()
+ self.app = makerequest( self.root, stdout=responseOut )
+ self.app._setObject( 'folder1', PortalFolder( 'folder1' ) )
+ self.app._setObject( 'folder2', PortalFolder( 'folder2' ) )
+ folder1 = getattr( self.app, 'folder1' )
+ folder2 = getattr( self.app, 'folder2' )
+
+ manage_addFile( folder1, 'file'
+ , file='', content_type='text/plain')
+
+ # Hack, we need a _p_mtime for the file, so we make sure that it
+ # has one. We use a subtransaction, which means we can rollback
+ # later and pretend we didn't touch the ZODB.
+ get_transaction().commit()
+ except:
+ self.connection.close()
+ raise
+ get_transaction().begin()
+
+ return self.app._getOb( 'folder1' ), self.app._getOb( 'folder2' )
+
+ def _cleanApp( self ):
+
+ get_transaction().abort()
+ self.app._p_jar.sync()
+ self.connection.close()
+ del self.app
+ del self.responseOut
+ del self.root
+ del self.connection
+
+ def _scrubSecurity( self ):
+
+ noSecurityManager()
+
+ if self._old_policy is not None:
+ SecurityManager.setSecurityPolicy( self._old_policy )
+
+ def _assertCopyErrorUnauth( self, callable, *args, **kw ):
+
+ import re
+ from zExceptions import Unauthorized
+ from OFS.CopySupport import CopyError
+
+ ce_regex = kw.get( 'ce_regex' )
+ if ce_regex is not None:
+ del kw[ 'ce_regex' ]
+
+ try:
+ callable( *args, **kw )
+
+ except CopyError, e:
+
+ if ce_regex is not None:
+
+ pattern = re.compile( ce_regex, re.DOTALL )
+ if pattern.search( e ) is None:
+ self.fail( "Paste failed; didn't match pattern:\n%s" % e )
+
+ else:
+ self.fail( "Paste failed; no pattern:\n%s" % e )
+
+ except Unauthorized, e:
+ pass
+
+ else:
+ self.fail( "Paste allowed unexpectedly." )
+
+ def _initPolicyAndUser( self
+ , a_lambda=None
+ , v_lambda=None
+ , c_lambda=None
+ ):
+ def _promiscuous( *args, **kw ):
+ return 1
+
+ if a_lambda is None:
+ a_lambda = _promiscuous
+
+ if v_lambda is None:
+ v_lambda = _promiscuous
+
+ if c_lambda is None:
+ c_lambda = _promiscuous
+
+ scp = _SensitiveSecurityPolicy( v_lambda, c_lambda )
+ self._old_policy = SecurityManager.setSecurityPolicy( scp )
+
+ newSecurityManager( None
+ , _AllowedUser( a_lambda ).__of__( self.root ) )
+
+ def test_copy_baseline( self ):
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = FILE_META_TYPES
+
+ self._initPolicyAndUser()
+
+ self.failUnless( 'file' in folder1.objectIds() )
+ self.failIf( 'file' in folder2.objectIds() )
+
+ cookie = folder1.manage_copyObjects( ids=( 'file', ) )
+ folder2.manage_pasteObjects( cookie )
+
+ self.failUnless( 'file' in folder1.objectIds() )
+ self.failUnless( 'file' in folder2.objectIds() )
+
+ def test_copy_cant_read_source( self ):
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = FILE_META_TYPES
+
+ a_file = folder1._getOb( 'file' )
+
+ def _validate( a, c, n, v, *args, **kw ):
+ return aq_base( v ) is not aq_base( a_file )
+
+ self._initPolicyAndUser( v_lambda=_validate )
+
+ cookie = folder1.manage_copyObjects( ids=( 'file', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Insufficient privileges'
+ )
+
+ def test_copy_cant_create_target_metatype_not_supported( self ):
+
+ from OFS.CopySupport import CopyError
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = ()
+
+ self._initPolicyAndUser()
+
+ cookie = folder1.manage_copyObjects( ids=( 'file', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Not Supported'
+ )
+
+ def test_move_baseline( self ):
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = FILE_META_TYPES
+
+ self.failUnless( 'file' in folder1.objectIds() )
+ self.failIf( 'file' in folder2.objectIds() )
+
+ self._initPolicyAndUser()
+
+ cookie = folder1.manage_cutObjects( ids=( 'file', ) )
+ folder2.manage_pasteObjects( cookie )
+
+ self.failIf( 'file' in folder1.objectIds() )
+ self.failUnless( 'file' in folder2.objectIds() )
+
+ def test_move_cant_read_source( self ):
+
+ from OFS.CopySupport import CopyError
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = FILE_META_TYPES
+
+ a_file = folder1._getOb( 'file' )
+
+ def _validate( a, c, n, v, *args, **kw ):
+ return aq_base( v ) is not aq_base( a_file )
+
+ self._initPolicyAndUser( v_lambda=_validate )
+
+ cookie = folder1.manage_cutObjects( ids=( 'file', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Insufficient privileges'
+ )
+
+ def test_move_cant_create_target_metatype_not_supported( self ):
+
+ from OFS.CopySupport import CopyError
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = ()
+
+ self._initPolicyAndUser()
+
+ cookie = folder1.manage_cutObjects( ids=( 'file', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Not Supported'
+ )
+
+ def test_move_cant_create_target_metatype_not_allowed( self ):
+
+ #
+ # This test can't succeed on Zope's earlier than 2.7.3 because
+ # of the DWIM'y behavior of 'guarded_getattr', which tries to
+ # filter # acquired-but-inaccessible objects, rather than raising
+ # Unauthorized.
+ #
+ # If you are running with such a Zope, this test will error out
+ # with an AttributeError (instead of the expected Unauthorized).
+ #
+ from OFS.CopySupport import CopyError
+
+ folder1, folder2 = self._initFolders()
+ folder2.all_meta_types = FILE_META_TYPES
+
+ def _no_manage_addFile( a, c, n, v, *args, **kw ):
+ return n != 'manage_addFile'
+
+ self._initPolicyAndUser( v_lambda=_no_manage_addFile )
+
+ cookie = folder1.manage_cutObjects( ids=( 'file', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Insufficient Privileges'
+ + '.*%s' % ADD_IMAGES_AND_FILES
+ )
+
+ def test_move_cant_delete_source( self ):
+
+ #
+ # This test fails on Zope's earlier than 2.7.3 because of the
+ # changes required to 'OFS.CopytSupport.manage_pasteObjects'
+ # which must pass 'validate_src' of 2 to '_verifyObjectPaste'
+ # to indicate that the object is being moved, rather than
+ # simply copied.
+ #
+ # If you are running with such a Zope, this test will fail,
+ # because the move (which should raise Unauthorized) will be
+ # allowed.
+ #
+ from AccessControl.Permissions import delete_objects as DeleteObjects
+ from OFS.CopySupport import CopyError
+ from Products.CMFCore.PortalFolder import PortalFolder
+ from Products.CMFCore.CMFCorePermissions import AddPortalFolders
+
+ folder1, folder2 = self._initFolders()
+ folder1.manage_permission( DeleteObjects, roles=(), acquire=0 )
+
+ folder1._setObject( 'sub', PortalFolder( 'sub' ) )
+ get_transaction().commit() # get a _p_jar for 'sub'
+
+ FOLDER_CTOR = 'manage_addProducts/CMFCore/manage_addPortalFolder'
+ folder2.all_meta_types = ( { 'name' : 'CMF Core Content'
+ , 'action' : FOLDER_CTOR
+ , 'permission' : AddPortalFolders
+ }
+ ,
+ )
+
+ self.app.portal_types = DummyTypesTool()
+
+ def _no_delete_objects(permission, object, context):
+ return permission != DeleteObjects
+
+ self._initPolicyAndUser( c_lambda=_no_delete_objects )
+
+ cookie = folder1.manage_cutObjects( ids=( 'sub', ) )
+ self._assertCopyErrorUnauth( folder2.manage_pasteObjects
+ , cookie
+ , ce_regex='Insufficient Privileges'
+ + '.*%s' % DeleteObjects
+ )
+
+class DummyTypeInfo:
+
+ def allowType( self, portal_type ):
+ return True
+
+class DummyTypesTool( Implicit ):
+
+ def getTypeInfo( self, portal_type ):
+
+ return DummyTypeInfo()
+
def test_suite():
return TestSuite((
makeSuite( PortalFolderFactoryTests ),
makeSuite( PortalFolderTests ),
makeSuite( ContentFilterTests ),
+ makeSuite( PortalFolderCopySupportTests ),
))
if __name__ == '__main__':
More information about the CMF-checkins
mailing list