[z3-checkins] r5188 - in z3/Five/trunk: . demo/FiveViewsDemo directives handlers security tests

dreamcatcher at codespeak.net dreamcatcher at codespeak.net
Mon Jun 21 02:08:58 MEST 2004


Author: dreamcatcher
Date: Mon Jun 21 02:08:57 2004
New Revision: 5188

Modified:
   z3/Five/trunk/browser.py
   z3/Five/trunk/demo/FiveViewsDemo/configure.zcml
   z3/Five/trunk/demo/FiveViewsDemo/interfaces.py
   z3/Five/trunk/demo/FiveViewsDemo/simplecontent.py
   z3/Five/trunk/directives/content.py
   z3/Five/trunk/fiveconfigure.py
   z3/Five/trunk/fivedirectives.py
   z3/Five/trunk/handlers/content.py
   z3/Five/trunk/security/permission.py
   z3/Five/trunk/tests/test_security.py
   z3/Five/trunk/viewattribute.py
Log:
Implement security for five:page directive. Use five:content for SimpleContent. Add a test for five:page and improve test for five:content security decls.

Modified: z3/Five/trunk/browser.py
==============================================================================
--- z3/Five/trunk/browser.py	(original)
+++ z3/Five/trunk/browser.py	Mon Jun 21 02:08:57 2004
@@ -1,25 +1,32 @@
 import Acquisition
-from AccessControl import ClassSecurityInfo
+from AccessControl import ClassSecurityInfo, getSecurityManager
+from zExceptions import Unauthorized
 from Globals import InitializeClass
 
 class BrowserView(Acquisition.Explicit):
     security = ClassSecurityInfo()
-    
+
     def __init__(self, context, request):
         self.context = context
         self.request = request
 
     # XXX do not create any methods on the subclass called index_html,
     # as this makes Zope 2 traverse into that first!
-    
+
     def __call__(self, *args, **kw):
         # XXX this is definitely not the way Zope 3 does it..
         if hasattr(self, 'index'):
-            return self.index(self, *args, **kw)
-        attr = self.__page_attribute__
+            attr = 'index'
+        else:
+            attr = self.__page_attribute__
+        meth = getattr(self, attr)
+        security_manager = getSecurityManager()
+        if not security_manager.validate(meth, self, attr, meth):
+            raise Unauthorized
         if attr == '__call__':
             raise AttributeError("__call__")
-        meth = getattr(self, attr)
+        elif attr == 'index':
+            return meth(self, *args, **kw)
         return meth(*args, **kw)
-    
+
 InitializeClass(BrowserView)

Modified: z3/Five/trunk/demo/FiveViewsDemo/configure.zcml
==============================================================================
--- z3/Five/trunk/demo/FiveViewsDemo/configure.zcml	(original)
+++ z3/Five/trunk/demo/FiveViewsDemo/configure.zcml	Mon Jun 21 02:08:57 2004
@@ -6,6 +6,7 @@
     class=".browser.SimpleContentView"
     attribute="eagle"
     name="eagle.txt"
+    permission="zope.ViewManagementScreens"
     />
 
   <five:implements
@@ -22,28 +23,19 @@
     class=".browser.SimpleFolderView"
     attribute="eagle"
     name="eagle.txt"
+    permission="zope.Public"
     />
 
-  <five:content class=".browser.SimpleContentView">
+  <five:content class=".simplecontent.SimpleContent">
 
-    <five:require attributes="eagle"
-	     permission="zope.ViewManagementScreens"
-	     />
-
-  </five:content>
+    <allow interface=".interfaces.IPublicSimpleContent" />
 
-  <five:content class=".browser.SimpleFolderView">
+    <deny interface=".interfaces.IPrivateSimpleContent" />
 
-    <implements interface=".interfaces.ISimpleFolderView"
-		/>
-
-    <require interface=".interfaces.IWriteSimpleFolderView"
-	     permission="zope.ViewManagementScreens"
+    <require permission="zope.ViewManagementScreens"
+	     interface=".interfaces.IProtectedSimpleContent"
 	     />
 
-    <allow interface=".interfaces.IReadSimpleFolderView"
-	   />
-
   </five:content>
 
 </configure>

Modified: z3/Five/trunk/demo/FiveViewsDemo/interfaces.py
==============================================================================
--- z3/Five/trunk/demo/FiveViewsDemo/interfaces.py	(original)
+++ z3/Five/trunk/demo/FiveViewsDemo/interfaces.py	Mon Jun 21 02:08:57 2004
@@ -1,9 +1,26 @@
 from zope.interface import Interface
 
-class ISimpleContent(Interface):
+class IPublicSimpleContent(Interface):
+
     def mymethod():
         """This is just a sample method.
         """
+class IPrivateSimpleContent(Interface):
+
+    def myprivatemethod():
+        """This is just a sample method.
+        """
+
+class IProtectedSimpleContent(Interface):
+
+    def myprotectedmethod():
+        """This is just a sample method.
+        """
+
+class ISimpleContent(IPublicSimpleContent,
+                     IPrivateSimpleContent,
+                     IProtectedSimpleContent):
+    """A Simple Content Interface"""
 
 class IFolder(Interface):
     pass

Modified: z3/Five/trunk/demo/FiveViewsDemo/simplecontent.py
==============================================================================
--- z3/Five/trunk/demo/FiveViewsDemo/simplecontent.py	(original)
+++ z3/Five/trunk/demo/FiveViewsDemo/simplecontent.py	Mon Jun 21 02:08:57 2004
@@ -1,28 +1,33 @@
+from zope.interface import implements
+
 from OFS.SimpleItem import SimpleItem
-from Globals import InitializeClass
-from AccessControl import ClassSecurityInfo
-from helpers import add_and_edit
 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 from Products.Five.api import Viewable
-from zope.interface import implements
+
 from interfaces import ISimpleContent
+from helpers import add_and_edit
 
 class SimpleContent(SimpleItem, Viewable):
+    """A Simple Content"""
 
     implements(ISimpleContent)
-    
     meta_type = 'SimpleContent'
-    security = ClassSecurityInfo()
 
     def __init__(self, id, title):
         self.id = id
         self.title = title
-        
-    security.declarePublic('mymethod')
+
     def mymethod(self):
+        """A public method"""
         return "Hello world"
 
-InitializeClass(SimpleContent)
+    def myprivatemethod(self):
+        """A private method"""
+        return "Hello private world"
+
+    def myprotectedmethod(self):
+        """A protected method"""
+        return "Hello protected world"
 
 manage_addSimpleContentForm = PageTemplateFile(
     "www/simpleContentAdd", globals(),

Modified: z3/Five/trunk/directives/content.py
==============================================================================
--- z3/Five/trunk/directives/content.py	(original)
+++ z3/Five/trunk/directives/content.py	Mon Jun 21 02:08:57 2004
@@ -18,7 +18,6 @@
 from zope.configuration.fields import GlobalObject, Tokens, \
      PythonIdentifier, MessageID
 from zope.schema import TextLine, Id
-
 from Products.Five.security.fields import Permission
 
 class IContentDirective(Interface):

Modified: z3/Five/trunk/fiveconfigure.py
==============================================================================
--- z3/Five/trunk/fiveconfigure.py	(original)
+++ z3/Five/trunk/fiveconfigure.py	Mon Jun 21 02:08:57 2004
@@ -11,7 +11,8 @@
 from viewable import Viewable
 from api import BrowserView
 from metaclass import makeClass
-from security.permission import getSecurityInfo
+from security.permission import getSecurityInfo, CheckerPublic
+from handlers.content import protectName, initializeClass
 
 #def handler(serviceName, methodName, *args, **kwargs):
 #    method=getattr(getService(serviceName), methodName)
@@ -24,6 +25,7 @@
 def page(_context, name, for_,
          layer='default', template=None, class_=None,
          attribute='__call__', menu=None, title=None,
+         permission=CheckerPublic
          ):
 
     try:
@@ -60,6 +62,7 @@
         # the security decls directives haven't been
         # executed yet.
         if template:
+            attribute = 'index'
             # class and template
             new_class = SimpleViewClass(
                 template, bases=(class_, ),
@@ -72,6 +75,7 @@
 
     else:
         # template
+        attribute = 'index'
         new_class = SimpleViewClass(template, bases=(BrowserView,))
 
     _handle_for(_context, for_)
@@ -83,6 +87,17 @@
                 IBrowserRequest, new_class, name, [for_], Interface, layer,
                 _context.info),
         )
+    _context.action(
+        discriminator = ('five:protectName', new_class, attribute),
+        callable = protectName,
+        args = (new_class, attribute, permission)
+        )
+    _context.action(
+        discriminator = ('five:initialize:class', new_class),
+        callable = initializeClass,
+        args = (new_class,)
+        )
+
 
 def _handle_for(_context, for_):
     if for_ is not None:

Modified: z3/Five/trunk/fivedirectives.py
==============================================================================
--- z3/Five/trunk/fivedirectives.py	(original)
+++ z3/Five/trunk/fivedirectives.py	Mon Jun 21 02:08:57 2004
@@ -2,6 +2,7 @@
 from zope.configuration.fields import GlobalObject, Tokens,\
      PythonIdentifier, MessageID
 from zope.schema import TextLine, Id
+from Products.Five.security.fields import Permission
 
 class IBasicViewInformation(Interface):
     """
@@ -89,6 +90,15 @@
         required=False
         )
 
+    permission = Permission(
+        title=u"Permission",
+        description=u"""
+        Specifies the permission by id that will be required to
+        access or mutate the attributes and methods specified.""",
+        required=False
+        )
+
+
 class IPageDirective(IPagesDirective, IPagesPageSubdirective):
     """
     The page directive is used to create views that provide a single

Modified: z3/Five/trunk/handlers/content.py
==============================================================================
--- z3/Five/trunk/handlers/content.py	(original)
+++ z3/Five/trunk/handlers/content.py	Mon Jun 21 02:08:57 2004
@@ -89,7 +89,7 @@
     def __protectName(self, name, permission_id):
         "Set a permission on a particular name."
         self.__context.action(
-            discriminator = ('protectName', self.__class, name),
+            discriminator = ('five:protectName', self.__class, name),
             callable = protectName,
             args = (self.__class, name, permission_id)
             )
@@ -109,7 +109,6 @@
 
 
 def initializeClass(klass):
-    print 'Initializing class %r' % klass
     InitializeClass(klass)
 
 def _getSecurity(klass):
@@ -122,7 +121,6 @@
     return security
 
 def protectName(klass, name, permission_id):
-    print klass, name, permission_id
     security = _getSecurity(klass)
     # Zope 2 uses string, not unicode yet
     name = str(name)

Modified: z3/Five/trunk/security/permission.py
==============================================================================
--- z3/Five/trunk/security/permission.py	(original)
+++ z3/Five/trunk/security/permission.py	Mon Jun 21 02:08:57 2004
@@ -15,6 +15,15 @@
             sec[k] = v
     return sec
 
+def clearSecurityInfo(klass):
+    sec = {}
+    info = vars(klass)
+    if info.has_key('__ac_permissions__'):
+        delattr(klass, '__ac_permissions__')
+    for k, v in info.items():
+        if k.endswith('__roles__'):
+            delattr(klass, k)
+
 def checkPermission(context, permission_id):
     """Check whether a given permission exists in the provided context.
 

Modified: z3/Five/trunk/tests/test_security.py
==============================================================================
--- z3/Five/trunk/tests/test_security.py	(original)
+++ z3/Five/trunk/tests/test_security.py	Mon Jun 21 02:08:57 2004
@@ -24,11 +24,29 @@
 ZopeTestCase.installProduct('Five')
 
 from zope.configuration import xmlconfig
+from zope.component import getView
+from zope.interface import Interface, implements
+from zope.testing.cleanup import CleanUp
 from Products.Five import zcml
+from Products.Five.browser import BrowserView
+from Products.Five.viewable import FakeRequest
+from Products.Five.security.permission import clearSecurityInfo
 from AccessControl import ClassSecurityInfo
+from AccessControl.PermissionRole import PermissionRole
 from Globals import InitializeClass
 
+class IDummy(Interface):
+    """Just a marker interface"""
+
+class DummyView(BrowserView):
+    """A dummy view"""
+
+    def foo(self):
+        """A foo"""
+        return 'A foo view'
+
 class Dummy1:
+    implements(IDummy)
     def foo(self): pass
     def bar(self): pass
     def baz(self): pass
@@ -42,29 +60,59 @@
     security.declarePrivate('baz')
     security.declareProtected('View management screens', 'keg')
 
-class SecurityTestCase(ZopeTestCase.ZopeTestCase):
+class PageSecurityTestCase(CleanUp, ZopeTestCase.ZopeTestCase):
+
+    def setUp(self):
+        super(PageSecurityTestCase, self).setUp()
+        self.dummy1 = Dummy1
+        zcml.initialize()
 
-    def test_require(self):
-        # Need to use xmlconfig() to run a
-        # small zcml snippet and then check
-        # the class __ac_permissions__ to see
-        # if the declaration took effect.
-        pass
+    def tearDown(self):
+        super(PageSecurityTestCase, self).tearDown()
+        clearSecurityInfo(self.dummy1)
+        zcml.reset()
 
-    def test_allow(self):
-        pass
+    def test_page_security(self):
+        self.failIf(hasattr(self.dummy1, '__ac_permissions__'))
 
-    def test_initialize(self):
-        pass
+        decl = """
+        <configure xmlns="http://namespaces.zope.org/zope"
+            xmlns:five="http://namespaces.zope.org/five">
 
-class SecurityEquivalenceTestCase(ZopeTestCase.ZopeTestCase):
+          <five:page
+             for="Five.tests.test_security.IDummy"
+             class="Five.tests.test_security.DummyView"
+             attribute="foo"
+             name="foo.txt"
+             permission="zope.ViewManagementScreens"
+           />
+
+        </configure>
+        """
+        zcml.string(decl)
+        request = FakeRequest()
+        view = getView(Dummy1(), 'foo.txt', request)
+
+        ac = getattr(view, '__ac_permissions__')
+        ex_ac = (('View management screens', ('foo',)),)
+        self.assertEquals(ac, ex_ac)
+        foo_roles = getattr(view, 'foo__roles__', None)
+        self.failIf(foo_roles is None)
+        self.failIf(foo_roles == ())
+        self.assertEquals(foo_roles.__of__(view), ('Manager',))
+
+class SecurityEquivalenceTestCase(CleanUp, ZopeTestCase.ZopeTestCase):
 
     def setUp(self):
+        super(SecurityEquivalenceTestCase, self).setUp()
         self.dummy1 = Dummy1
         self.dummy2 = Dummy2
         zcml.initialize()
 
     def tearDown(self):
+        super(SecurityEquivalenceTestCase, self).tearDown()
+        clearSecurityInfo(self.dummy1)
+        clearSecurityInfo(self.dummy2)
         zcml.reset()
 
     def test_equivalence(self):
@@ -91,23 +139,39 @@
         """
         zcml.string(decl)
         InitializeClass(self.dummy2)
+
         ac1 = getattr(self.dummy1, '__ac_permissions__')
         ac2 = getattr(self.dummy2, '__ac_permissions__')
         self.assertEquals(ac1, ac2)
+
+        bar_roles1 = getattr(self.dummy1, 'bar__roles__').__of__(self.dummy1)
+        self.assertEquals(bar_roles1.__of__(self.dummy1), ('Manager',))
+
+        keg_roles1 = getattr(self.dummy1, 'keg__roles__').__of__(self.dummy1)
+        self.assertEquals(keg_roles1.__of__(self.dummy1), ('Manager',))
+
         foo_roles1 = getattr(self.dummy1, 'foo__roles__')
-        baz_roles1 = getattr(self.dummy1, 'baz__roles__')
         self.assertEquals(foo_roles1, None)
+
+        baz_roles1 = getattr(self.dummy1, 'baz__roles__')
         self.assertEquals(baz_roles1, ())
 
+        bar_roles2 = getattr(self.dummy2, 'bar__roles__').__of__(self.dummy2)
+        self.assertEquals(bar_roles2.__of__(self.dummy2), ('Manager',))
+
+        keg_roles2 = getattr(self.dummy2, 'keg__roles__').__of__(self.dummy2)
+        self.assertEquals(keg_roles2.__of__(self.dummy2), ('Manager',))
+
         foo_roles2 = getattr(self.dummy2, 'foo__roles__')
-        baz_roles2 = getattr(self.dummy2, 'baz__roles__')
         self.assertEquals(foo_roles2, None)
+
+        baz_roles2 = getattr(self.dummy2, 'baz__roles__')
         self.assertEquals(baz_roles2, ())
 
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(SecurityEquivalenceTestCase))
-    suite.addTest(unittest.makeSuite(SecurityTestCase))
+    suite.addTest(unittest.makeSuite(PageSecurityTestCase))
     return suite
 
 if __name__ == '__main__':

Modified: z3/Five/trunk/viewattribute.py
==============================================================================
--- z3/Five/trunk/viewattribute.py	(original)
+++ z3/Five/trunk/viewattribute.py	Mon Jun 21 02:08:57 2004
@@ -8,10 +8,10 @@
     pass
 
 class ViewAttribute(Acquisition.Explicit):
- 
+
     def __init__(self, view_type):
         self._view_type = view_type
-        
+
     def index_html(self):
         """Default method on view
         """
@@ -26,9 +26,9 @@
         security_manager = getSecurityManager()
         if not security_manager.validate(method_on_view, obj, 'index_html',
                                          method_on_view):
-            raise Unauthorized        
+            raise Unauthorized
         return method_on_view()
-     
+
     def __getitem__(self, name):
         """Get correct method on view
         """
@@ -40,10 +40,10 @@
         view = view.__of__(obj)
         # look up method
         method_on_view = getattr(view, name, None)
-        
+
         if method_on_view is None:
             # we do not accept calling unknown methods
-            raise FiveViewError, "Unknown view method: %s" % name 
-        
+            raise FiveViewError, "Unknown view method: %s" % name
+
         # let the ZPublisher do the calling, its security kicks in
         return method_on_view


More information about the z3-checkins mailing list