[Zope-Checkins] SVN: Zope/trunk/ - refactored ``browser:view`` and ``browser:page`` directives

Yvo Schubbe cvs-admin at zope.org
Sat Jul 7 09:41:21 UTC 2012


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()
     """
 



More information about the Zope-Checkins mailing list