[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/ Added TrustedAdapterFactory

Jim Fulton jim at zope.com
Fri Jul 9 15:28:16 EDT 2004


Log message for revision 26363:
Added TrustedAdapterFactory

This is for adapters that are trusted and need unfettered access to
the objects they adapt. If asked to adapt security-proxied objects,
the return security-proxied adapters of unproxied objects.


-=-
Modified: Zope3/trunk/src/zope/app/component/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/component/metaconfigure.py	2004-07-09 19:24:42 UTC (rev 26362)
+++ Zope3/trunk/src/zope/app/component/metaconfigure.py	2004-07-09 19:28:16 UTC (rev 26363)
@@ -26,6 +26,7 @@
 from zope.app import zapi
 from zope.app.component.interface import queryInterface
 from zope.app.servicenames import Adapters, Presentation
+from zope.app.security.adapter import TrustedAdapterFactory
 
 PublicPermission = 'zope.Public'
 
@@ -120,7 +121,8 @@
                 args = ('', iface)
                 )
 
-def adapter(_context, factory, provides, for_, permission=None, name=''):
+def adapter(_context, factory, provides, for_, permission=None, name='',
+            trusted=False):
     if permission is not None:
         if permission == PublicPermission:
             permission = CheckerPublic
@@ -145,6 +147,9 @@
         # Store the original factory for documentation
         factory.factory = factories[0]
 
+    if trusted:
+        factory = TrustedAdapterFactory(factory)
+
     _context.action(
         discriminator = ('adapter', for_, provides, name),
         callable = handler,

Modified: Zope3/trunk/src/zope/app/component/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/component/metadirectives.py	2004-07-09 19:24:42 UTC (rev 26362)
+++ Zope3/trunk/src/zope/app/component/metadirectives.py	2004-07-09 19:28:16 UTC (rev 26363)
@@ -14,55 +14,58 @@
 """
 $Id$
 """
-from zope.interface import Interface
-from zope.configuration.fields import GlobalObject, Tokens, \
-     PythonIdentifier, MessageID
-from zope.schema import TextLine, Id
-from zope.app.security.fields import Permission
 
-class IBasicComponentInformation(Interface):
+import zope.configuration.fields
+import zope.interface
+import zope.schema
 
-    component = GlobalObject(
+import zope.app.security.fields
+
+class IBasicComponentInformation(zope.interface.Interface):
+
+    component = zope.configuration.fields.GlobalObject(
         title=u"Component to be used",
         required=False
         )
 
-    permission = Permission(
+    permission = zope.app.security.fields.Permission(
         title=u"Permission",
         required=False
         )
 
-    factory = GlobalObject(
+    factory = zope.configuration.fields.GlobalObject(
         title=u"Factory",
         required=False
         )
 
-class IBasicViewInformation(Interface):
+class IBasicViewInformation(zope.interface.Interface):
     """
     This is the basic information for all views.
     """
     
-    for_ = Tokens(
+    for_ = zope.configuration.fields.Tokens(
         title=u"Specifications of the objects to be viewed",
         description=u"""This should be a list of interfaces or classes
         """,
         required=True,
-        value_type=GlobalObject(missing_value=object())
+        value_type=zope.configuration.fields.GlobalObject(
+          missing_value=object(),
+          ),
         )
 
-    permission = Permission(
+    permission = zope.app.security.fields.Permission(
         title=u"Permission",
         description=u"The permission needed to use the view.",
         required=False
         )
 
-    class_ = GlobalObject(
+    class_ = zope.configuration.fields.GlobalObject(
         title=u"Class",
         description=u"A class that provides attributes used by the view.",
         required=False
         )
 
-    layer = TextLine(
+    layer = zope.schema.TextLine(
         title=u"The layer the view is in.",
         description=u"""
         A skin is composed of layers. It is common to put skin
@@ -71,7 +74,7 @@
         required=False
         )
 
-    allowed_interface = Tokens(
+    allowed_interface = zope.configuration.fields.Tokens(
         title=u"Interface that is also allowed if user has permission.",
         description=u"""
         By default, 'permission' only applies to viewing the view and
@@ -82,10 +85,10 @@
         Multiple interfaces can be provided, separated by
         whitespace.""",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-    allowed_attributes = Tokens(
+    allowed_attributes = zope.configuration.fields.Tokens(
         title=u"View attributes that are also allowed if user has permission.",
         description=u"""
         By default, 'permission' only applies to viewing the view and
@@ -93,118 +96,138 @@
         you can make the permission also apply to the extra attributes
         on the view object.""",
         required=False,
-        value_type=PythonIdentifier()
+        value_type=zope.configuration.fields.PythonIdentifier()
         )
 
-class IBasicResourceInformation(Interface):
+class IBasicResourceInformation(zope.interface.Interface):
     """
     Basic information for resources
     """
 
-    name = TextLine(
+    name = zope.schema.TextLine(
         title=u"The name of the resource.",
         description=u"The name shows up in URLs/paths. For example 'foo'.",
         required=True,
         default=u'',
         )
 
-    provides = GlobalObject(
+    provides = zope.configuration.fields.GlobalObject(
         title=u"The interface this component provides.",
         description=u"""
         A view can provide an interface.  This would be used for
         views that support other views.""",
         required=False,
-        default=Interface,
+        default=zope.interface.Interface,
         )
 
-    type = GlobalObject(
+    type = zope.configuration.fields.GlobalObject(
         title=u"Request type",
         required=True
         )
 
-class IInterfaceDirective(Interface):
+class IInterfaceDirective(zope.interface.Interface):
     """
     Define an interface
     """
     
-    interface = GlobalObject(
+    interface = zope.configuration.fields.GlobalObject(
         title=u"Interface",
         required=True
         )
 
-    type = GlobalObject(
+    type = zope.configuration.fields.GlobalObject(
         title=u"Interface type",
         required=False
         )
 
-class IAdapterDirective(Interface):
+class IAdapterDirective(zope.interface.Interface):
     """
     Register an adapter
     """
 
-    factory = Tokens(
+    factory = zope.configuration.fields.Tokens(
         title=u"Adapter factory/factories",
         description=u"""A list of factories (usually just one) that create the
         adapter instance.""",
         required=True,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-    provides = GlobalObject(
+    provides = zope.configuration.fields.GlobalObject(
         title=u"Interface the component provides",
         description=u"""This attribute specifes the interface the adapter
         instance must provide.""",
         required=True
         )
 
-    for_ = Tokens(
+    for_ = zope.configuration.fields.Tokens(
         title=u"Specifications to be adapted",
         description=u"""This should be a list of interfaces or classes
         """,
         required=True,
-        value_type=GlobalObject(missing_value=object())
+        value_type=zope.configuration.fields.GlobalObject(
+          missing_value=object(),
+          ),
         )
 
-    permission = Permission(
+    permission = zope.app.security.fields.Permission(
         title=u"Permission",
         description=u"""This adapter is only available, if the principal has
         this permission.""",
         required=False
         )
 
-    name = TextLine(
+    name = zope.schema.TextLine(
         title=u"Name",
-        description=u"""Adapters can have names. This attribute allows you to
+        description=u"""Adapters can have names.
+
+        This attribute allows you to
         specify the name for this adapter.""",
         required=False
         )
 
-class ISubscriberDirective(Interface):
+    trusted = zope.configuration.fields.Bool(
+        title=u"Trusted",
+        description=u"""Make the adapter a trusted adapter
+
+        Trusted adapters have unfettered access to the objects they
+        adapt.  If asked to adapt security-proxied objects, then,
+        rather than getting an unproxied adapter of security-proxied
+        objects, you get a security-proxied adapter of unproxied
+        objects.
+        """,
+        required=False,
+        default=False,
+        )
+
+class ISubscriberDirective(zope.interface.Interface):
     """
     Register a subscriber
     """
 
-    factory = GlobalObject(
+    factory = zope.configuration.fields.GlobalObject(
         title=u"Subscriber factory",
         description=u"A factory used to create the subscriber instance.",
         required=True
         )
 
-    provides = GlobalObject(
+    provides = zope.configuration.fields.GlobalObject(
         title=u"Interface the component provides",
         description=u"""This attribute specifes the interface the adapter
         instance must provide.""",
         required=False,
         )
 
-    for_ = Tokens(
+    for_ = zope.configuration.fields.Tokens(
         title=u"Interfaces or classes that this subscriber depends on",
         description=u"This should be a list of interfaces or classes",
         required=True,
-        value_type=GlobalObject(missing_value = object()),
+        value_type=zope.configuration.fields.GlobalObject(
+          missing_value = object(),
+          ),
         )
 
-    permission = Permission(
+    permission = zope.app.security.fields.Permission(
         title=u"Permission",
         description=u"""This subscriber is only available, if the principal has
         this permission.""",
@@ -216,32 +239,32 @@
     Register a utility
     """
 
-    provides = GlobalObject(
+    provides = zope.configuration.fields.GlobalObject(
         title=u"Interface the component provides",
         required=True
         )
 
-    name = TextLine(
+    name = zope.schema.TextLine(
         title=u"Name",
         required=False
         )
 
-class IFactoryDirective(Interface):
+class IFactoryDirective(zope.interface.Interface):
     """
     Define a factory
     """
 
-    component = GlobalObject(
+    component = zope.configuration.fields.GlobalObject(
         title=u"Component to be used",
         required=True
         )
     
-    id = TextLine(
+    id = zope.schema.TextLine(
         title=u"ID",
         required=False
         )
 
-    title = MessageID(
+    title = zope.configuration.fields.MessageID(
         title=u"Title",
         description=u"""
         text suitable for use in the 'add content' menu of a
@@ -249,7 +272,7 @@
         required=False
         )
 
-    description = MessageID(
+    description = zope.configuration.fields.MessageID(
         title=u"Description",
         description=u"Longer narrative description of what this factory does",
         required=False
@@ -261,10 +284,10 @@
     Register a view for a component
     """
 
-    factory = Tokens(
+    factory = zope.configuration.fields.Tokens(
         title=u"Factory",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
 class IDefaultViewDirective(IBasicResourceInformation):
@@ -275,7 +298,7 @@
     explicitly).
     """
 
-    for_ = GlobalObject(
+    for_ = zope.configuration.fields.GlobalObject(
         title=u"The interface this view is the default for.",
         description=u"""
         Specifies the interface for which the default view is declared. All
@@ -293,32 +316,32 @@
     Register a resource
     """
     
-    layer = TextLine(
+    layer = zope.schema.TextLine(
         title=u"The layer the resource is in.",
         required=False
         )
 
-    allowed_interface = Tokens(
+    allowed_interface = zope.configuration.fields.Tokens(
         title=u"Interface that is also allowed if user has permission.",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-    allowed_attributes = Tokens(
+    allowed_attributes = zope.configuration.fields.Tokens(
         title=u"View attributes that are also allowed if user has permission.",
         required=False,
-        value_type=PythonIdentifier()
+        value_type=zope.configuration.fields.PythonIdentifier()
         )
 
 
-class IServiceTypeDirective(Interface):
+class IServiceTypeDirective(zope.interface.Interface):
 
-    id = TextLine(
+    id = zope.schema.TextLine(
         title=u"ID of the service type",
         required=True
         )
 
-    interface = GlobalObject(
+    interface = zope.configuration.fields.GlobalObject(
         title=u"Interface of the service type",
         required=True
         )
@@ -328,40 +351,40 @@
     Register a service
     """
 
-    serviceType = TextLine(
+    serviceType = zope.schema.TextLine(
         title=u"ID of service type",
         required=True
         )
 
-class IClassDirective(Interface):
+class IClassDirective(zope.interface.Interface):
     """
     Make statements about a class
     """
 
-    class_ = GlobalObject(
+    class_ = zope.configuration.fields.GlobalObject(
         title=u"Class",
         required=True
         )
 
-class IImplementsSubdirective(Interface):
+class IImplementsSubdirective(zope.interface.Interface):
     """
     Declare that the class given by the content directive's class
     attribute implements a given interface
     """
 
-    interface = Tokens(
+    interface = zope.configuration.fields.Tokens(
         title=u"One or more interfaces",
         required=True,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-class IRequireSubdirective(Interface):
+class IRequireSubdirective(zope.interface.Interface):
     """
     Indicate that the a specified list of names or the names in a
     given Interface require a given permission for access.
     """
 
-    permission = Permission(
+    permission = zope.app.security.fields.Permission(
         title=u"Permission",
         description=u"""
         Specifies the permission by id that will be required to
@@ -369,39 +392,39 @@
         required=False
         )
 
-    attributes = Tokens(
+    attributes = zope.configuration.fields.Tokens(
         title=u"Attributes and methods",
         description=u"""
         This is a list of attributes and methods that can be accessed.""",
         required=False,
-        value_type=PythonIdentifier()
+        value_type=zope.configuration.fields.PythonIdentifier()
         )
         
-    set_attributes = Tokens(
+    set_attributes = zope.configuration.fields.Tokens(
         title=u"Attributes that can be set",
         description=u"""
         This is a list of attributes that can be modified/mutated.""",
         required=False,
-        value_type=PythonIdentifier()
+        value_type=zope.configuration.fields.PythonIdentifier()
         )
 
-    interface = Tokens(
+    interface = zope.configuration.fields.Tokens(
         title=u"Interfaces",
         description=u"""
         The listed interfaces' methods and attributes can be accessed.""",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-    set_schema = Tokens(
+    set_schema = zope.configuration.fields.Tokens(
         title=u"The attributes specified by the schema can be set",
         description=u"""
         The listed schemas' properties can be modified/mutated.""",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-    like_class = GlobalObject(
+    like_class = zope.configuration.fields.GlobalObject(
         title=u"Configure like this class",
         description=u"""
         This argument says that this content class should be configured in the
@@ -410,31 +433,31 @@
         required=False
         )
     
-class IAllowSubdirective(Interface):
+class IAllowSubdirective(zope.interface.Interface):
     """
     Declare a part of the class to be publicly viewable (that is,
     requires the zope.Public permission). Only one of the following
     two attributes may be used.
     """
 
-    attributes = Tokens(
+    attributes = zope.configuration.fields.Tokens(
         title=u"Attributes",
         required=False,
-        value_type=PythonIdentifier()
+        value_type=zope.configuration.fields.PythonIdentifier()
         )
 
-    interface = Tokens(
+    interface = zope.configuration.fields.Tokens(
         title=u"Interface",
         required=False,
-        value_type=GlobalObject()
+        value_type=zope.configuration.fields.GlobalObject()
         )
 
-class IFactorySubdirective(Interface):
+class IFactorySubdirective(zope.interface.Interface):
     """
     Specify the factory used to create this content object
     """
 
-    id = TextLine(
+    id = zope.schema.TextLine(
         title=u"ID",
         description=u"""
         the identifier for this factory in the ZMI factory
@@ -443,7 +466,7 @@
         required=False
         )
 
-    title = MessageID(
+    title = zope.configuration.fields.MessageID(
         title=u"Title",
         description=u"""
         text suitable for use in the 'add content' menu of a
@@ -451,7 +474,7 @@
         required=False
         )
 
-    description = MessageID(
+    description = zope.configuration.fields.MessageID(
         title=u"Description",
         description=u"Longer narrative description of what this factory does",
         required=False

Modified: Zope3/trunk/src/zope/app/component/tests/test_directives.py
===================================================================
--- Zope3/trunk/src/zope/app/component/tests/test_directives.py	2004-07-09 19:24:42 UTC (rev 26362)
+++ Zope3/trunk/src/zope/app/component/tests/test_directives.py	2004-07-09 19:28:16 UTC (rev 26363)
@@ -195,6 +195,42 @@
 
         self.assertEqual(IApp(Content()).__class__, Comp)
 
+    def testTrustedAdapter(self):
+        # Full import is critical!
+        from zope.component.tests.components import Content
+        from zope.app.component.tests.adapter import A1, I1
+
+        xmlconfig(StringIO(template % (
+            """
+            <adapter
+              factory="zope.app.component.tests.adapter.A1"
+              provides="zope.app.component.tests.adapter.I1"
+              for="zope.component.tests.components.IContent"
+              trusted="yes"
+              />
+            """
+            )))
+
+        # With an unproxied object, busoness as usual
+        ob = Content()
+        self.assertEqual(type(I1(ob)), type(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)
+
+        # around an unproxied object:
+        from zope.security.proxy import getProxiedObject
+        a = getProxiedObject(a)
+        a.context[0] is ob
+        
+        
+
     def testAdapter_w_multiple_factories(self):
         from zope.app.component.tests.adapter import A1, A2, A3
         from zope.component.tests.components import Content, IApp

Added: Zope3/trunk/src/zope/app/security/adapter.py
===================================================================
--- Zope3/trunk/src/zope/app/security/adapter.py	2004-07-09 19:24:42 UTC (rev 26362)
+++ Zope3/trunk/src/zope/app/security/adapter.py	2004-07-09 19:28:16 UTC (rev 26363)
@@ -0,0 +1,143 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Support for taking security into account in adaptation
+
+$Id$
+"""
+
+from zope.security.checker import ProxyFactory
+from zope.security.proxy import getProxiedObject
+from zope.app.location import ILocation, Location
+
+class TrustedAdapterFactory(object):
+    """Adapt an adapter factory to to provide trusted adapters
+
+       Trusted adapters always adapt unproxied objects.  If asked to
+       adapt any proxid objects, it will unproxy them and then proxy the
+       resulting adapter.
+
+       Suppose we have an adapter factory:
+
+         >>> class A(object):
+         ...     def __init__(self, context):
+         ...         self.context = context
+
+       Now, suppose have an object and proxy it:
+
+         >>> o = []
+         >>> p = ProxyFactory(o)
+
+       If we adapt it:
+
+         >>> a = A(p)
+
+       the result is not a proxy:
+
+         >>> type(a).__name__
+         'A'
+
+       But the object it adapts still is:
+
+         >>> type(a.context).__name__
+         '_Proxy'
+
+       Now, will we'll adapt our adapter factory to a trusted adapter factory:
+
+         >>> TA = TrustedAdapterFactory(A)
+
+       and if we use it:
+
+         >>> a = TA(p)
+
+       then the adapter is proxied:
+
+         >>> type(a).__name__
+         '_Proxy'
+
+       And the object proxied is not.  (We actually have to remove the
+       adapter to get to the adapted object in this case.)
+
+         >>> a = getProxiedObject(a)
+         >>> type(a.context).__name__
+         'list'
+
+       This works with multiple objects too:
+
+         >>> class M(object):
+         ...     def __init__(self, *context):
+         ...         self.context = context
+
+         >>> TM = TrustedAdapterFactory(M)
+
+         >>> o2 = []
+         >>> o3 = []
+
+         >>> a = TM(p, o2, o3)
+         >>> type(a).__name__
+         '_Proxy'
+         >>> a = getProxiedObject(a)
+         >>> a.context[0] is o, a.context[1] is o2, a.context[2] is o3
+         (True, True, True)
+
+         >>> a = TM(p, ProxyFactory(o2), ProxyFactory(o3))
+         >>> type(a).__name__
+         '_Proxy'
+         >>> a = getProxiedObject(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
+         >>> getProxiedObject(TL(p)).__parent__ is o
+         True
+
+       """
+
+    __slots__ = ('factory', )
+
+    def __init__(self, factory):
+        self.factory = factory
+
+    def __call__(self, *args):
+        for arg in args:
+            if getProxiedObject(arg) is not arg:
+                args = map(getProxiedObject, 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


Property changes on: Zope3/trunk/src/zope/app/security/adapter.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/app/security/tests/test_adapter.py
===================================================================
--- Zope3/trunk/src/zope/app/security/tests/test_adapter.py	2004-07-09 19:24:42 UTC (rev 26362)
+++ Zope3/trunk/src/zope/app/security/tests/test_adapter.py	2004-07-09 19:28:16 UTC (rev 26363)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('zope.app.security.adapter'),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: Zope3/trunk/src/zope/app/security/tests/test_adapter.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list