FYI they're below. I'm porting them to trunk right now.
Florent
Index: doc/CHANGES.txt =================================================================== RCS file: /cvs-repository/Zope/doc/Attic/CHANGES.txt,v retrieving revision 1.625.2.309 diff -u -r1.625.2.309 CHANGES.txt --- doc/CHANGES.txt 18 Mar 2005 15:31:01 -0000 1.625.2.309 +++ doc/CHANGES.txt 24 Mar 2005 18:32:34 -0000 @@ -11,6 +11,10 @@ - DateIndex/DateRangeIndexes did not reset their __len__ attribute properly when cleared
+ - Fixed brain.getObject() to correctly traverse to an object even + if one of its parents is not accessible, to be close to what the + Publisher does. + Zope 2.7.5 final (2005/03/20)
Bugs fixed
Index: lib/python/Products/ZCatalog/CatalogBrains.py
===================================================================
RCS file: /cvs-repository/Products/ZCatalog/Attic/CatalogBrains.py,v
retrieving revision 1.8.44.1
diff -u -r1.8.44.1 CatalogBrains.py
--- lib/python/Products/ZCatalog/CatalogBrains.py 23 Mar 2004 20:27:23 -0000 1.8.44.1
+++ lib/python/Products/ZCatalog/CatalogBrains.py 24 Mar 2005 18:32:35 -0000
@@ -42,12 +42,24 @@
def getObject(self, REQUEST=None):
"""Return the object for this record
-
+
Will return None if the object cannot be found via its cataloged path
(i.e., it was deleted or moved without recataloging), or if the user is
- not authorized to access an object along the path.
+ not authorized to access the object.
+
+ This method mimicks a subset of what publisher's traversal does,
+ so it allows access if the final object can be accessed even
+ if intermediate objects cannot.
"""
- return self.aq_parent.restrictedTraverse(self.getPath(), None)
+ path = self.getPath().split('/')
+ if not path:
+ return None
+ parent = self.aq_parent
+ if len(path) > 1:
+ parent = parent.unrestrictedTraverse('/'.join(path[:-1]), None)
+ if parent is None:
+ return None
+ return parent.restrictedTraverse(path[-1], None)
def getRID(self):
"""Return the record ID for this object."""
Index: lib/python/Products/ZCatalog/tests/testBrains.py
===================================================================
RCS file: /cvs-repository/Products/ZCatalog/tests/Attic/testBrains.py,v
retrieving revision 1.1.4.1
diff -u -r1.1.4.1 testBrains.py
--- lib/python/Products/ZCatalog/tests/testBrains.py 23 Mar 2004 20:27:23 -0000 1.1.4.1
+++ lib/python/Products/ZCatalog/tests/testBrains.py 24 Mar 2005 18:32:35 -0000
@@ -23,15 +23,17 @@
"""Happy content"""
def __init__(self, id):
self.id = id
+ def check(self):
+ pass
class Secret(Happy): """Object that raises Unauthorized when accessed""" - def __of__(self, parent): + def check(self): raise Unauthorized
class Conflicter(Happy): """Object that raises ConflictError when accessed""" - def __of__(self, parent): + def check(self): raise ConflictError
class DummyRequest(Acquisition.Implicit):
@@ -50,10 +52,20 @@
'/conflicter':Conflicter('conflicter')}
_paths = _objs.keys() + ['/zonked']
_paths.sort()
-
+
+ # This is sooooo ugly
+
+ def unrestrictedTraverse(self, path, default=None):
+ assert path == '' # for these tests...
+ return self
+
def restrictedTraverse(self, path, default=_marker):
+ if not path.startswith('/'):
+ path = '/'+path
try:
- return self._objs[path].__of__(self)
+ ob = self._objs[path].__of__(self)
+ ob.check()
+ return ob
except (KeyError, Unauthorized):
if default is not _marker:
return default
Index: lib/python/Products/ZCatalog/tests/testCatalog.py
===================================================================
RCS file: /cvs-repository/Products/ZCatalog/tests/Attic/testCatalog.py,v
retrieving revision 1.22.12.6
diff -u -r1.22.12.6 testCatalog.py
--- lib/python/Products/ZCatalog/tests/testCatalog.py 18 May 2004 14:48:49 -0000 1.22.12.6
+++ lib/python/Products/ZCatalog/tests/testCatalog.py 24 Mar 2005 18:32:35 -0000
@@ -26,6 +26,10 @@
from Products.ZCatalog import ZCatalog,Vocabulary
from Products.ZCatalog.Catalog import Catalog, CatalogError
import ExtensionClass
+from OFS.Folder import Folder as OFS_Folder
+from AccessControl.SecurityManagement import setSecurityManager
+from AccessControl.SecurityManagement import noSecurityManager
+from AccessControl import Unauthorized
from Products.PluginIndexes.FieldIndex.FieldIndex import FieldIndex from Products.PluginIndexes.TextIndex.TextIndex import TextIndex @@ -60,18 +64,6 @@ # XXX What's this mean? What does this comment apply to?
################################################################################
-# XXX These imports and class don't appear to be needed? -## from AccessControl.SecurityManagement import newSecurityManager -## from AccessControl.SecurityManagement import noSecurityManager - -## class DummyUser: - -## def __init__( self, name ): -## self._name = name - -## def getUserName( self ): -## return self._name - def sort(iterable): L = list(iterable) L.sort() @@ -584,7 +576,82 @@ expected.sort() expected = [rid for sortkey, rid, getitem in expected] self.assertEqual(merged_rids, expected) - + + +class PickySecurityManager: + def __init__(self, badnames=[]): + self.badnames = badnames + def validateValue(self, value): + return 1 + def validate(self, accessed, container, name, value): + if name not in self.badnames: + return 1 + raise Unauthorized(name) + +class Folder(OFS_Folder): + def __init__(self, id): + self._setId(id) + OFS_Folder.__init__(self) + +class TestZCatalogGetObject(unittest.TestCase): + # Test what objects are returned by brain.getObject() + + def setUp(self): + catalog = ZCatalog.ZCatalog('catalog') + catalog.addIndex('id', 'FieldIndex') + root = Folder('') + root.getPhysicalRoot = lambda: root + self.root = root + self.root.catalog = catalog + + def tearDown(self): + noSecurityManager() + + def test_getObject_found(self): + # Check normal traversal + root = self.root + catalog = root.catalog + root.ob = Folder('ob') + catalog.catalog_object(root.ob) + brain = catalog.searchResults()[0] + self.assertEqual(brain.getPath(), '/ob') + self.assertEqual(brain.getObject().getId(), 'ob') + + def test_getObject_missing(self): + # Check that if the object is missing None is returned + root = self.root + catalog = root.catalog + root.ob = Folder('ob') + catalog.catalog_object(root.ob) + brain = catalog.searchResults()[0] + del root.ob + self.assertEqual(brain.getObject(), None) + + def test_getObject_restricted(self): + # Check that if the object's security does not allow traversal, + # None is returned + root = self.root + catalog = root.catalog + root.fold = Folder('fold') + root.fold.ob = Folder('ob') + catalog.catalog_object(root.fold.ob) + brain = catalog.searchResults()[0] + # allow all accesses + pickySecurityManager = PickySecurityManager() + setSecurityManager(pickySecurityManager) + self.assertEqual(brain.getObject().getId(), 'ob') + # disallow just 'ob' access + pickySecurityManager = PickySecurityManager(['ob']) + setSecurityManager(pickySecurityManager) + self.assertEqual(brain.getObject(), None) + # disallow just 'fold' access + pickySecurityManager = PickySecurityManager(['fold']) + setSecurityManager(pickySecurityManager) + ob = brain.getObject() + self.failIf(ob is None) + self.assertEqual(ob.getId(), 'ob') + + def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.makeSuite( TestAddDelColumn ) ) @@ -593,6 +660,7 @@ suite.addTest( unittest.makeSuite( TestCatalogObject ) ) suite.addTest( unittest.makeSuite( TestRS ) ) suite.addTest( unittest.makeSuite( TestMerge ) ) + suite.addTest( unittest.makeSuite( TestZCatalogGetObject ) ) return suite
if __name__ == '__main__':
--
Florent Guillaume, Nuxeo (Paris, France) CTO, Director of R&D
+33 1 40 33 71 59 http://nuxeo.com [EMAIL PROTECTED]
_______________________________________________
Zope-Dev maillist - Zope-Dev@zope.org
http://mail.zope.org/mailman/listinfo/zope-dev
** No cross posts or HTML encoding! **
(Related lists - http://mail.zope.org/mailman/listinfo/zope-announce
http://mail.zope.org/mailman/listinfo/zope )