[Zope-Checkins] CVS: Zope3/lib/python/Zope/Security - Checker.py:1.2 IChecker.py:1.2 ISecurityContext.py:1.2 ISecurityManagement.py:1.2 ISecurityManager.py:1.2 ISecurityPolicy.py:1.2 ISecurityProxyFactory.py:1.2 Proxy.py:1.2 RestrictedBuiltins.py:1.2 RestrictedInterpreter.py:1.2 SecurityContext.py:1.2 SecurityManagement.py:1.2 SecurityManager.py:1.2 SimpleSecurityPolicies.py:1.2 _Proxy.c:1.2 __init__.py:1.2 setup.py:1.2

Jim Fulton jim@zope.com
Mon, 10 Jun 2002 19:30:06 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/Security
In directory cvs.zope.org:/tmp/cvs-serv20468/lib/python/Zope/Security

Added Files:
	Checker.py IChecker.py ISecurityContext.py 
	ISecurityManagement.py ISecurityManager.py ISecurityPolicy.py 
	ISecurityProxyFactory.py Proxy.py RestrictedBuiltins.py 
	RestrictedInterpreter.py SecurityContext.py 
	SecurityManagement.py SecurityManager.py 
	SimpleSecurityPolicies.py _Proxy.c __init__.py setup.py 
Log Message:
Merged Zope-3x-branch into newly forked Zope3 CVS Tree.

=== Zope3/lib/python/Zope/Security/Checker.py 1.1 => 1.2 ===
+from Zope.Exceptions \
+     import Unauthorized, ForbiddenAttribute, Forbidden, DuplicationError
+from Interface.IInterface import IInterface
+from Interface import Interface
+from _Proxy import _Proxy as Proxy
+from ISecurityProxyFactory import ISecurityProxyFactory
+from Zope.Security.SecurityManagement import getSecurityManager
+import sys, os, types
+
+if os.environ.get('ZOPE_WATCH_CHECKERS'):
+    WATCH_CHECKERS = 1
+else:
+    WATCH_CHECKERS = 0
+
+
+# Marker for public attributes
+CheckerPublic = object()
+
+def ProxyFactory(object, checker=None):
+    """Factory function that creats a proxy for an object
+
+    The proxy checker is looked up if not provided.
+    """
+    
+    if checker is None:        
+        checker = getattr(object, '__Security_checker__', None)
+    
+    if checker is None:        
+
+        checker = selectChecker(object)
+        if checker is None:
+            return object
+
+    else:
+        # Maybe someone passed us a proxy and a checker
+        if type(object) is Proxy:
+            # XXX should we keep the existing proxy or create a new one.
+            return object
+
+    return Proxy(object, checker)
+
+ProxyFactory.__implements__ = ISecurityProxyFactory
+
+class Checker:
+
+    __implements__ =  IChecker
+
+    def __init__(self, permission_func):
+        """Create a checker
+
+        A callable must be provided for computing permissions for
+        names. The callable will be called with attribute names and
+        must return a permission id, None, or the special marker,
+        CheckerPublic. If None is returned, then access to the name is
+        forbidden. If CheckerPublic is returned, then access will be
+        granted without checking a permission.
+        """
+
+        self.__permission_func = permission_func
+
+
+    def getPermission_func(self):
+        return self.__permission_func
+
+    def permission_id(self, name):
+        """Return the result of calling the permission func
+        """
+        return self.__permission_func(name)
+
+    ############################################################
+    # Implementation methods for interface
+    # Zope.Security.IChecker.
+
+    def check_getattr(self, object, name):
+        'See Zope.Security.IChecker.IChecker'
+        self.check(object, name)
+
+    def check_setattr(self, object, name):
+        'See Zope.Security.IChecker.IChecker'
+        # YAGNI
+        raise Forbidden("Can't set attributes in untrusted code.")
+
+    def check(self, object, name):
+        'See Zope.Security.IChecker.IChecker'
+
+        if WATCH_CHECKERS:
+            print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
+
+        # We have the information we need already
+        permission = self.__permission_func(name)
+        if permission:
+            if permission is CheckerPublic:
+                if WATCH_CHECKERS:
+                    print >> sys.stderr, 'Public.'
+                return # Public
+            manager = getSecurityManager()
+            if manager.checkPermission(permission, object):
+                if WATCH_CHECKERS:
+                    print >> sys.stderr, 'Granted.'
+                return
+            else:
+                if WATCH_CHECKERS:
+                    print >> sys.stderr, 'Unauthorized.'
+                raise Unauthorized(name=name)
+        elif name in _always_available:
+            if WATCH_CHECKERS:
+                print >> sys.stderr, 'Always available.'
+            return
+
+        if WATCH_CHECKERS:
+            print >> sys.stderr, 'Forbidden.'
+
+        raise ForbiddenAttribute(name)
+
+    def proxy(self, value):
+        'See Zope.Security.IChecker.IChecker'
+        # Now we need to create a proxy
+
+        checker = getattr(value, '__Security_checker__', None)
+        if checker is None:
+            checker = selectChecker(value)
+            if checker is None:
+                return value
+
+        return Proxy(value, checker)
+
+    #
+    ############################################################
+
+def NamesChecker(names=(), permission_id=CheckerPublic, **__kw__):
+    """Return a checker that grants access to a set of names.
+
+    A sequence of names is given as the first argument. If a second
+    argument, permission_id, is given, it is the permission required
+    to access the names.  Additional names and persmission ids can be
+    supplied as keyword arguments.
+    """
+
+    data = {}
+    data.update(__kw__)
+    for name in names:
+        if data.get(name, permission_id) is not permission_id:
+            raise DuplicationError(name)
+        data[name] = permission_id
+
+    return Checker(data.get)
+
+def InterfaceChecker(interface, permission_id=CheckerPublic):
+    return NamesChecker(interface.names(1), permission_id)
+
+def MultiChecker(specs):
+    """Create a checker from a sequence of specifications
+
+    A specification is:
+
+    - A two-tuple with:
+
+      o a sequence of names or an interface
+
+      o a permission id
+
+      All the names in the sequence of names or the interface are
+      protected by the permission.
+
+    - A dictionoid (having anitems method), with items that are
+      name/permission-id pairs.
+    """
+    data = {}
+
+    for spec in specs:
+        if type(spec) is tuple:
+            names, permission_id = spec
+            if IInterface.isImplementedBy(names):
+                names = names.names(1)
+            for name in names:
+                if data.get(name, permission_id) is not permission_id:
+                    raise DuplicationError(name)
+                data[name] = permission_id
+        else:
+            for name, permission_id in spec.items():
+                if data.get(name, permission_id) is not permission_id:
+                    raise DuplicationError(name)
+                data[name] = permission_id
+
+    return Checker(data.get)
+
+def NonPrivateChecker(permission_id = CheckerPublic):
+
+    def check(name, permission_id=permission_id):
+        if name.startswith('_'):
+            return None
+        return permission_id
+
+    return Checker(check)
+
+
+def selectChecker(object):
+    """Get a checker for the given object
+
+    The appropriate checker is returned or None is returned. If the
+    return value is None, then object should not be wrapped in a proxy.
+    """
+    checker = _getChecker(type(object), _defaultChecker)
+    if checker is NoProxy:
+        return None
+
+    while not isinstance(checker, Checker):
+        checker = checker(object)
+        if checker is NoProxy or checker is None:
+            return None
+    
+    return checker
+
+def getCheckerForInstancesOf(class_):
+    return _checkers.get(class_)
+
+def defineChecker(type_, checker):
+    """Define a checker for a given type of object
+
+    The checker can be a Checker, or a function that, when called with
+    an object, returns a Checker.
+    """
+    if type_ in _checkers:
+        raise DuplicationError(type_)
+    _checkers[type_] = checker    
+    
+
+NoProxy = object()
+
+# _checkers is a mapping.
+#
+#  - Keys are types
+#
+#  - Values are
+#
+#    o None => rock
+#    o a Checker
+#    o a function returning None or a Checker
+#
+_checkers = {}
+_getChecker = _checkers.get
+
+_defaultChecker = Checker({}.get)
+
+def _instanceChecker(inst):
+    checker = _checkers.get(inst.__class__, _defaultChecker)
+    if checker is _defaultChecker and isinstance(inst, Exception):
+        return NoProxy # XXX we should be more careful
+    return checker
+
+def _classChecker(class_):
+    checker = _checkers.get(class_, _typeChecker)
+    if checker is _typeChecker and issubclass(class_, Exception):
+        return NoProxy # XXX we should be more careful
+
+    return checker
+
+def _moduleChecker(module):
+    return _checkers.get(module, _typeChecker)
+
+
+
+_always_available = ['__lt__', '__le__', '__eq__',
+                     '__gt__', '__ge__', '__ne__',
+                     '__hash__', '__nonzero__',
+                     '__class__', '__implements__',
+                     ]
+
+_callableChecker = NamesChecker(['__str__', '__repr__', '__name__',
+                                 '__call__'])
+_typeChecker = NamesChecker(['__str__', '__repr__', '__name__', '__module__',
+                             '__bases__'])
+
+_interfaceChecker = NamesChecker(['__str__', '__repr__', '__name__',
+                                  '__module__', '__bases__',
+                                  'isImplementedBy', 'extends'])
+
+BasicTypes = {
+    object: NoProxy,
+    int: NoProxy,
+    float: NoProxy,
+    long: NoProxy,
+    complex: NoProxy,
+    types.NoneType: NoProxy,
+    str: NoProxy,
+    unicode: NoProxy,
+    type(not 1): NoProxy, # Boolean, if available :)
+}
+
+_default_checkers = {
+    dict: NamesChecker(['__getitem__', '__len__', '__iter__',
+                        'get', 'has_key', '__copy__',
+                        'keys', 'values', 'items',
+                        'iterkeys', 'iteritems', 'itervalues', '__contains__',
+                        ]),
+    list: NamesChecker(['__getitem__', '__getslice__', '__len__', '__iter__',
+                        '__contains__', 'index', 'count']),
+
+    # YAGNI: () a rock
+    tuple: NamesChecker(['__getitem__', '__getslice__',
+                         '__contains__', '__len__', '__iter__']),
+    types.InstanceType: _instanceChecker,
+    Proxy: NoProxy,
+    types.ClassType: _classChecker,
+    types.FunctionType: _callableChecker,
+    types.MethodType: _callableChecker,
+    types.BuiltinFunctionType: _callableChecker,
+    types.BuiltinMethodType: _callableChecker,
+    type: _typeChecker,
+    types.ModuleType: _moduleChecker,
+    type(iter([])): NamesChecker(['next']),
+    type(Interface): _interfaceChecker,
+    }
+
+
+def _clear():
+    _checkers.clear()
+    _checkers.update(_default_checkers)
+    _checkers.update(BasicTypes)
+
+_clear()
+
+from Zope.Testing.CleanUp import addCleanUp
+addCleanUp(_clear)


=== Zope3/lib/python/Zope/Security/IChecker.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Interface import Interface
+
+class IChecker(Interface):
+    """Security-proxy plugin objects that implement low-level checks
+
+    The checker is responsible for creating proxies for
+    operation return values, via the proxy method.
+
+    There are check_getattr() and check_setattr() methods for checking
+    getattr and setattr, and a check() method for all other operations.
+
+    The check methods may raise errors.  They return no value.
+
+    Example (for __getitem__):
+
+           checker.check(ob, "__getitem__")
+           return checker.proxy(ob[key])
+    """
+
+    def check_getattr(ob, name):
+        """Check whether attribute access is allowed.
+        """
+
+    def check_setattr(ob, name):
+        """Check whether attribute assignment is allowed.
+        """
+
+    def check(ob, operation):
+        """Check whether operation is allowed.
+
+        The operation name is the Python special method name,
+        e.g. "__getitem__".
+        """
+
+    def proxy(value):
+        """Return a security proxy for the value.
+        """


=== Zope3/lib/python/Zope/Security/ISecurityContext.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from Interface import Interface
+from Interface.Attribute import Attribute
+
+class ISecurityContext( Interface ):
+    """
+        Capture transient request-specific security information.
+    """
+    Attribute( 'stack'
+             , 'A stack of elements, each either be an ExecutableObject'
+               'or a tuple consisting of an ExecutableObject and a'
+               'custom SecurityPolicy.'
+             )
+
+    Attribute( 'principal'
+             , 'The AUTHENTICATED_USER for the request.'
+             )
+


=== Zope3/lib/python/Zope/Security/ISecurityManagement.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from Interface import Interface
+
+class ISecurityManagementSetup( Interface ):
+    """
+        Infrastructure (including tests, etc.) calls these things to
+        tweak the security manager.
+    """
+    def newSecurityManager( user ):
+        """
+            Install a new SecurityManager, using user.  Return the
+            old SecurityManager, if any, or None.
+        """
+
+    def replaceSecurityManager( old_manager ):
+        """
+            Replace the SecurityManager with 'old_manager', which
+            must implement ISecurityManager.
+        """
+
+    def noSecurityManager():
+        """
+            Clear any existing SecurityManager.
+        """
+
+class ISecurityManagement( Interface ):
+    """
+        "Public" SM API.
+    """
+    def getSecurityManager():
+        """
+            Get a SecurityManager (create if needed).
+        """
+
+    def setSecurityPolicy( aSecurityPolicy ):
+        """
+            Set the system default security policy. 
+
+            This method should only be caused by system startup code.
+            It should never, for example, be called during a web request.
+        """


=== Zope3/lib/python/Zope/Security/ISecurityManager.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from Interface import Interface
+
+# XXX This interface has too muct Zope application dependence. This
+# needs to be refactored somehow.
+
+class ISecurityManager( Interface ):
+    """
+        A security manager provides methods for checking access and managing
+        executable context and policies.
+    """
+
+    def getPrincipal():
+        """
+            Return the authenticated principal. 
+
+            This is equivalent to something like::
+
+            REQUEST['AUTHENTICATED_USER']
+
+            but is a bit cleaner, especially if 'REQUEST' isn't handy.
+        """
+
+    def checkPermission( permission, object ):
+        """
+            Check whether the security context allows the given
+            permission on the given object. Return a boolean value.
+
+            Arguments:
+
+            permission -- A permission name
+
+            object -- The object being accessed according to the permission
+        """
+
+    def pushExecutable( anExecutableObject ):
+        """
+            Push an ExecutableObject onto the manager's stack, and
+            activate it's custom security policy, if any.
+        """
+
+    def popExecutable( anExecutableObject ):
+        """
+            Pop the topmost ExecutableObject from the stack, deactivating
+            any custom security policy it might have installed.
+        """
+
+    def calledByExecutable():
+        """
+            Return a boolean indicating whether the current request has
+            invoked any IExecutableObjects.
+
+            This can be used to determine if an object was called 
+            (more or less) directly from a URL, or if it was called by
+            through-the-web provided code.
+        """


=== Zope3/lib/python/Zope/Security/ISecurityPolicy.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from Interface import Interface
+
+class ISecurityPolicy( Interface ):
+
+    def checkPermission( permission
+                       , object
+                       , context
+                       ):
+        """
+            Check whether the security context allows the given permission on
+            the given object, returning a boolean value.
+
+            Arguments:
+
+            permission -- A permission name
+
+            object -- The object being accessed according to the permission
+
+            context -- A SecurityContext, which provides access to information
+            shuch as the context stack and AUTHENTICATED_USER.
+        """


=== Zope3/lib/python/Zope/Security/ISecurityProxyFactory.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Interface import Interface
+
+class ISecurityProxyFactory(Interface):
+
+    def __call__(object, checker=None):
+        """Create a security proxy
+
+        If a checker is given, then use it, otherwise, try to figure
+        out a checker.
+
+        If the object is already a security proxy, then it will be
+        returned.
+        """
+


=== Zope3/lib/python/Zope/Security/Proxy.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from _Proxy import getObject, getChecker
+from _Proxy import _Proxy as Proxy
+from Checker import ProxyFactory, Checker as _trustedChecker
+
+def trustedRemoveSecurityProxy(object):
+    if ((type(object) is Proxy) and
+        isinstance(getChecker(object), _trustedChecker)
+        ):
+        return getObject(object)
+
+    return object
+
+
+def getTestProxyItems(proxy):
+    """Try to get checker names and permissions for testing
+
+    If this succeeds, a sorted sequence of items is returned,
+    otherwise, None is retirned.
+    """
+    checker = getChecker(proxy)
+    func = checker.getPermission_func()
+    dict = getattr(func, '__self__', None)
+    if dict is None:
+        return None
+    items = dict.items()
+    items.sort()
+    return items
+    


=== Zope3/lib/python/Zope/Security/RestrictedBuiltins.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2002 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
+# 
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+import sys
+
+def RestrictedBuiltins():
+
+    from Proxy import ProxyFactory
+    from Checker import NamesChecker
+
+    # It's better to say what is safe than it say what is not safe
+    _safe = [
+        'ArithmeticError', 'AssertionError', 'AttributeError',
+        'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError',
+        'Exception', 'FloatingPointError', 'IOError', 'ImportError',
+        'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
+        'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',
+        'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning',
+        'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError',
+        'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
+        'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError',
+        'UnicodeError', 'UserWarning', 'ValueError', 'Warning',
+        'ZeroDivisionError',
+        '__debug__', '__doc__', '__name__', 'abs', 'apply', 'buffer',
+        'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile',
+        'complex', 'copyright', 'credits', 'delattr', 'dict',
+        'divmod', 'eval', 'filter', 'float', 'getattr', 'globals',
+        'hasattr', 'hash', 'hex', 'id', 'int', 'isinstance',
+        'issubclass', 'iter', 'len', 'license', 'list', 'locals',
+        'long', 'map', 'max', 'min', 'object', 'oct', 'ord', 'pow',
+        'property', 'quit', 'range', 'reduce', 'repr', 'round',
+        'setattr', 'slice', 'staticmethod', 'str', 'super', 'tuple',
+        'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip',
+        ]
+
+    # XXX dir segfaults with a seg fault due to a bas tuple check in
+    # merge_class_dict in object.c. The assert macro seems to be doing
+    # the wrong think. Basically, if an object has bases, then bases
+    # is assumed to be a tuple.
+    
+    # Anything that accesses an external file is a no no:
+    # 'open', 'execfile', 'file'
+
+    # We dont want restricted code to call exit: 'SystemExit', 'exit'
+
+    # Other no nos:
+    #    help prints
+    #    input does I/O
+    #    raw_input does I/O
+    #    intern's effect is too global
+    #    reload does import, XXX doesn't it use __import__?
+
+    _builtinTypeChecker = NamesChecker(
+        ['__str__', '__repr__', '__name__', '__module__',
+         '__bases__', '__call__'])
+
+    import __builtin__
+
+    builtins = {}
+    for name in _safe:
+        value = getattr(__builtin__, name)
+        if isinstance(value, type):
+            value = ProxyFactory(value, _builtinTypeChecker)
+        else:
+            value = ProxyFactory(value)
+        builtins[name] = value
+
+    def __import__(name, globals=None, locals=None, fromlist=()):
+        # Waaa, we have to emulate __import__'s weird semantics.
+        try:
+            module = sys.modules[name]
+            if fromlist:
+                return module
+
+            l = name.find('.')
+            if l < 0:
+                return module
+
+            return sys.modules[name[:l]]                
+            
+        except KeyError:
+            raise ImportError(name)
+
+    builtins['__import__'] = ProxyFactory(__import__)
+
+    return builtins
+
+RestrictedBuiltins = RestrictedBuiltins()


=== Zope3/lib/python/Zope/Security/RestrictedInterpreter.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2002 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
+# 
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+import sys
+
+from Proxy import ProxyFactory
+from RestrictedBuiltins import RestrictedBuiltins
+
+class RestrictedInterpreter:
+
+    def __init__(self):
+        self.globals = {'__builtins__' : RestrictedBuiltins}
+
+    def ri_exec(self, code):
+        # what is the type of code?
+        exec code in self.globals


=== Zope3/lib/python/Zope/Security/SecurityContext.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+""" Default ISecurityContext impl """
+
+from ISecurityContext import ISecurityContext
+
+class SecurityContext:
+    """
+        Capture transient request-specific security information.
+
+        Attribute( 'stack'
+                , 'A stack of elements, each either be an ExecutableObject'
+                'or a tuple consisting of an ExecutableObject and a'
+                'custom SecurityPolicy.'
+                )
+
+        Attribute( 'user'
+                , 'The AUTHENTICATED_USER for the request.'
+                )
+    """
+
+    def __init__( self, user ):
+
+        self.stack       = []
+        self.user        = user
+        self.objectCache = {}


=== Zope3/lib/python/Zope/Security/SecurityManagement.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+""" Default ISecurityManagement implementation """
+
+from ISecurityManagement import ISecurityManagement, ISecurityManagementSetup
+from SecurityManager import SecurityManager
+from SecurityManager import setSecurityPolicy as _setSecurityPolicy
+from SecurityContext import SecurityContext
+
+__implements__ = ( ISecurityManagement, ISecurityManagementSetup )
+
+try:
+    import thread
+except:
+    get_ident=lambda: 0
+else:
+    get_ident=thread.get_ident
+
+_managers={}
+
+from Zope.Testing.CleanUp import addCleanUp
+addCleanUp(_managers.clear)
+
+
+#
+#   ISecurityManagementSetup implementation
+#
+def newSecurityManager( user ):
+    """
+        Install a new SecurityManager, using user.  Return the
+        old SecurityManager, if any, or None.
+    """
+    return replaceSecurityManager( SecurityManager( SecurityContext( user ) ) )
+
+def replaceSecurityManager( old_manager ):
+    """
+        Replace the SecurityManager with 'old_manager', which
+        must implement ISecurityManager.
+    """
+    thread_id = get_ident()
+    old = _managers.get( thread_id, None )
+    _managers[ thread_id ] = old_manager
+    return old
+
+def noSecurityManager():
+    """
+        Clear any existing SecurityManager.
+    """
+    try:
+        del _managers[ get_ident() ]
+    except KeyError:
+        pass
+
+#
+#   ISecurityManagement implementation
+#
+def getSecurityManager():
+    """
+        Get a SecurityManager (create if needed).
+    """
+    thread_id = get_ident()
+    manager=_managers.get( thread_id, None )
+
+    if manager is None:
+        newSecurityManager( None )
+        manager=_managers.get( thread_id, None )
+        
+    return manager
+
+def setSecurityPolicy( aSecurityPolicy ):
+    """
+        Set the system default security policy, and return the previous
+        value.
+
+        This method should only be caused by system startup code.
+        It should never, for example, be called during a web request.
+    """
+    return _setSecurityPolicy( aSecurityPolicy )
+


=== Zope3/lib/python/Zope/Security/SecurityManager.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+""" Default ISecurityManager implementation """
+
+import os
+from SimpleSecurityPolicies import ParanoidSecurityPolicy
+
+MAX_STACK_SIZE = 100
+
+_defaultPolicy = ParanoidSecurityPolicy()
+
+def _clear():
+    global _defaultPolicy
+    _defaultPolicy = ParanoidSecurityPolicy()
+
+from Zope.Testing.CleanUp import addCleanUp
+addCleanUp(_clear)
+
+
+def setSecurityPolicy( aSecurityPolicy ):
+    """
+        Set the system default security policy. 
+
+        This method should only be caused by system startup code. It should
+        never, for example, be called during a web request.
+    """
+    global _defaultPolicy
+
+    last, _defaultPolicy = _defaultPolicy, aSecurityPolicy
+
+    return last
+
+from ISecurityManager import ISecurityManager
+
+class SecurityManager:
+    """
+        A security manager provides methods for checking access and managing
+        executable context and policies.
+    """
+    __implements__ = ISecurityManager
+    
+    def __init__( self, context ):
+        self._context = context
+        self._policy = None
+
+    def _getPolicy( self ):
+        """
+            Find current policy, or default.
+        """
+        policy = self._policy
+        if policy is None:
+            policy = _defaultPolicy
+        return policy
+
+    #
+    #   ISecurityManager implementation
+    #
+    def getPrincipal( self ):
+        """
+            Return the authenticated user. 
+
+            This is equivalent to something like::
+
+            REQUEST['AUTHENTICATED_USER']
+
+            but is a bit cleaner, especially if 'REQUEST' isn't handy.
+        """
+        return self._context.user
+
+    def checkPermission( self, permission, object ):
+        """
+            Check whether the security context allows the given
+            permission on the given object. Return a boolean value.
+
+            Arguments:
+
+            permission -- A permission name
+
+            object -- The object being accessed according to the permission
+        """
+        return self._getPolicy().checkPermission( permission, object
+                                              , self._context )
+
+    def pushExecutable( self, anExecutableObject ):
+        """
+            Push an ExecutableObject onto the manager's stack, and
+            activate it's custom security policy, if any.
+        """
+        stack=self._context.stack
+
+        if len( stack ) >= MAX_STACK_SIZE:
+            raise SystemError, 'Excessive recursion'
+
+        stack.append( anExecutableObject )
+        p = getattr( anExecutableObject, '_customSecurityPolicy', None )
+
+        if p is not None:
+            p = p()
+
+        self._policy = p
+
+    def popExecutable( self, anExecutableObject ):
+        """
+            Pop the topmost ExecutableObject from the stack, deactivating
+            any custom security policy it might have installed.
+        """
+        stack=self._context.stack
+
+        if not stack:
+            return
+
+        top = stack[-1]
+
+        if top is anExecutableObject:
+            del stack[-1]
+        else:
+            indexes = range(len(stack))
+            indexes.reverse()
+            for i in indexes:
+                top=stack[i]
+                if top is anExecutableObject:
+                    del stack[i:]
+                    break
+            else:
+                return
+
+        if stack:
+
+            top = stack[-1]
+            p = getattr( top, '_customSecurityPolicy', None )
+
+            if p is not None:
+                p=p()
+            self._policy=p
+
+        else:
+            self._policy=None
+
+    def calledByExecutable( self ):
+        """
+            Return a boolean indicating whether the current request has
+            invoked any IExecutableObjects.
+
+            This can be used to determine if an object was called 
+            (more or less) directly from a URL, or if it was called by
+            through-the-web provided code.
+        """
+        return len( self._context.stack )
+


=== Zope3/lib/python/Zope/Security/SimpleSecurityPolicies.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+""" Simple ISecurityPolicy implementations."""
+
+from ISecurityPolicy import ISecurityPolicy
+from Zope.Exceptions import Unauthorized
+
+class ParanoidSecurityPolicy:
+    """
+        Deny all access.
+    """
+    __implements__ = ISecurityPolicy
+
+    def checkPermission( sel, permission, object, context ):
+        if (context.user is None   # no user
+            and not context.stack  # no untrusted code
+            ):
+            return 1 # Nobody not to trust!
+        
+        return 0
+
+class PermissiveSecurityPolicy:
+    """
+        Allow all access
+    """
+    __implements__ = ISecurityPolicy
+
+    def checkPermission( self, permission, object, context ):
+        return 1
+


=== Zope3/lib/python/Zope/Security/_Proxy.c 1.1 => 1.2 === (801/901 lines abridged)
+ * Security proxy.
+ */
+
+#include <Python.h>
+
+static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0;
+
+typedef struct {
+	PyObject_HEAD
+	PyObject *proxy_object;
+	PyObject *proxy_checker;
+} ProxyObject;
+
+#define Proxy_Check(proxy) \
+	PyObject_TypeCheck(proxy, &ProxyType)
+
+#define Proxy_GetObject(proxy) \
+        (((ProxyObject *)proxy)->proxy_object)
+
+#define Proxy_GetChecker(proxy) \
+        (((ProxyObject *)proxy)->proxy_checker)
+
+
+staticforward PyTypeObject ProxyType;
+
+
+/*
+ * Machinery to call the checker.
+ */
+
+typedef PyObject *(*function1)(PyObject *);
+
+static int
+check(PyObject *checker, char *opname, PyObject *object)
+{
+	PyObject *checked;
+
+	checked = PyObject_CallMethod(checker, "check", "(Os)",
+				      object, opname);
+	if (checked == NULL)
+		return 0;
+	Py_DECREF(checked);
+	return 1;
+}
+
+static int
+checkattr(PyObject *checker, char *check_method,
+	  PyObject *object, PyObject *name)
+{
+	PyObject *checked;

[-=- -=- -=- 801 lines omitted -=- -=- -=-]

+static PyObject *
+module_getChecker(PyObject *self, PyObject *arg)
+{
+	PyObject *result;
+
+	if (!Proxy_Check(arg)) {
+		PyErr_SetString(PyExc_TypeError,
+				"getChecker argument must be a _Proxy");
+		return NULL;
+	}
+	result = Proxy_GetChecker(arg);
+	Py_INCREF(result);
+	return result;
+}
+
+static PyMethodDef
+module_functions[] = {
+	{"getObject", module_getObject, METH_O, "get object from proxy"},
+	{"getChecker", module_getChecker, METH_O, "get checker from proxy"},
+	{NULL}
+};
+
+static char
+module___doc__[] = "Security proxy implementation.";
+
+void
+init_Proxy(void)
+{
+	PyObject *m;
+
+	__class__str = PyString_FromString("__class__");
+	if (! __class__str) return;
+
+	__name__str = PyString_FromString("__name__");
+	if (! __name__str) return;
+
+	__module__str = PyString_FromString("__module__");
+	if (! __module__str) return;
+
+	ProxyType.ob_type = &PyType_Type;
+	if (PyType_Ready(&ProxyType) < 0)
+		return;
+
+	m = Py_InitModule3("_Proxy", module_functions, module___doc__);
+	if (m == NULL)
+		return;
+
+	Py_INCREF(&ProxyType);
+	PyModule_AddObject(m, "_Proxy", (PyObject *)&ProxyType);
+}


=== Zope3/lib/python/Zope/Security/__init__.py 1.1 => 1.2 ===


=== Zope3/lib/python/Zope/Security/setup.py 1.1 => 1.2 ===
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+
+from distutils.core import setup, Extension
+
+setup(name="_Proxy", version = "0.1",
+      ext_modules=[Extension("_Proxy", ["_Proxy.c"])])