[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