Log message for revision 127279: - refactored ``browser:view`` and ``browser:page`` directives
Changed: U Zope/trunk/doc/CHANGES.rst UU Zope/trunk/src/Products/Five/browser/metaconfigure.py U Zope/trunk/src/Products/Five/browser/tests/pages.txt U Zope/trunk/src/Products/Five/browser/tests/test_zope3security.py -=- Modified: Zope/trunk/doc/CHANGES.rst =================================================================== --- Zope/trunk/doc/CHANGES.rst 2012-07-06 22:50:25 UTC (rev 127278) +++ Zope/trunk/doc/CHANGES.rst 2012-07-07 09:41:15 UTC (rev 127279) @@ -40,6 +40,10 @@ Features Added ++++++++++++++ +- Five: Refactored ``browser:view`` and ``browser:page`` directives. + This makes their implementation more similar to that in ``zope.browserpage`` + and adds allowed_interface support for the ``browser:view`` directive. + - Optimized the `OFS.Traversable.getPhysicalPath` method to avoid excessive amounts of method calls. Modified: Zope/trunk/src/Products/Five/browser/metaconfigure.py =================================================================== --- Zope/trunk/src/Products/Five/browser/metaconfigure.py 2012-07-06 22:50:25 UTC (rev 127278) +++ Zope/trunk/src/Products/Five/browser/metaconfigure.py 2012-07-07 09:41:15 UTC (rev 127279) @@ -33,9 +33,12 @@ from zope.security.zcml import Permission import zope.browserpage.metaconfigure -from zope.browserpage.metaconfigure import providesCallable -from zope.browserpage.metaconfigure import _handle_menu +from zope.browserpage.metaconfigure import _handle_allowed_attributes +from zope.browserpage.metaconfigure import _handle_allowed_interface from zope.browserpage.metaconfigure import _handle_for +from zope.browserpage.metaconfigure import _handle_menu +from zope.browserpage.metaconfigure import _handle_permission +from zope.browserpage.metaconfigure import providesCallable from zope.browserpage.metadirectives import IViewDirective from AccessControl.class_init import InitializeClass @@ -52,14 +55,48 @@ from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.metaclass import makeClass +def _configure_z2security(_context, new_class, required): + _context.action( + discriminator=('five:protectClass', new_class), + callable=protectClass, + args=(new_class, required.pop('')) + ) + for attr, permission in required.iteritems(): + _context.action( + discriminator=('five:protectName', new_class, attr), + callable=protectName, + args=(new_class, attr, permission) + ) + # Make everything else private + private_attrs = [name for name in dir(new_class) + if (not name.startswith('_')) and + (name not in required) and + ismethod(getattr(new_class, name))] + for attr in private_attrs: + _context.action( + discriminator=('five:protectName', new_class, attr), + callable=protectName, + args=(new_class, attr, CheckerPrivateId, False) + ) + # Protect the class + _context.action( + discriminator=('five:initialize:class', new_class), + callable=InitializeClass, + args=(new_class,) + ) +# page + def page(_context, name, permission, for_=Interface, layer=IDefaultBrowserLayer, template=None, class_=None, allowed_interface=None, allowed_attributes=None, attribute='__call__', menu=None, title=None, ): _handle_menu(_context, menu, title, [for_], name, permission, layer) + required = {} + permission = _handle_permission(_context, permission) + if not (class_ or template): raise ConfigurationError("Must specify a class or template") @@ -77,6 +114,7 @@ if not os.path.isfile(template): raise ConfigurationError("No such file", template) + # TODO: new __name__ attribute must be tested if class_: if attribute != '__call__': if not hasattr(class_, attribute): @@ -122,14 +160,18 @@ # template new_class = makeClassForTemplate(template, name=name) - if allowed_attributes is None: - allowed_attributes = [] - if allowed_interface is not None: - for interface in allowed_interface: - allowed_attributes.extend(interface.names(all=True)) + for n in ('', attribute): + required[n] = permission + _handle_allowed_interface(_context, allowed_interface, permission, + required) + _handle_allowed_attributes(_context, allowed_attributes, permission, + required) + _handle_for(_context, for_) + _configure_z2security(_context, new_class, required) + _context.action( discriminator = ('view', (for_, layer), name, IBrowserRequest), callable = handler, @@ -137,40 +179,7 @@ new_class, (for_, layer), Interface, name, _context.info), ) - # Security - _context.action( - discriminator = ('five:protectClass', new_class), - callable = protectClass, - args = (new_class, permission) - ) - if allowed_attributes: - for attr in allowed_attributes: - _context.action( - discriminator = ('five:protectName', new_class, attr), - callable = protectName, - args = (new_class, attr, permission) - ) - # Make everything else private - allowed = [attribute] + (allowed_attributes or []) - private_attrs = [name for name in dir(new_class) - if (not name.startswith('_')) and - (name not in allowed) and - ismethod(getattr(new_class, name))] - for attr in private_attrs: - _context.action( - discriminator = ('five:protectName', new_class, attr), - callable = protectName, - args = (new_class, attr, CheckerPrivateId) - ) - # Protect the class - _context.action( - discriminator = ('five:initialize:class', new_class), - callable = InitializeClass, - args = (new_class,) - ) - - class pages(zope.browserpage.metaconfigure.pages): def page(self, _context, name, attribute='__call__', template=None, @@ -274,8 +283,17 @@ cdict['__name__'] = name newclass = makeClass(cname, bases, cdict) + for n in ('',): + required[n] = permission + + _handle_allowed_interface(_context, allowed_interface, permission, + required) + _handle_allowed_attributes(_context, allowed_attributes, permission, + required) _handle_for(_context, for_) + _configure_z2security(_context, newclass, required) + if self.provides is not None: _context.action( discriminator = None, @@ -291,42 +309,7 @@ _context.info), ) - # Security - _context.action( - discriminator = ('five:protectClass', newclass), - callable = protectClass, - args = (newclass, permission) - ) - - if allowed_attributes: - for attr in allowed_attributes: - _context.action( - discriminator = ('five:protectName', newclass, attr), - callable = protectName, - args = (newclass, attr, permission) - ) - - # Make everything else private - allowed = allowed_attributes or [] - private_attrs = [name for name in dir(newclass) - if (not name.startswith('_')) and - (name not in allowed) and - ismethod(getattr(newclass, name))] - for attr in private_attrs: - _context.action( - discriminator = ('five:protectName', newclass, attr), - callable = protectName, - args = (newclass, attr, CheckerPrivateId, False) - ) - - # Protect the class - _context.action( - discriminator = ('five:initialize:class', newclass), - callable = InitializeClass, - args = (newclass,) - ) - _factory_map = {'image':{'prefix':'ImageResource', 'count':0, 'factory':ImageResourceFactory}, @@ -448,6 +431,14 @@ class ViewMixinForAttributes(BrowserView, zope.browserpage.metaconfigure.simple): + # XXX: this alternative implementation would support permission checks for + # the attribute instead of the view + # def browserDefault(self, request): + # return self, (self.__page_attribute__,) + # + # def publishTraverse(self, request, name): + # return getattr(self, name) + # For some reason, the 'simple' baseclass doesn't implement this # mandatory method (see https://bugs.launchpad.net/zope3/+bug/129296) def browserDefault(self, request): Property changes on: Zope/trunk/src/Products/Five/browser/metaconfigure.py ___________________________________________________________________ Deleted: svn:keywords - Id Modified: Zope/trunk/src/Products/Five/browser/tests/pages.txt =================================================================== --- Zope/trunk/src/Products/Five/browser/tests/pages.txt 2012-07-06 22:50:25 UTC (rev 127278) +++ Zope/trunk/src/Products/Five/browser/tests/pages.txt 2012-07-07 09:41:15 UTC (rev 127279) @@ -205,11 +205,11 @@ >>> request = TestRequest() >>> view = getMultiAdapter((self.folder.testoid, request), name=u'eagle.txt') -It's protecting the object with the permission, and not the attribute, -so we get ('',) instead of ('eagle',): +With the current browserDefault implementation permissions are checked for the +object, and not for the attribute, but it is more robust to protect both: >>> view.__ac_permissions__ - (('View management screens', ('',)),) + (('View management screens', ('', 'eagle')),) The view's __roles__ attribute can be evaluated correctly: @@ -224,6 +224,21 @@ >>> aq_acquire(view, '__roles__') ('Manager',) + >>> aq_acquire(view, 'eagle__roles__') + ('Manager',) + +Other attributes are private: + + >>> from AccessControl import ACCESS_PRIVATE + >>> aq_acquire(view, 'mouse__roles__') is ACCESS_PRIVATE + True + +In zope.browserpage this is just protected with the specified permission. Not +sure if this has to be private in Zope 2: + + >>> aq_acquire(view, 'publishTraverse__roles__') is ACCESS_PRIVATE + True + Check to see if view's context properly acquires its true parent Modified: Zope/trunk/src/Products/Five/browser/tests/test_zope3security.py =================================================================== --- Zope/trunk/src/Products/Five/browser/tests/test_zope3security.py 2012-07-06 22:50:25 UTC (rev 127278) +++ Zope/trunk/src/Products/Five/browser/tests/test_zope3security.py 2012-07-07 09:41:15 UTC (rev 127279) @@ -78,6 +78,12 @@ ... xmlns:browser="http://namespaces.zope.org/browser"> ... <browser:page ... for="*" + ... name="testpage" + ... permission="zope2.ViewManagementScreens" + ... class="AccessControl.tests.testZCML.Dummy1" + ... allowed_interface="AccessControl.tests.testZCML.IDummy" /> + ... <browser:view + ... for="*" ... name="testview" ... permission="zope2.ViewManagementScreens" ... class="AccessControl.tests.testZCML.Dummy1" @@ -106,7 +112,7 @@ >>> from zope.component import getMultiAdapter >>> from zope.publisher.browser import TestRequest >>> request = TestRequest() - >>> view = getMultiAdapter((dummy1, request), name="testview") + >>> view = getMultiAdapter((dummy1, request), name="testpage") As 'foo' is defined in IDummy, it should have the 'Manager' role. @@ -124,6 +130,16 @@ >>> getRoles(view, 'superMethod', view.superMethod, ('Def',)) ('Manager',) + Same tests work using the view directive: + + >>> view = getMultiAdapter((dummy1, request), name="testview") + >>> getRoles(view, 'foo', view.foo, ('Def',)) + ('Manager',) + >>> getRoles(view, 'wot', view.wot, ('Def',)) is ACCESS_PRIVATE + True + >>> getRoles(view, 'superMethod', view.superMethod, ('Def',)) + ('Manager',) + >>> tearDown() """ _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org https://mail.zope.org/mailman/listinfo/zope-checkins