[Zope3-checkins] SVN: Zope3/trunk/ Add locate attribute to the adapter directive in

Dominik Huber dominik.huber at projekt01.ch
Mon May 23 05:50:12 EDT 2005


Log message for revision 30480:
  Add locate attribute to the adapter directive in
  order to ensure location for security proxied objects.
  An explicite locate should be used if complex adapter
  permission are declared within an additional class
  directive.
  
  Assert location if a non-public permission
  was specified within an adapter directive so
  that restricted adapters work with local 
  authentication too.
  
  Fix 'Wiki permissions and PAU' bug 
  reported by Paolo Invernizzi

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  U   Zope3/trunk/src/zope/app/component/metaconfigure.py
  U   Zope3/trunk/src/zope/app/component/metadirectives.py
  U   Zope3/trunk/src/zope/app/component/tests/test_directives.py
  U   Zope3/trunk/src/zope/app/security/adapter.py
  U   Zope3/trunk/src/zwiki/configure.zcml

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/doc/CHANGES.txt	2005-05-23 09:50:12 UTC (rev 30480)
@@ -10,6 +10,14 @@
 
     New features
 
+      - Add content_factory_id attribute to the addform and addwizard
+        directive so that named factories can be invoked too.
+
+      - Add locate attribute to the adapter directive in order to ensure
+        location for security proxied objects. An explicite locate should be
+        if complex adapter security declarations within an additional class
+        directive are used.
+
       - The http() function exposed to functional doctests how accepts
         an optional "form" argument to use as the set of form values.
         This works for any type of request; this can used to make
@@ -591,6 +599,12 @@
 
     Bug Fixes
 
+      - Fix 'Wiki permissions and PAU' bug reported by Paolo Invernizzi
+
+      - Assert location if a non-public permission was specified within an
+        adapter directive so that restricted adapters work with local
+        authentication too.
+
       - Fix #313 : importchecker.py ignores assignment to module attributes
     
       - Fix #327 : File type change clears object content

Modified: Zope3/trunk/src/zope/app/component/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/component/metaconfigure.py	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/src/zope/app/component/metaconfigure.py	2005-05-23 09:50:12 UTC (rev 30480)
@@ -29,6 +29,8 @@
 from zope.security.proxy import Proxy
 
 from zope.app import zapi
+from zope.app.security.adapter import LocatingTrustedAdapterFactory
+from zope.app.security.adapter import LocatingUntrustedAdapterFactory
 from zope.app.security.adapter import TrustedAdapterFactory
 
 PublicPermission = 'zope.Public'
@@ -61,7 +63,7 @@
 
 _handler=handler
 def subscriber(_context, for_=None, factory=None, handler=None, provides=None,
-               permission=None, trusted=False):
+               permission=None, trusted=False, locate=False):
 
 
     if factory is None:
@@ -111,8 +113,15 @@
                 ob = f(ob)
             return ob
 
-    if trusted:
-        factory = TrustedAdapterFactory(factory)
+    # invoke custom adapter factories
+    if locate or (permission is not None and permission is not CheckerPublic):
+        if trusted:
+            factory = LocatingTrustedAdapterFactory(factory)
+        else:
+            factory = LocatingUntrustedAdapterFactory(factory) 
+    else:
+        if trusted:
+            factory = TrustedAdapterFactory(factory) 
 
     _context.action(
         discriminator = None,
@@ -138,7 +147,7 @@
                 )
 
 def adapter(_context, factory, provides=None, for_=None, permission=None,
-            name='', trusted=False):
+            name='', trusted=False, locate=False):
 
     if for_ is None:
         if len(factory) == 1:
@@ -176,8 +185,15 @@
         checker = InterfaceChecker(provides, permission)
         factory = _protectedFactory(factory, checker)
 
-    if trusted:
-        factory = TrustedAdapterFactory(factory)
+    # invoke custom adapter factories
+    if locate or (permission is not None and permission is not CheckerPublic):
+        if trusted:
+            factory = LocatingTrustedAdapterFactory(factory)
+        else:
+            factory = LocatingUntrustedAdapterFactory(factory) 
+    else:
+        if trusted:
+            factory = TrustedAdapterFactory(factory) 
 
     _context.action(
         discriminator = ('adapter', for_, provides, name),

Modified: Zope3/trunk/src/zope/app/component/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/component/metadirectives.py	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/src/zope/app/component/metadirectives.py	2005-05-23 09:50:12 UTC (rev 30480)
@@ -219,6 +219,17 @@
         default=False,
         )
 
+    locate = zope.configuration.fields.Bool(
+        title=_("Locate"),
+        description=_("""Make the adapter a locatable adapter
+
+        Located adapter should be used if a non-public permission
+        is used.
+        """),
+        required=False,
+        default=False,
+        )
+
 class ISubscriberDirective(zope.interface.Interface):
     """
     Register a subscriber
@@ -273,6 +284,17 @@
         default=False,
         )
 
+    locate = zope.configuration.fields.Bool(
+        title=_("Locate"),
+        description=_("""Make the adapter a locatable adapter
+
+        Located adapter should be used if a non-public permission
+        is used.
+        """),
+        required=False,
+        default=False,
+        )
+
 class IUtilityDirective(IBasicComponentInformation):
     """Register a utility."""
 

Modified: Zope3/trunk/src/zope/app/component/tests/test_directives.py
===================================================================
--- Zope3/trunk/src/zope/app/component/tests/test_directives.py	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/src/zope/app/component/tests/test_directives.py	2005-05-23 09:50:12 UTC (rev 30480)
@@ -184,7 +184,6 @@
               />
             '''
             )))
-
         # With an unproxied object, business as usual
         content = Content()
         a1 = A1()
@@ -193,6 +192,7 @@
         a3 = subscribers[0]
 
         self.assertEqual(a3.__class__, A3)
+        self.assertEqual(type(a3).__name__, 'A3')
         self.assertEqual(a3.context, (content, a1))
 
         # Now with a proxied object:
@@ -205,10 +205,50 @@
         self.assertEqual(type(a3), Proxy)
 
 
-        # around an unproxied object:
+        # behind the security proxy is no locatin proxy:
         from zope.security.proxy import removeSecurityProxy
         self.assert_(removeSecurityProxy(a3).context[0] is content)
+        self.assertEqual(type(removeSecurityProxy(a3)).__name__, 'A3')
 
+
+    def testLocatableTrustedSubscriber(self):
+        xmlconfig(StringIO(template % (
+            '''
+            <subscriber
+              provides="zope.app.component.tests.adapter.IS"
+              factory="zope.app.component.tests.adapter.A3"
+              for="zope.app.component.tests.components.IContent
+                   zope.app.component.tests.adapter.I1"
+              trusted="yes"
+              locate="yes"
+              />
+            '''
+            )))
+        # With an unproxied object, business as usual
+        content = Content()
+        a1 = A1()
+        subscribers = zapi.subscribers((content, a1), IS)
+
+        a3 = subscribers[0]
+
+        self.assertEqual(a3.__class__, A3)
+        self.assertEqual(type(a3).__name__, 'A3')
+        self.assertEqual(a3.context, (content, a1))
+
+        # Now with a proxied object:
+        from zope.security.checker import ProxyFactory
+        p = ProxyFactory(content)
+
+        # we get a proxied subscriber:
+        a3 = zapi.subscribers((p, a1), IS)[0]
+        from zope.security.proxy import Proxy
+        self.assertEqual(type(a3), Proxy)
+
+        # behind the security proxy is a locatio proxy:
+        from zope.security.proxy import removeSecurityProxy
+        self.assert_(removeSecurityProxy(a3).context[0] is content)
+        self.assertEqual(type(removeSecurityProxy(a3)).__name__, 'LocationProxy')
+
     def testSubscriber_w_no_provides(self):
         xmlconfig(StringIO(template % (
             '''
@@ -290,6 +330,29 @@
 
         self.assertEqual(IApp(Content()).__class__, Comp)
 
+    def testAdapterWithPermission(self):
+        # Full import is critical!
+        self.assertEqual(IV(Content(), None), None)
+
+        xmlconfig(StringIO(template % (
+            '''
+            <permission
+                id="y.x"
+                title="XY"
+                description="Allow XY." />
+
+            <adapter
+              factory="zope.app.component.tests.components.Comp"
+              provides="zope.app.component.tests.components.IApp"
+              for="zope.app.component.tests.components.IContent"
+              permission="y.x"
+              />
+            '''
+            )))
+
+        self.assertEqual(IApp(Content()).__class__, Comp)
+        self.assertEqual(type(IApp(Content())).__name__, 'LocationProxy')
+
     def testAdapter_wo_provides_or_for(self):
         # Full import is critical!
         self.assertEqual(IV(Content(), None), None)
@@ -345,7 +408,7 @@
 
         # With an unproxied object, business as usual
         ob = Content()
-        self.assertEqual(type(I1(ob)), type(A1()))
+        self.assertEqual(type(I1(ob)).__name__, 'A1')
 
         # Now with a proxied object:
         from zope.security.checker import ProxyFactory
@@ -359,10 +422,117 @@
         # around an unproxied object:
         from zope.security.proxy import removeSecurityProxy
         a = removeSecurityProxy(a)
+        self.assertEqual(type(a).__name__, 'A1')
         self.assert_(a.context[0] is ob)
-        
-        
 
+
+    def testTrustedAdapterWithPermission(self):
+        # Full import is critical!
+        xmlconfig(StringIO(template % (
+            '''
+            <permission
+                id="y.x"
+                title="XY"
+                description="Allow XY." />
+
+            <adapter
+              factory="zope.app.component.tests.adapter.A1"
+              provides="zope.app.component.tests.adapter.I1"
+              for="zope.app.component.tests.components.IContent"
+              permission="y.x"
+              trusted="yes"
+              />
+            '''
+            )))
+
+        # With an unproxied object, business as usual
+        ob = Content()
+        self.assertEqual(type(I1(ob)).__name__, 'A1')
+
+        # Now with a proxied object:
+        from zope.security.checker import ProxyFactory
+        p = ProxyFactory(ob)
+
+        # we get a proxied adapter:
+        a = I1(p)
+        from zope.security.proxy import Proxy
+        self.assertEqual(type(a), Proxy)
+
+        # behind the security proxy is location proxy
+        # if non-public permission is used
+        from zope.security.proxy import removeSecurityProxy
+        a = removeSecurityProxy(a)
+        self.assertEqual(type(a).__name__, 'LocationProxy')
+        self.assert_(a.context[0] is ob)
+
+
+    def testTrustedAdapterWithPublicPermission(self):
+        # Full import is critical!
+        xmlconfig(StringIO(template % (
+            '''
+            <adapter
+              factory="zope.app.component.tests.adapter.A1"
+              provides="zope.app.component.tests.adapter.I1"
+              for="zope.app.component.tests.components.IContent"
+              permission="zope.Public"
+              trusted="yes"
+              />
+            '''
+            )))
+
+        # With an unproxied object, business as usual
+        ob = Content()
+        self.assertEqual(type(I1(ob)).__name__, 'A1')
+
+        # Now with a proxied object:
+        from zope.security.checker import ProxyFactory
+        p = ProxyFactory(ob)
+
+        # we get a proxied adapter:
+        a = I1(p)
+        from zope.security.proxy import Proxy
+        self.assertEqual(type(a), Proxy)
+
+        # behind the security proxy is no location proxy
+        from zope.security.proxy import removeSecurityProxy
+        a = removeSecurityProxy(a)
+        self.assertEqual(type(a).__name__, 'A1')
+        self.assert_(a.context[0] is ob)
+
+
+    def testLocatableTrustedAdapter(self):
+        # Full import is critical!
+        xmlconfig(StringIO(template % (
+            '''
+            <adapter
+              factory="zope.app.component.tests.adapter.A1"
+              provides="zope.app.component.tests.adapter.I1"
+              for="zope.app.component.tests.components.IContent"
+              trusted="yes"
+              locate="yes"
+              />
+            '''
+            )))
+
+        # With an unproxied object, business as usual
+        ob = Content()
+        self.assertEqual(type(I1(ob)).__name__, 'A1')
+
+        # Now with a proxied object:
+        from zope.security.checker import ProxyFactory
+        p = ProxyFactory(ob)
+
+        # we get a proxied adapter:
+        a = I1(p)
+        from zope.security.proxy import Proxy
+        self.assertEqual(type(a), Proxy)
+
+        # behind the security proxy is always location proxy:
+        from zope.security.proxy import removeSecurityProxy
+        a = removeSecurityProxy(a)
+        self.assertEqual(type(a).__name__, 'LocationProxy')
+        self.assert_(a.context[0] is ob) 
+
     def testAdapter_w_multiple_factories(self):
         xmlconfig(StringIO(template % (
             '''

Modified: Zope3/trunk/src/zope/app/security/adapter.py
===================================================================
--- Zope3/trunk/src/zope/app/security/adapter.py	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/src/zope/app/security/adapter.py	2005-05-23 09:50:12 UTC (rev 30480)
@@ -18,132 +18,117 @@
 
 from zope.security.checker import ProxyFactory
 from zope.security.proxy import removeSecurityProxy
-from zope.app.location import ILocation, Location
+from zope.app.location import ILocation, Location, LocationProxy
 
-class TrustedAdapterFactory(object):
-    """Adapt an adapter factory to to provide trusted adapters
 
-       Trusted adapters always adapt unproxied objects.  If asked to
-       adapt any proxied objects, it will unproxy them and then proxy the
-       resulting adapter.
+def assertLocation(adapter, parent):
+    """Assert locatable adapters.
 
-       Suppose we have an adapter factory:
+    This function asserts that the adapter get location-proxied unless it does
+    not provide ILocation itself. Further more the returned locatable adapter
+    get its parent set unless its __parent__ attribute is not None.
 
-         >>> class A(object):
-         ...     def __init__(self, context):
-         ...         self.context = context
+    see adapter.txt
+    """
+    # handle none-locatable adapters (A)
+    if not ILocation.providedBy(adapter):
+        locatable = LocationProxy(adapter)
+        locatable.__parent__ = parent
+        return locatable
 
-       Now, suppose have an object and proxy it:
+    # handle locatable, parentless adapters (B)
+    if adapter.__parent__ is None:
+        adapter.__parent__ = parent
+        return adapter
 
-         >>> o = []
-         >>> p = ProxyFactory(o)
+    # handle locatable, parentful adapters (C)
+    else:
+        return adapter
 
-       If we adapt it:
 
-         >>> a = A(p)
+class LocatingTrustedAdapterFactory(object):
+    """Adapt an adapter factory to provide trusted and (locatable) adapters.
 
-       the result is not a proxy:
+    Trusted adapters always adapt unproxied objects. If asked to
+    adapt any proxied objects, it will unproxy them and then 
+    security-proxy the resulting adapter (S) unless the objects where not
+    security-proxied before (N).
 
-         >>> type(a).__name__
-         'A'
+    Further locating trusted adapters provide a location for protected
+    adapters only (S). If such a protected adapter itself does not provide
+    ILocation it is wrapped within a location proxy and it parent will 
+    be set. If the adapter does provide ILocation and it's __parent__ is None,
+    we set the __parent__ to the adapter's context: 
 
-       But the object it adapts still is:
+    see adapter.txt
+    """
+    def __init__(self, factory):
+        self.factory = factory
+        self.__name__ = factory.__name__
+        self.__module__ = factory.__module__
 
-         >>> type(a.context).__name__
-         '_Proxy'
+    # protected methods
+    def _customizeProtected(self, adapter, context):
+        return assertLocation(adapter, context)
 
-       Now, will we'll adapt our adapter factory to a trusted adapter factory:
+    def _customizeUnprotected(self, adapter, context):
+        if (ILocation.providedBy(adapter)
+            and adapter.__parent__ is None):
+                    adapter.__parent__ = context
+        return adapter
 
-         >>> TA = TrustedAdapterFactory(A)
+    def __call__(self, *args):
+        for arg in args:
+            if removeSecurityProxy(arg) is not arg:
+                args = map(removeSecurityProxy, args)
+                adapter = self.factory(*args)
+                adapter = self._customizeProtected(adapter, args[0])
+                return ProxyFactory(adapter)
 
-       and if we use it:
+        adapter = self.factory(*args)
+        adapter = self._customizeUnprotected(adapter, args[0])
+        return adapter
 
-         >>> a = TA(p)
 
-       then the adapter is proxied:
+# BBB, entire class gone in 3.2 
+class TrustedAdapterFactory(LocatingTrustedAdapterFactory):
+    """Adapt an adapter factory to provide trusted adapters.
 
-         >>> type(a).__name__
-         '_Proxy'
+    Trusted adapters always adapt unproxied objects. If asked to
+    adapt any proxied objects, it will unproxy them and then 
+    security-proxy the resulting adapter unless the objects where not
+    security-proxied before.
 
-       And the object proxied is not.  (We actually have to remove the
-       adapter to get to the adapted object in this case.)
+    If the adapter does provide ILocation and it's __parent__ is None,
+    we set the __parent__ to the adapter's context.
+    """
 
-         >>> a = removeSecurityProxy(a)
-         >>> type(a.context).__name__
-         'list'
+    # do not location-proxy the adapter
+    def _customizeProtected(self, adapter, context):
+        return self._customizeUnprotected(adapter, context)
 
-       This works with multiple objects too:
 
-         >>> class M(object):
-         ...     def __init__(self, *context):
-         ...         self.context = context
+class LocatingUntrustedAdapterFactory(object):
+    """Adapt an adapter factory to provide locatable untrusted adapters
 
-         >>> TM = TrustedAdapterFactory(M)
+    Untrusted adapters always adapt proxied objects. If any permission
+    other than zope.Public is required, untrusted adapters need a location
+    in order that the local authentication mechanism can be inovked
+    correctly.
 
-         >>> o2 = []
-         >>> o3 = []
+    If the adapter does not provide ILocation, we location proxy it and
+    set the parent. If the adapter does provide ILocation and 
+    it's __parent__ is None, we set the __parent__ to the adapter's
+    context only:
 
-         >>> a = TM(p, o2, o3)
-         >>> type(a).__name__
-         '_Proxy'
-         >>> a = removeSecurityProxy(a)
-         >>> a.context[0] is o, a.context[1] is o2, a.context[2] is o3
-         (True, True, True)
+    see adapter.txt
+    """
 
-         >>> a = TM(p, ProxyFactory(o2), ProxyFactory(o3))
-         >>> type(a).__name__
-         '_Proxy'
-         >>> a = removeSecurityProxy(a)
-         >>> a.context[0] is o, a.context[1] is o2, a.context[2] is o3
-         (True, True, True)
-
-       The __parent__ will be set to the first object if the adapter
-       is a location. M isn't a location, so the adapter has no
-       __parent__:
-
-         >>> a.__parent__
-         Traceback (most recent call last):
-         ...
-         AttributeError: 'M' object has no attribute '__parent__'
-
-       But if we create an adapter that is a Location:
-
-         >>> class L(A, Location):
-         ...     pass
-         >>> TL = TrustedAdapterFactory(L)
-
-       Then __parent__ will be set:
-
-         >>> TL(o).__parent__ is o
-         True
-         >>> removeSecurityProxy(TL(p)).__parent__ is o
-         True
-
-       The factory adapter has the __name__ and __module__ of the
-       factory it adapts:
-
-         >>> (TA.__module__, TA.__name__) == (A.__module__, A.__name__)
-         True
-
-       """
-
     def __init__(self, factory):
         self.factory = factory
         self.__name__ = factory.__name__
         self.__module__ = factory.__module__
 
     def __call__(self, *args):
-        for arg in args:
-            if removeSecurityProxy(arg) is not arg:
-                args = map(removeSecurityProxy, args)
-                adapter = self.factory(*args)
-                if (ILocation.providedBy(adapter)
-                    and adapter.__parent__ is None):
-                    adapter.__parent__ = args[0]
-                return ProxyFactory(adapter)
-
         adapter = self.factory(*args)
-        if (ILocation.providedBy(adapter)
-            and adapter.__parent__ is None):
-            adapter.__parent__ = args[0]
-        return adapter
+        return assertLocation(adapter, args[0])

Modified: Zope3/trunk/src/zwiki/configure.zcml
===================================================================
--- Zope3/trunk/src/zwiki/configure.zcml	2005-05-23 09:46:14 UTC (rev 30479)
+++ Zope3/trunk/src/zwiki/configure.zcml	2005-05-23 09:50:12 UTC (rev 30480)
@@ -169,7 +169,8 @@
       factory=".wikipage.WikiPageHierarchyAdapter"
       provides=".interfaces.IWikiPageHierarchy"
       for=".interfaces.IWikiPage"
-      trusted="true" />
+      trusted="true"
+      locate="true" />
 
   <class class=".wikipage.WikiPageHierarchyAdapter">
     <require



More information about the Zope3-Checkins mailing list