[Zope-Checkins] CVS: Zope/lib/python/AccessControl - ImplC.py:1.2 ImplPython.py:1.2 Implementation.py:1.2 DTML.py:1.12 PermissionRole.py:1.21 Permissions.py:1.8 Role.py:1.59 SecurityInfo.py:1.20 SecurityManagement.py:1.9 SecurityManager.py:1.15 SimpleObjectPolicies.py:1.13 ZopeGuards.py:1.17 ZopeSecurityPolicy.py:1.26 __init__.py:1.17

Tres Seaver tseaver at zope.com
Thu Jan 15 18:09:36 EST 2004


Update of /cvs-repository/Zope/lib/python/AccessControl
In directory cvs.zope.org:/tmp/cvs-serv24317/AccessControl

Modified Files:
	DTML.py PermissionRole.py Permissions.py Role.py 
	SecurityInfo.py SecurityManagement.py SecurityManager.py 
	SimpleObjectPolicies.py ZopeGuards.py ZopeSecurityPolicy.py 
	__init__.py 
Added Files:
	ImplC.py ImplPython.py Implementation.py 
Log Message:


  - Merge a number of entangled issues from 2.6 / 2.7 audit:

    Iteration over sequences could in some cases fail to check access 
    to an object obtained from the sequence. Subsequent checks (such 
    as for attributes access) of such an object would still be 
    performed, but it should not have been possible to obtain the 
    object in the first place.

    List and dictionary instance methods such as the get method of 
    dictionary objects were not security aware and could return an 
    object without checking access to that object. Subsequent checks 
    (such as for attributes access) of such an object would still be 
    performed, but it should not have been possible to obtain the 
    object in the first place.

    Use of "import as" in Python scripts could potentially rebind 
    names in ways that could be used to avoid appropriate security 
    checks.

    A number of newer built-ins were either unavailable in untrusted 
    code or did not perform adequate security checking.

    Unpacking via function calls, variable assignment, exception 
    variables and other contexts did not perform adequate security 
    checks, potentially allowing access to objects that should have 
    been protected.

    Class security was not properly intialized for PythonScripts, 
    potentially allowing access to variables that should be protected. 
    It turned out that most of the security assertions were in fact 
    activated as a side effect of other code, but this fix is still 
    appropriate to ensure that all security declarations are properly 
    applied.

    DTMLMethods with proxy rights could incorrectly transfer those 
    rights via acquisition when traversing to a parent object.


=== Zope/lib/python/AccessControl/ImplC.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:36 2004
+++ Zope/lib/python/AccessControl/ImplC.py	Thu Jan 15 18:09:03 2004
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+
+"""C implementation of the access control machinery."""
+
+
+try:
+    from cAccessControl import rolesForPermissionOn, \
+         PermissionRole, imPermissionRole, _what_not_even_god_should_do, \
+         RestrictedDTMLMixin, aq_validate, guarded_getattr, \
+         ZopeSecurityPolicy, setDefaultBehaviors
+    from cAccessControl import SecurityManager as cSecurityManager
+except ImportError:
+    import sys
+    # make sure a partial import doesn't pollute sys.modules
+    del sys.modules[__name__]
+
+
+from ImplPython import RestrictedDTML, SecurityManager
+
+
+class RestrictedDTML(RestrictedDTMLMixin, RestrictedDTML):
+    """A mix-in for derivatives of DT_String.String that adds Zope security."""
+
+class SecurityManager(cSecurityManager, SecurityManager):
+    """A security manager provides methods for checking access and managing
+    executable context and policies
+    """


=== Zope/lib/python/AccessControl/ImplPython.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:36 2004
+++ Zope/lib/python/AccessControl/ImplPython.py	Thu Jan 15 18:09:03 2004
@@ -0,0 +1,561 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+
+"""Python implementation of the access control machinery."""
+
+import os
+import string
+
+from Acquisition import aq_base
+from ExtensionClass import Base
+from zLOG import LOG, PROBLEM
+
+# This is used when a permission maps explicitly to no permission.  We
+# try and get this from cAccessControl first to make sure that if both
+# security implementations exist, we can switch between them later.
+try:
+    from cAccessControl import _what_not_even_god_should_do
+except ImportError:
+    _what_not_even_god_should_do = []
+
+from AccessControl import SecurityManagement
+from AccessControl import Unauthorized
+from AccessControl.SimpleObjectPolicies import Containers, _noroles
+from AccessControl.ZopeGuards import guarded_getitem
+
+
+# AccessControl.PermissionRole
+# ----------------------------
+
+_ident_chars = string.ascii_letters + string.digits + "_"
+name_trans = filter(lambda c, an=_ident_chars: c not in an,
+                    map(chr, range(256)))
+name_trans = string.maketrans(''.join(name_trans), '_' * len(name_trans))
+
+_default_roles = ('Manager',)
+
+def rolesForPermissionOn(perm, object, default=_default_roles, n=None):
+    """Return the roles that have the given permission on the given object
+    """
+    n = n or '_' + string.translate(perm, name_trans) + "_Permission"
+    r = None
+    
+    while 1:
+        if hasattr(object, n):
+            roles = getattr(object, n)
+            if roles is None:
+                return 'Anonymous',
+
+            t = type(roles)
+            if t is tuple:
+                # If we get a tuple, then we don't acquire
+                if r is None:
+                    return roles
+                return r+list(roles)
+
+            if t is str:
+                # We found roles set to a name.  Start over
+                # with the new permission name.  If the permission
+                # name is '', then treat as private!
+                if roles:
+                    if roles != n:
+                        n = roles
+                    # If we find a name that is the same as the
+                    # current name, we just ignore it.
+                    roles = None
+                else:
+                    return _what_not_even_god_should_do
+
+            elif roles:
+                if r is None:
+                    r = list(roles)
+                else: r = r + list(roles)
+
+        object = getattr(object, 'aq_inner', None)
+        if object is None:
+            break
+        object = object.aq_parent
+
+    if r is None:
+        return default
+
+    return r
+
+
+
+class PermissionRole(Base):
+    """Implement permission-based roles.
+
+    Under normal circumstances, our __of__ method will be
+    called with an unwrapped object.  The result will then be called
+    with a wrapped object, if the original object was wrapped.
+    To deal with this, we have to create an intermediate object.
+
+    """
+
+    def __init__(self, name, default=('Manager',)):
+        self.__name__ = name
+        self._p = '_' + string.translate(name, name_trans) + "_Permission"
+        self._d = self.__roles__ = default
+
+    def __of__(self, parent):
+        r = imPermissionRole()
+        r._p = self._p
+        r._pa = parent
+        r._d = self._d
+        p = getattr(parent, 'aq_inner', None)
+        if p is not None:
+            return r.__of__(p)
+        else:
+            return r
+
+    def rolesForPermissionOn(self, value):
+        return rolesForPermissionOn(None, value, self._d, self._p)
+
+
+class imPermissionRole(Base):
+    """Implement permission-based roles"""
+
+    def __of__(self, value):
+        return rolesForPermissionOn(None, value, self._d, self._p)
+    rolesForPermissionOn = __of__
+
+    # The following methods are needed in the unlikely case that an unwrapped
+    # object is accessed:
+    def __getitem__(self, i):
+        try:
+            v = self._v
+        except:
+            v = self._v = self.__of__(self._pa)
+            del self._pa
+
+        return v[i]
+
+    def __len__(self):
+        try:
+            v = self._v
+        except:
+            v = self._v = self.__of__(self._pa)
+            del self._pa
+
+        return len(v)
+
+
+# AccessControl.DTML
+# ------------------
+
+class RestrictedDTML:
+    """A mix-in for derivatives of DT_String.String that adds Zope security."""
+
+    def guarded_getattr(self, *args): # ob, name [, default]
+        return guarded_getattr(*args)
+
+    def guarded_getitem(self, ob, index):
+        return guarded_getitem(ob, index)
+
+
+# AccessControl.ZopeSecurityPolicy
+# --------------------------------
+#
+#   TODO:  implement this in cAccessControl, and have Implementation
+#          do the indirection.
+#
+from AccessControl.ZopeSecurityPolicy import getRoles  # XXX
+
+class ZopeSecurityPolicy:
+
+    def __init__(self, ownerous=1, authenticated=1):
+        """Create a Zope security policy.
+
+        Two optional keyword arguments may be provided:
+
+        ownerous -- Untrusted users can create code
+                    (e.g. Python scripts or templates),
+                    so check that code owners can access resources.
+                    The argument must have a truth value.
+                    The default is true.
+
+        authenticated -- Allow access to resources based on the
+                    privaledges of the authenticated user.
+                    The argument must have a truth value.
+                    The default is true.
+
+                    This (somewhat experimental) option can be set
+                    to false on sites that allow only public
+                    (unauthenticated) access. An anticipated
+                    scenario is a ZEO configuration in which some
+                    clients allow only public access and other
+                    clients allow full management.
+        """
+        self._ownerous = ownerous
+        self._authenticated = authenticated
+
+    def validate(self, accessed, container, name, value, context,
+                 roles=_noroles, getattr=getattr, _noroles=_noroles,
+                 valid_aq_=('aq_parent','aq_inner', 'aq_explicit')):
+
+        # Note: accessed is not used.
+
+        ############################################################
+        # Provide special rules for the acquisition attributes
+        if isinstance(name, str):
+            if name.startswith('aq_') and name not in valid_aq_:
+                raise Unauthorized(name, value)
+
+        ############################################################
+        # If roles weren't passed in, we'll try to get them from the object
+
+        if roles is _noroles:
+            roles = getRoles(container, name, value, _noroles)
+
+        ############################################################
+        # We still might not have any roles
+
+        if roles is _noroles:
+
+            ############################################################
+            # We have an object without roles and we didn't get a list
+            # of roles passed in. Presumably, the value is some simple
+            # object like a string or a list.  We'll try to get roles
+            # from its container.
+            if container is None:
+                # Either container or a list of roles is required
+                # for ZopeSecurityPolicy to know whether access is
+                # allowable.
+                raise Unauthorized(name, value)
+
+            roles = getattr(container, '__roles__', roles)
+            if roles is _noroles:
+                # Try to acquire __roles__.  If __roles__ can't be
+                # acquired, the value is unprotected and roles is
+                # left set to _noroles.
+                if aq_base(container) is not container:
+                    try:
+                        roles = container.aq_acquire('__roles__')
+                    except AttributeError:
+                        pass
+
+            # We need to make sure that we are allowed to
+            # get unprotected attributes from the container. We are
+            # allowed for certain simple containers and if the
+            # container says we can. Simple containers
+            # may also impose name restrictions.
+            p = Containers(type(container), None)
+            if p is None:
+                p = getattr(container,
+                            '__allow_access_to_unprotected_subobjects__',
+                            None)
+
+            if p is not None:
+                tp = p.__class__
+                if tp is not int:
+                    if tp is dict:
+                        if isinstance(name, basestring):
+                            p = p.get(name)
+                        else:
+                            p = 1
+                    else:
+                        p = p(name, value)
+
+            if not p:
+                raise Unauthorized(name, value)
+
+            if roles is _noroles:
+                return 1
+
+            # We are going to need a security-aware object to pass
+            # to allowed(). We'll use the container.
+            value = container
+
+        # Short-circuit tests if we can:
+        try:
+            if roles is None or 'Anonymous' in roles:
+                return 1
+        except TypeError:
+            # 'roles' isn't a sequence
+            LOG('Zope Security Policy', PROBLEM, "'%s' passed as roles"
+                " during validation of '%s' is not a sequence." % (
+                `roles`, name))
+            raise
+
+        # Check executable security
+        stack = context.stack
+        if stack:
+            eo = stack[-1]
+
+            # If the executable had an owner, can it execute?
+            if self._ownerous:
+                owner = eo.getOwner()
+                if (owner is not None) and not owner.allowed(value, roles):
+                    # We don't want someone to acquire if they can't
+                    # get an unacquired!
+                    raise Unauthorized(name, value)
+
+            # Proxy roles, which are a lot safer now.
+            proxy_roles = getattr(eo, '_proxy_roles', None)
+            if proxy_roles:
+                # Verify that the owner actually can state the proxy role
+                # in the context of the accessed item; users in subfolders
+                # should not be able to use proxy roles to access items
+                # above their subfolder!
+                owner = eo.getOwner()
+                # Sigh; the default userfolder doesn't return users wrapped
+                if owner and not hasattr(owner, 'aq_parent'):
+                    udb = eo.getOwner(1)[0]
+                    root = container.getPhysicalRoot()
+                    udb = root.unrestrictedTraverse(udb)
+                    owner = owner.__of__(udb)
+
+                if owner is not None:
+                    if not owner._check_context(container):
+                        # container is higher up than the owner, deny access
+                        raise Unauthorized(name, value)
+
+                for r in proxy_roles:
+                    if r in roles:
+                        return 1
+
+                # Proxy roles actually limit access!
+                raise Unauthorized(name, value)
+
+        try:
+            if self._authenticated and context.user.allowed(value, roles):
+                return 1
+        except AttributeError:
+            pass
+
+        raise Unauthorized(name, value)
+
+    def checkPermission(self, permission, object, context):
+        # XXX proxy roles and executable owner are not checked
+        roles = rolesForPermissionOn(permission, object)
+        if isinstance(roles, basestring):
+            roles = [roles]
+        return context.user.allowed(object, roles)
+
+
+# AccessControl.SecurityManager
+# -----------------------------
+
+# There is no corresponding control in the C implementation of the
+# access control machinery (cAccessControl.c); this should probably go
+# away in a future version.  If you're concerned about the size of
+# security stack, you probably have bigger problems.
+#
+try: max_stack_size = int(os.environ.get('Z_MAX_STACK_SIZE','100'))
+except: max_stack_size = 100
+
+def setDefaultBehaviors(ownerous, authenticated):
+    global _defaultPolicy
+    _defaultPolicy = ZopeSecurityPolicy(
+        ownerous=ownerous,
+        authenticated=authenticated)
+
+setDefaultBehaviors(True, True)
+
+
+class SecurityManager:
+    """A security manager provides methods for checking access and managing
+    executable context and policies
+    """
+
+    __allow_access_to_unprotected_subobjects__ = {
+        'validate': 1, 'checkPermission': 1,
+        'getUser': 1, 'calledByExecutable': 1
+        }
+
+    def __init__(self, thread_id, context):
+        self._thread_id = thread_id
+        self._context = context
+        self._policy = _defaultPolicy
+
+    def validate(self, accessed=None, container=None, name=None, value=None,
+                 roles=_noroles):
+        """Validate access.
+
+        Arguments:
+
+        accessed -- the object that was being accessed
+
+        container -- the object the value was found in
+
+        name -- The name used to access the value
+
+        value -- The value retrieved though the access.
+
+        roles -- The roles of the object if already known.
+
+        The arguments may be provided as keyword arguments. Some of these
+        arguments may be ommitted, however, the policy may reject access
+        in some cases when arguments are ommitted.  It is best to provide
+        all the values possible.
+        """
+        policy = self._policy
+        if roles is _noroles:
+            return policy.validate(accessed, container, name, value,
+                                   self._context)
+        else:
+            return policy.validate(accessed, container, name, value,
+                                   self._context, roles)
+
+    def DTMLValidate(self, accessed=None, container=None, name=None,
+                    value=None, md=None):
+        """Validate access.
+        * THIS EXISTS FOR DTML COMPATIBILITY *
+
+        Arguments:
+
+        accessed -- the object that was being accessed
+
+        container -- the object the value was found in
+
+        name -- The name used to access the value
+
+        value -- The value retrieved though the access.
+
+        md -- multidict for DTML (ignored)
+
+        The arguments may be provided as keyword arguments. Some of these
+        arguments may be ommitted, however, the policy may reject access
+        in some cases when arguments are ommitted.  It is best to provide
+        all the values possible.
+
+        """
+        policy = self._policy
+        return policy.validate(accessed, container, name, value, self._context)
+
+    def checkPermission(self, permission, object):
+        """Check whether the security context allows the given permission on
+        the given object.
+
+        Arguments:
+
+        permission -- A permission name
+
+        object -- The object being accessed according to the permission
+        """
+        policy = self._policy
+        return policy.checkPermission(permission, object, self._context)
+
+    def addContext(self, anExecutableObject,
+                   getattr=getattr):
+        """Add an ExecutableObject to the current security
+        context. Optionally, add a new SecurityPolicy as well.
+        """
+        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()
+        else:
+            p = _defaultPolicy
+        self._policy = p
+
+    def removeContext(self, anExecutableObject):
+        """Remove an ExecutableObject, and optionally, a
+        SecurityPolicy, from the current security context.
+        """
+        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()
+            else:
+                p = _defaultPolicy
+            self._policy = p
+        else:
+            self._policy = _defaultPolicy
+
+    def getUser(self):
+        """Get the current authenticated user"""
+        return self._context.user
+
+    def calledByExecutable(self):
+        """Return a boolean value indicating if this context was called
+        by an executable"""
+        return len(self._context.stack)
+
+
+# AccessControl.ZopeGuards
+# ------------------------
+
+def aq_validate(inst, object, name, v, validate):
+    return validate(inst, object, name, v)
+
+
+_marker = object()
+
+def guarded_getattr(inst, name, default=_marker):
+    """Retrieves an attribute, checking security in the process.
+
+    Raises Unauthorized if the attribute is found but the user is
+    not allowed to access the attribute.
+    """
+    if name[:1] != '_':
+        # Try to get the attribute normally so that unusual
+        # exceptions are caught early.
+        try: v = getattr(inst, name)
+        except AttributeError:
+            if default is not _marker:
+                return default
+            raise
+
+        assertion = Containers(type(inst))
+        if isinstance(assertion, dict):
+            # We got a table that lets us reason about individual
+            # attrs
+            assertion = assertion.get(name)
+            if assertion:
+                # There's an entry, but it may be a function.
+                if callable(assertion):
+                    return assertion(inst, name)
+
+                # Nope, it's boolean
+                return v
+            raise Unauthorized, name
+
+        elif assertion:
+            # So the entry in the outer table is not a dict
+            # It's allowed to be a vetoing function:
+            if callable(assertion):
+                assertion(name, v)
+            # No veto, so we can return
+            return v
+
+        validate = SecurityManagement.getSecurityManager().validate
+        # Filter out the objects we can't access.
+        if hasattr(inst, 'aq_acquire'):
+            return inst.aq_acquire(name, aq_validate, validate)
+        # Or just try to get the attribute directly.
+        if validate(inst, inst, name, v):
+            return v
+    raise Unauthorized, name


=== Zope/lib/python/AccessControl/Implementation.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:36 2004
+++ Zope/lib/python/AccessControl/Implementation.py	Thu Jan 15 18:09:03 2004
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+
+"""Controller that can switch between security machinery implementations.
+
+This module allows configuration of the security implementation after
+the initial import of the modules.  It is intended to allow runtime
+selection of the machinery based on Zope's configuration file.
+
+The helper function defined here switches between the 'C' and 'PYTHON'
+security implementations by loading the appropriate implementation
+module and wiring the implementation into the other modules of the
+AccessControl package that defined the various components before this
+module was introduced.
+
+"""
+
+def getImplementationName():
+    """Return the name of the implementation currently being used."""
+    return _implementation_name
+
+
+def setImplementation(name):
+    """Select the policy implementation to use.
+
+    'name' must be either 'PYTHON' or 'C'.
+
+    XXX:  'C' version is currently broken
+    """
+    import sys
+    global _implementation_name
+    #
+    name = name.upper()
+    if name == _implementation_name:
+        return
+    if name == "C":
+        raise NotImplementedError( # XXX
+                "'C' version of ZSP not yet working.")
+        try:
+            from AccessControl import ImplC as impl
+        except ImportError:
+            name = "PYTHON"
+            from AccessControl import ImplPython as impl
+    elif name == "PYTHON":
+        from AccessControl import ImplPython as impl
+    else:
+        raise ValueError("unknown policy implementation: %r" % name)
+    #
+    _implementation_name = name
+    for modname, names in _policy_names.items():
+        __import__(modname)
+        mod = sys.modules[modname]
+        for n in names:
+            setattr(mod, n, getattr(impl, n))
+        if hasattr(mod, "initialize"):
+            mod.initialize(impl)
+
+
+_implementation_name = None
+
+_policy_names = {
+    "AccessControl": ("setDefaultBehaviors",
+                      ),
+    "AccessControl.DTML": ("RestrictedDTML",
+                           ),
+    "AccessControl.PermissionRole": ("_what_not_even_god_should_do",
+                                     "rolesForPermissionOn",
+                                     "PermissionRole",
+                                     "imPermissionRole",
+                                     ),
+    "AccessControl.SecurityManagement": ("SecurityManager",
+                                      ),
+    "AccessControl.SecurityManager": ("SecurityManager",
+                                      ),
+    "AccessControl.ZopeGuards": ("aq_validate",
+                                 "guarded_getattr",
+                                 ),
+    "AccessControl.ZopeSecurityPolicy": ("ZopeSecurityPolicy",
+                                         ),
+    }
+
+
+# start with the default, mostly because we need something for the tests
+#setImplementation("C")   XXX: C version of ZSP isn't yet working
+setImplementation("PYTHON")


=== Zope/lib/python/AccessControl/DTML.py 1.11 => 1.12 ===
--- Zope/lib/python/AccessControl/DTML.py:1.11	Fri Nov 28 11:43:51 2003
+++ Zope/lib/python/AccessControl/DTML.py	Thu Jan 15 18:09:03 2004
@@ -19,33 +19,9 @@
 import SecurityManagement, string, math, whrandom, random
 import DocumentTemplate.sequence
 
-from ZopeGuards import guarded_getattr, guarded_getitem
+from ZopeGuards import safe_builtins
 
-class RestrictedDTML:
-    '''
-    A mix-in for derivatives of DT_String.String that adds Zope security.
-    '''
-    def guarded_getattr(self, *args): # ob, name [, default]
-        return guarded_getattr(*args)
-
-    def guarded_getitem(self, ob, index):
-        return guarded_getitem(ob, index)
-
-
-try:
-    #raise ImportError
-    import os
-    if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
-        raise ImportError # :)
-    from cAccessControl import RestrictedDTMLMixin
-except ImportError:
-    pass
-else:
-
-    class RestrictedDTML(RestrictedDTMLMixin, RestrictedDTML):
-        '''
-        A mix-in for derivatives of DT_String.String that adds Zope security.
-        '''
+# RestrictedDTML is inserted by AccessControl.Implementation.
 
 
 # Allow access to unprotected attributes
@@ -121,3 +97,9 @@
 for name, v in DTMLSecurityAPI.__dict__.items():
     if name[0] != '_':
         setattr(DT_Util.TemplateDict, name, v)
+
+for name, v in safe_builtins.items():
+    v = DT_Util.NotBindable(v)
+    if name.startswith('__'):
+        continue
+    setattr(DT_Util.TemplateDict, name, v)


=== Zope/lib/python/AccessControl/PermissionRole.py 1.20 => 1.21 ===
--- Zope/lib/python/AccessControl/PermissionRole.py:1.20	Fri Nov 28 11:43:53 2003
+++ Zope/lib/python/AccessControl/PermissionRole.py	Thu Jan 15 18:09:03 2004
@@ -10,200 +10,11 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''Objects that implement Permission-based roles.
-
+'''Objects that implement Permission-based roles.
 
 $Id$'''
-__version__='$Revision$'[11:-2]
-
-_use_python_impl = 0
-import os
-if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
-    _use_python_impl = 1
-else:
-    try:
-        # C Optimization:
-        from cAccessControl import rolesForPermissionOn, \
-             PermissionRole, imPermissionRole, _what_not_even_god_should_do
-    except ImportError:
-        # Fall back to Python implementation.
-        _use_python_impl = 1
-
-
-if 1 or _use_python_impl:
-
-    import sys
-
-    from ExtensionClass import Base
-
-    import string
-
-    name_trans=filter((lambda c, an=string.letters+string.digits+'_':
-                       c not in an
-                       ),
-                      map(chr,range(256)))
-    name_trans=string.maketrans(''.join(name_trans), '_'*len(name_trans))
-
-    def rolesForPermissionOn(perm, obj, default=('Manager',), n=None):
-        """Return the roles that have the given permission on the given object
-        """
-
-        n = n or '_'+string.translate(perm, name_trans)+"_Permission"
-        r = None
-        
-        while 1:
-            if hasattr(obj, n):
-                roles = getattr(obj, n)
-                if roles is None:
-                    return 'Anonymous',
-
-                t = type(roles)
-                if t is tuple:
-                    # If we get a tuple, then we don't acquire
-                    if r is None:
-                        return roles
-                    return r+list(roles)
-
-                if t is str:
-                    # We found roles set to a name.  Start over
-                    # with the new permission name.  If the permission
-                    # name is '', then treat as private!
-                    if roles:
-                        if roles != n:
-                            n = roles
-                        # If we find a name that is the same as the
-                        # current name, we just ignore it.
-                        roles = None
-                    else:
-                        return _what_not_even_god_should_do
-
-                elif roles:
-                    if r is None:
-                        r = list(roles)
-                    else: r = r + list(roles)
-
-            obj = getattr(obj, 'aq_inner', None)
-            if obj is None:
-                break
-            obj = obj.aq_parent
-
-        if r is None:
-            return default
-
-        return r
-
-    class PermissionRole(Base):
-        """Implement permission-based roles.
-
-        Under normal circumstances, our __of__ method will be
-        called with an unwrapped object.  The result will then be called
-        with a wrapped object, if the original object was wrapped.
-        To deal with this, we have to create an intermediate object.
-
-        """
-
-        def __init__(self, name, default=('Manager',)):
-            self.__name__=name
-            self._p='_'+string.translate(name,name_trans)+"_Permission"
-            self._d = self.__roles__ = default
-
-        def __of__(self, parent, getattr=getattr):
-            r=imPermissionRole()
-            r._p=self._p
-            r._pa=parent
-            r._d=self._d
-            p=getattr(parent, 'aq_inner', None)
-            if p is not None:
-                return r.__of__(p)
-            else:
-                return r
-
-        def rolesForPermissionOn(self, value):
-            return rolesForPermissionOn(None, value, self._d, self._p)
-
-    # This is used when a permission maps explicitly to no permission.
-    _what_not_even_god_should_do=[]
-
-    class imPermissionRole(Base):
-        """Implement permission-based roles
-        """
-
-        def __of__(self, value):
-            return rolesForPermissionOn(None, value, self._d, self._p)
-        rolesForPermissionOn = __of__
-
-        # The following methods are needed in the unlikely case that
-        # an unwrapped object is accessed:
-        
-        def __getitem__(self, i):
-            try:
-                v=self._v
-            except:
-                v=self._v=self.__of__(self._pa)
-                del self._pa
-
-            return v[i]
-
-        def __len__(self):
-            try:
-                v=self._v
-            except:
-                v=self._v=self.__of__(self._pa)
-                del self._pa
-
-            return len(v)
-
-##############################################################################
-# Test functions:
+  
+# The following names are inserted by AccessControl.Implementation:
 #
-
-def main():
-    # The "main" program for this module
-
-    import sys
-    sys.path.append('/projects/_/ExtensionClass')
-
-    from Acquisition import Implicit
-    class I(Implicit):
-        x__roles__=PermissionRole('x')
-        y__roles__=PermissionRole('y')
-        z__roles__=PermissionRole('z')
-        def x(self): pass
-        def y(self): pass
-        def z(self): pass
-
-
-
-    a=I()
-    a.b=I()
-    a.b.c=I()
-    a.q=I()
-    a.q._x_Permission=('foo',)
-    a._y_Permission=('bar',)
-    a._z_Permission=('zee',)
-    a.b.c._y_Permission=('Manage',)
-    a.b._z_Permission=['also']
-
-    print a.x.__roles__, list(a.x.__roles__)
-    print a.b.x.__roles__
-    print a.b.c.x.__roles__
-    print a.q.x.__roles__
-    print a.b.q.x.__roles__
-    print a.b.c.q.x.__roles__
-    print
-
-    print a.y.__roles__, list(a.y.__roles__)
-    print a.b.y.__roles__
-    print a.b.c.y.__roles__
-    print a.q.y.__roles__
-    print a.b.q.y.__roles__
-    print a.b.c.q.y.__roles__
-    print
-
-    print a.z.__roles__, list(a.z.__roles__)
-    print a.b.z.__roles__
-    print a.b.c.z.__roles__
-    print a.q.z.__roles__
-    print a.b.q.z.__roles__
-    print a.b.c.q.z.__roles__
-    print
+# rolesForPermissionOn, PermissionRole, imPermissionRole,
+# _what_not_even_god_should_do


=== Zope/lib/python/AccessControl/Permissions.py 1.7 => 1.8 ===
--- Zope/lib/python/AccessControl/Permissions.py:1.7	Thu Jun 12 06:20:59 2003
+++ Zope/lib/python/AccessControl/Permissions.py	Thu Jan 15 18:09:03 2004
@@ -10,9 +10,9 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-"""Constant definitions for built-in Zope permissions"""
+"""Constant definitions for built-in Zope permissions
 
-__version__='$Revision$'[11:-2]
+$Id$"""
 
 
 access_contents_information='Access contents information'


=== Zope/lib/python/AccessControl/Role.py 1.58 => 1.59 ===
--- Zope/lib/python/AccessControl/Role.py:1.58	Tue Nov 18 08:16:57 2003
+++ Zope/lib/python/AccessControl/Role.py	Thu Jan 15 18:09:03 2004
@@ -210,7 +210,7 @@
         if fails:
             return MessageDialog(title="Warning!",
                                  message="Some permissions had errors: "
-                                   + ', '.join(fails),
+                                   + escape(', '.join(fails)),
                                  action='manage_access')
         return MessageDialog(
             title  ='Success!',


=== Zope/lib/python/AccessControl/SecurityInfo.py 1.19 => 1.20 ===
--- Zope/lib/python/AccessControl/SecurityInfo.py:1.19	Fri Nov 28 11:43:56 2003
+++ Zope/lib/python/AccessControl/SecurityInfo.py	Thu Jan 15 18:09:03 2004
@@ -38,17 +38,18 @@
 
 """
 
-__version__='$Revision$'[11:-2]
+import sys
 
+import Acquisition
 
-import Acquisition, PermissionRole, sys
+from AccessControl.ImplPython import _what_not_even_god_should_do
 from zLOG import LOG, WARNING
 
 
 # Security constants - these are imported into the AccessControl
 # namespace and can be referenced as AccessControl.PUBLIC etc.
 
-ACCESS_NONE    = PermissionRole._what_not_even_god_should_do
+ACCESS_NONE    = _what_not_even_god_should_do
 ACCESS_PRIVATE = ()
 ACCESS_PUBLIC  = None
 


=== Zope/lib/python/AccessControl/SecurityManagement.py 1.8 => 1.9 ===
--- Zope/lib/python/AccessControl/SecurityManagement.py:1.8	Wed May 14 17:51:23 2003
+++ Zope/lib/python/AccessControl/SecurityManagement.py	Thu Jan 15 18:09:03 2004
@@ -33,10 +33,14 @@
     return manager
 
 import SpecialUsers
-from SecurityManager import SecurityManager
-try:    import thread
-except: get_ident=lambda: 0
-else:   get_ident=thread.get_ident
+
+# AccessControl.Implementation inserts SecurityManager.
+
+try:
+    from thread import get_ident
+except ImportError:
+    def get_ident():
+        return 0
 
 _managers={}
 


=== Zope/lib/python/AccessControl/SecurityManager.py 1.14 => 1.15 ===
--- Zope/lib/python/AccessControl/SecurityManager.py:1.14	Fri Nov 28 11:44:03 2003
+++ Zope/lib/python/AccessControl/SecurityManager.py	Thu Jan 15 18:09:03 2004
@@ -10,192 +10,24 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''short description
+'''API module to set the security policy
 
 
 $Id$'''
-__version__='$Revision$'[11:-2]
 
-import ZopeSecurityPolicy, os
+from AccessControl import ImplPython as _ImplPython
+from AccessControl.SimpleObjectPolicies import _noroles
 
-_noroles = ZopeSecurityPolicy._noroles
 
-try: max_stack_size=int(os.environ.get('Z_MAX_STACK_SIZE','100'))
-except: max_stack_size=100
-
-if os.environ.has_key("ZSP_OWNEROUS_SKIP"): ownerous=0
-else: ownerous=1
-if os.environ.has_key("ZSP_AUTHENTICATION_SKIP"): authenticated=0
-else: authenticated=1
-_defaultPolicy=ZopeSecurityPolicy.ZopeSecurityPolicy(ownerous=ownerous,
-    authenticated=authenticated)
 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
+    last = _ImplPython._defaultPolicy
+    _ImplPython._defaultPolicy = aSecurityPolicy
     return last
 
 
-class SecurityManager:
-    """A security manager provides methods for checking access and managing
-    executable context and policies
-    """
-
-    __allow_access_to_unprotected_subobjects__ = {
-        'validate': 1, 'checkPermission': 1,
-        'getUser': 1, 'calledByExecutable': 1
-        }
-
-    def __init__(self, thread_id, context):
-        self._thread_id=thread_id
-        self._context=context
-        self._policy=_defaultPolicy
-
-    def validate(self, accessed=None, container=None, name=None, value=None,
-                 roles=_noroles):
-        """Validate access.
-
-        Arguments:
-
-        accessed -- the object that was being accessed
-
-        container -- the object the value was found in
-
-        name -- The name used to access the value
-
-        value -- The value retrieved though the access.
-
-        roles -- The roles of the object if already known.
-
-        The arguments may be provided as keyword arguments. Some of these
-        arguments may be ommitted, however, the policy may reject access
-        in some cases when arguments are ommitted.  It is best to provide
-        all the values possible.
-        """
-        policy=self._policy
-        if roles is _noroles:
-            return policy.validate(accessed, container, name, value,
-                                   self._context)
-        else:
-            return policy.validate(accessed, container, name, value,
-                                   self._context, roles)
-
-    def DTMLValidate(self, accessed=None, container=None, name=None,
-                    value=None, md=None):
-
-        """Validate access.
-        * THIS EXISTS FOR DTML COMPATIBILITY *
-
-        Arguments:
-
-        accessed -- the object that was being accessed
-
-        container -- the object the value was found in
-
-        name -- The name used to access the value
-
-        value -- The value retrieved though the access.
-
-        md -- multidict for DTML (ignored)
-
-        The arguments may be provided as keyword arguments. Some of these
-        arguments may be ommitted, however, the policy may reject access
-        in some cases when arguments are ommitted.  It is best to provide
-        all the values possible.
-
-        """
-        policy=self._policy
-        return policy.validate(accessed, container, name, value,
-                               self._context)
-
-    def checkPermission(self, permission, object):
-        """Check whether the security context allows the given permission on
-        the given object.
-
-        Arguments:
-
-        permission -- A permission name
-
-        object -- The object being accessed according to the permission
-        """
-        policy=self._policy
-        return policy.checkPermission(permission, object,
-                                      self._context)
-
-    def addContext(self, anExecutableObject,
-                   getattr=getattr):
-        """Add an ExecutableObject to the current security
-        context. Optionally, add a new SecurityPolicy as well.
-        """
-        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()
-        else:
-            p=_defaultPolicy
-        self._policy=p
-
-    def removeContext(self, anExecutableObject,
-                      getattr=getattr):
-        """Remove an ExecutableObject, and optionally, a
-        SecurityPolicy, from the current security context.
-        """
-        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()
-            else:
-                p=_defaultPolicy
-            self._policy=p
-        else:
-            self._policy=_defaultPolicy
-
-    def getUser(self):
-        """Get the current authenticated user"""
-        return self._context.user
-
-    def calledByExecutable(self):
-        """Return a boolean value indicating if this context was called
-        by an executable"""
-        return len(self._context.stack)
-
-
-try:
-    #raise ImportError # uncomment to disable C optimization
-    import os
-    if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
-        raise ImportError # :)
-    from cAccessControl import SecurityManager as cSecurityManager
-except ImportError:
-    pass
-else:
-
-    class SecurityManager(cSecurityManager, SecurityManager):
-        """A security manager provides methods for checking access and managing
-        executable context and policies
-        """
+# AccessControl.Implementation inserts SecurityManager.


=== Zope/lib/python/AccessControl/SimpleObjectPolicies.py 1.12 => 1.13 ===
--- Zope/lib/python/AccessControl/SimpleObjectPolicies.py:1.12	Wed Aug 14 17:29:07 2002
+++ Zope/lib/python/AccessControl/SimpleObjectPolicies.py	Thu Jan 15 18:09:03 2004
@@ -10,22 +10,69 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''Collect rules for access to objects that don\'t have roles.
+'''Collect rules for access to objects that don\'t have roles.
+
+The rules are expressed as a mapping from type -> assertion
+
+An assertion can be:
+
+  - A dict
+
+  - A callable
+
+  - Something with a truth value
+
+If the assertion is a callable, then it will be called with
+a name being accessed and the name used.  Its return value is ignored,
+but in may veto an access by raising an exception.
+
+If the assertion is a dictionary, then the keys are attribute names.
+The values may be callables or objects with boolean values. If a value
+is callable, it will be called with the object we are accessing an
+attribute of and the attribute name. It should return an attribute
+value. Callables are often used to returned guarded versions of
+methods.  Otherwise, accesses are allowed if values in this dictionary
+are true and disallowed if the values are false or if an item for an
+attribute name is not present.
+
+If the assertion is not a dict and is not callable, then access to
+unprotected attributes is allowed if the assertion is true, and
+disallowed otherwise.
+
+XXX This descrition doesn't actually match what's done in ZopeGuards
+or in ZopeSecurityPolicy. :(
 
 $Id$'''
-__version__='$Revision$'[11:-2]
 
-_noroles=[] # this is imported from various places
+_noroles = [] # this is imported in various places
 
 import Record
 
 # Allow access to unprotected attributes
 Record.Record.__allow_access_to_unprotected_subobjects__=1
 
+# ContainerAssertions are used by cAccessControl to check access to
+# attributes of container types, like dict, list, or string.
+# ContainerAssertions maps types to a either a dict, a function, or a
+# simple boolean value.  When guarded_getattr checks the type of its
+# first argument against ContainerAssertions, and invokes checking
+# logic depending on what value it finds.
+
+# If the value for a type is:
+#   - a boolean value:
+#      - the value determines whether access is allowed
+#   - a function (or callable):
+#      - The function is called with the name of the attribute and
+#        the actual attribute value, then the value is returned.
+#        The function can raise an exception.
+#   - a dict:
+#      - The dict maps attribute names to boolean values or functions.
+#        The boolean values behave as above, but the functions do not.
+#        The value returned for attribute access is the result of
+#        calling the function with the object and the attribute name.
+
 ContainerAssertions={
     type(()): 1,
-    type([]): 1,
-    type({}): 1,
     type(''): 1,
     type(u''): 1,
     }
@@ -45,16 +92,15 @@
     # What to do?
     pass
 
-Containers=ContainerAssertions.get
+Containers = ContainerAssertions.get
 
-from types import IntType, DictType, TypeType
 def allow_type(Type, allowed=1):
     """Allow a type and all of its methods and attributes to be used from
     restricted code.  The argument Type must be a type."""
-    if type(Type) is not TypeType:
+    if type(Type) is not type:
         raise ValueError, "%s is not a type" % `Type`
     if hasattr(Type, '__roles__'):
         raise ValueError, "%s handles its own security" % `Type`
-    if not (isinstance(allowed, IntType) or isinstance(allowed, DictType)):
+    if not (isinstance(allowed, int) or isinstance(allowed, dict)):
         raise ValueError, "The 'allowed' argument must be an int or dict."
     ContainerAssertions[Type] = allowed


=== Zope/lib/python/AccessControl/ZopeGuards.py 1.16 => 1.17 ===
--- Zope/lib/python/AccessControl/ZopeGuards.py:1.16	Tue Jun 10 11:39:04 2003
+++ Zope/lib/python/AccessControl/ZopeGuards.py	Thu Jan 15 18:09:03 2004
@@ -13,12 +13,14 @@
 
 __version__='$Revision$'[11:-2]
 
-from RestrictedPython.Guards import safe_builtins, _full_read_guard, \
-     full_write_guard
+import sys
+
+import RestrictedPython
+from RestrictedPython.Guards import safe_builtins, full_write_guard
 from RestrictedPython.Utilities import utility_builtins
 from SecurityManagement import getSecurityManager
 from SecurityInfo import secureModule
-from SimpleObjectPolicies import Containers
+from SimpleObjectPolicies import Containers, ContainerAssertions
 from zExceptions import Unauthorized
 
 _marker = []  # Create a new marker object.
@@ -26,47 +28,16 @@
 safe_builtins = safe_builtins.copy()
 safe_builtins.update(utility_builtins)
 
-try:
+# AccessControl.Implementation inserts these names into this module as
+# module globals:  aq_validate, guarded_getattr
 
-    #raise ImportError
-    import os
-    if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
-        raise ImportError # :)
-    from cAccessControl import aq_validate, guarded_getattr
-
-except ImportError:
-
-    def aq_validate(inst, obj, name, v, validate):
-        return validate(inst, obj, name, v)
-
-
-    def guarded_getattr(inst, name, default=_marker):
-        """Retrieves an attribute, checking security in the process.
-
-        Raises Unauthorized if the attribute is found but the user is
-        not allowed to access the attribute.
-        """
-        if name[:1] != '_':
-            # Try to get the attribute normally so that unusual
-            # exceptions are caught early.
-            try: v = getattr(inst, name)
-            except AttributeError:
-                if default is not _marker:
-                    return default
-                raise
-            if Containers(type(inst)):
-                # Simple type.  Short circuit.
-                return v
-            validate = getSecurityManager().validate
-            # Filter out the objects we can't access.
-            if hasattr(inst, 'aq_acquire'):
-                return inst.aq_acquire(name, aq_validate, validate)
-            # Or just try to get the attribute directly.
-            if validate(inst, inst, name, v):
-                return v
-        raise Unauthorized, name
+def initialize(impl):
+    # Called by AccessControl.Implementation.setImplementation()
+    # whenever the selected implementation changes.
+    global guarded_getattr
+    guarded_getattr = impl.guarded_getattr
+    safe_builtins['getattr'] = guarded_getattr
 
-safe_builtins['getattr'] = guarded_getattr
 
 def guarded_hasattr(object, name):
     try:
@@ -96,13 +67,166 @@
     if Containers(type(object)) and Containers(type(v)):
         # Simple type.  Short circuit.
         return v
-    if getSecurityManager().validate(object, object, index, v):
+    if getSecurityManager().validate(object, object, None, v):
         return v
     raise Unauthorized, 'unauthorized access to element %s' % `i`
 
+if sys.version_info < (2, 2):
+    # Can't use nested scopes, so we create callable instances
+    class get_dict_get:
+        def __init__(self, d, name):
+            self.d = d
+
+        def __call__(self, key, default=None):
+            try:
+                return guarded_getitem(self.d, key)
+            except KeyError:
+                return default
+
+    class get_dict_pop:
+        def __init__(self, d, name):
+            self.d = d
+
+        def __call__(self, key, default=_marker):
+            try:
+                v = guarded_getitem(self.d, key)
+            except KeyError:
+                if default is not _marker:
+                    return default
+                raise
+            else:
+                del self.d[key]
+                return v
 
-full_read_guard = _full_read_guard(guarded_getattr, guarded_getitem)
+    # Dict methods not in Python 2.1
+    get_iter = 0
 
+    class get_list_pop:
+        def __init__(self, lst, name):
+            self.lst = lst
+
+        def __call__(self, index=-1):
+            # XXX This is not thread safe, but we don't expect
+            # XXX thread interactions between python scripts <wink>
+            v = guarded_getitem(self.lst, index)
+            del self.lst[index]
+            return v
+
+else:
+    # Python 2.2 or better: Create functions using nested scope to store state
+    # This is less expensive then instantiating and calling instances
+    def get_dict_get(d, name):
+        def guarded_get(key, default=None):
+            try:
+                return guarded_getitem(d, key)
+            except KeyError:
+                return default
+        return guarded_get
+
+    def get_dict_pop(d, name):
+        def guarded_pop(key, default=_marker):
+            try:
+                v = guarded_getitem(d, key)
+            except KeyError:
+                if default is not _marker:
+                    return default
+                raise
+            else:
+                del d[key]
+                return v
+        return guarded_pop
+
+    def get_iter(c, name):
+        iter = getattr(c, name)
+        def guarded_iter():
+            return SafeIter(iter(), c)
+        return guarded_iter
+
+    def get_list_pop(lst, name):
+        def guarded_pop(index=-1):
+            # XXX This is not thread safe, but we don't expect
+            # XXX thread interactions between python scripts <wink>
+            v = guarded_getitem(lst, index)
+            del lst[index]
+            return v
+        return guarded_pop
+
+# See comment in SimpleObjectPolicies for an explanation of what the
+# dicts below actually mean.
+
+ContainerAssertions[type({})] = {
+    'clear':1, 'copy':1, 'fromkeys':1, 'get':get_dict_get, 'has_key':1,
+    'items':1, 'iteritems':1, 'keys':1,
+    'iterkeys': get_iter,  'itervalues':get_iter,
+    'pop':get_dict_pop, 'popitem':1, 'setdefault':1, 'update':1, 'values':1}
+
+ContainerAssertions[type([])] = {
+    'append':1, 'count':1, 'extend':1, 'index':1, 'insert':1,
+    'pop':get_list_pop, 'remove':1, 'reverse':1, 'sort':1}
+
+
+# This implementation of a "safe" iterator uses a global guard()
+# function to implement the actual guard.  This check is defined as a
+# global so that it can delay imports of some module to avoid circular
+# dependencies while also making it possible to use a faster
+# implementation once the imports are done (by avoiding the import
+# machinery on subsequent calls).  Use of a method on the SafeIter
+# class is avoided to ensure the best performance of the resulting
+# function.
+
+
+if sys.version_info < (2, 2):
+
+    class SafeIter:
+        def __init__(self, sequence, container=None):
+            if container is None:
+                container = sequence
+            self.container = container
+            self.sequence = sequenece
+            self.next_index = 0
+
+        def __getitem__(self, index):
+            ob = self.sequence[self.next_index]
+            self.next_index += 1
+            guard(self.container, ob, self.next_index - 1)
+            return ob
+
+    def _error(index):
+        raise Unauthorized, 'unauthorized access to element %s' % `index`
+
+else:
+    class SafeIter(object):
+        #__slots__ = '_next', 'container'
+
+        def __init__(self, ob, container=None):
+            self._next = iter(ob).next
+            if container is None:
+                container = ob
+            self.container = container
+
+        def __iter__(self):
+            return self
+
+        def next(self):
+            ob = self._next()
+            guard(self.container, ob)
+            return ob
+
+    def _error(index):
+        raise Unauthorized, 'unauthorized access to element'
+
+    safe_builtins['iter'] = SafeIter
+
+
+def guard(container, value, index=None):
+    if Containers(type(container)) and Containers(type(value)):
+        # Simple type.  Short circuit.
+        return
+    if getSecurityManager().validate(container, container, index, value):
+        return
+    _error(index)
+
+# More replacement built-ins.
 
 def guarded_filter(f, seq, skip_unauthorized=0):
     if type(seq) is type(''):
@@ -120,6 +244,27 @@
     return result
 safe_builtins['filter'] = guarded_filter
 
+def guarded_reduce(f, seq, initial=_marker):
+    if initial is _marker:
+        return reduce(f, SafeIter(seq))
+    else:
+        return reduce(f, SafeIter(seq), initial)
+safe_builtins['reduce'] = guarded_reduce
+
+def guarded_max(item, *items):
+    if items:
+        item = [item]
+        item.extend(items)
+    return max(SafeIter(item))
+safe_builtins['max'] = guarded_max
+
+def guarded_min(item, *items):
+    if items:
+        item = [item]
+        item.extend(items)
+    return min(SafeIter(item))
+safe_builtins['min'] = guarded_min
+
 def guarded_map(f, *seqs):
     safe_seqs = []
     for seqno in range(len(seqs)):
@@ -128,7 +273,6 @@
     return map(f, *safe_seqs)
 safe_builtins['map'] = guarded_map
 
-import sys
 def guarded_import(mname, globals={}, locals={}, fromlist=None):
     mnameparts = mname.split('.')
     firstmname = mnameparts[0]
@@ -156,6 +300,31 @@
     raise ImportError, 'import of "%s" is unauthorized' % mname
 safe_builtins['__import__'] = guarded_import
 
+class GuardedListType:
+    def __call__(self, *args, **kwargs):
+        return list(*args, **kwargs)
+
+    if sys.version_info >= (2, 4):
+        def sorted(self, iterable, cmp=None, key=None, reverse=False):
+            return list.sorted(iterable, cmp=None, key=None, reverse=False)
+safe_builtins['list'] = GuardedListType()
+
+class GuardedDictType:
+    def __call__(self, *args, **kwargs):
+        return dict(*args, **kwargs)
+
+    def fromkeys(self, S, v=None):
+        return dict.fromkeys(S,v)
+safe_builtins['dict'] = GuardedDictType()
+
+def guarded_enumerate(seq):
+    return enumerate(SafeIter(seq))
+safe_builtins['enumerate'] = guarded_enumerate
+
+def guarded_sum(sequence, start=0):
+    return sum(SafeIter(sequence), start)
+safe_builtins['sum'] = guarded_sum
+
 def load_module(module, mname, mnameparts, validate, globals, locals):
     modules = sys.modules
     while mnameparts:
@@ -175,3 +344,65 @@
             return
         module = nextmodule
     return module
+
+# This version of apply is used by restricted Python, which transforms
+# extended call syntax into a call of _apply_(), after tucking the callable
+# into the first element of args.  For example,
+#     f(3, eggs=1, spam=False)
+# is changed to
+#     _apply_(f, 3, eggs=1, spam=False)
+def guarded_apply(func, *args, **kws):
+    return builtin_guarded_apply(func, args, kws)
+
+# This version is the safe_builtins apply() replacement, so needs to match the
+# signature of __builtin__.apply.
+def builtin_guarded_apply(func, args=(), kws={}):
+    # Check the args elements.  args may be an arbitrary iterable, and
+    # iterating over it may consume it, so we also need to save away
+    # the arguments in a new list to pass on to the real apply().
+    i, arglist = 0, []
+    for elt in args:
+        guard(args, elt, i)
+        arglist.append(elt)
+        i += 1
+    # Check kws similarly.  Checking the keys may not be strictly necessary,
+    # but better safe than sorry.  A new argument dict is created just in
+    # case kws is a hostile user-defined instance that may do horrid things
+    # as a side-effect of calling items().
+    argdict = {}
+    for k, v in kws.items():
+        guard(kws, k)
+        guard(kws, v, k)
+        argdict[k] = v
+    return func(*arglist, **argdict)
+
+safe_builtins['apply'] = builtin_guarded_apply
+
+
+# AccessControl clients generally need to set up a safe globals dict for
+# use by restricted code.  The get_safe_globals() function returns such
+# a dict, containing '__builtins__' mapped to our safe bulitins, and
+# bindings for all the special functions inserted into Python code by
+# RestrictionMutator transformations.  A client may wish to add more
+# bindings to this dict.  It's generally safe to do so, as
+# get_safe_globals returns a (shallow) copy of a canonical safe globals
+# dict.
+# Exception:  For obscure technical reasons, clients have to import
+# guarded_getattr from this module (ZopeGuards) and plug it into the
+# dict themselves, with key '_getattr_'.
+
+_safe_globals = {'__builtins__': safe_builtins,
+                 '_apply_':      guarded_apply,
+                 '_getitem_':    guarded_getitem,
+                 '_getiter_':    SafeIter,
+                 '_print_':      RestrictedPython.PrintCollector,
+                 '_write_':      full_write_guard,
+                 # The correct implementation of _getattr_, aka
+                 # guarded_getattr, isn't known until
+                 # AccessControl.Implementation figures that out, then
+                 # stuffs it into *this* module's globals bound to
+                 # 'guarded_getattr'.  We can't know what that is at
+                 ## '_getattr_':   guarded_getattr,
+                }
+
+get_safe_globals = _safe_globals.copy


=== Zope/lib/python/AccessControl/ZopeSecurityPolicy.py 1.25 => 1.26 ===
--- Zope/lib/python/AccessControl/ZopeSecurityPolicy.py:1.25	Fri Nov 28 11:44:06 2003
+++ Zope/lib/python/AccessControl/ZopeSecurityPolicy.py	Thu Jan 15 18:09:03 2004
@@ -10,215 +10,45 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''Define Zope\'s default security policy
+"""Define Zope's default security policy
 
+$Id$"""
 
-$Id$'''
-__version__='$Revision$'[11:-2]
 
+# AccessControl.Implementation inserts ZopeSecurityPolicy, getRoles
+from AccessControl.SimpleObjectPolicies import _noroles
 
-_use_python_impl = 0
-import os
-if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
-    _use_python_impl = 1
-else:
-    try:
-        # C Optimization:
-        from cAccessControl import ZopeSecurityPolicy
-        from SimpleObjectPolicies import _noroles
-    except ImportError:
-        # Fall back to Python implementation.
-        _use_python_impl = 1
+rolesForPermissionOn = None  # XXX:  avoid import loop
 
+tuple_or_list = tuple, list
 
-if 1 or _use_python_impl:
+def getRoles(container, name, value, default):
 
-    from types import StringType, UnicodeType
+    global rolesForPermissionOn  # XXX:  avoid import loop
 
-    import SimpleObjectPolicies
-    from AccessControl import Unauthorized
-    _noroles=SimpleObjectPolicies._noroles
-    from zLOG import LOG, PROBLEM
-    from Acquisition import aq_base
+    if rolesForPermissionOn is None:
+        from PermissionRole import rolesForPermissionOn
 
-    from PermissionRole import _what_not_even_god_should_do, \
-         rolesForPermissionOn
+    roles = getattr(value, '__roles__', _noroles)
+    if roles is _noroles:
+        if not name or not isinstance(name, basestring):
+            return default
 
-    tuple_or_list = tuple, list
-    def getRoles(container, name, value, default):
-        roles = getattr(value, '__roles__', _noroles)
+        cls = getattr(container, '__class__', None)
+        if cls is None:
+            return default
+        
+        roles = getattr(cls, name+'__roles__', _noroles)
         if roles is _noroles:
-            if not name or not isinstance(name, basestring):
-                return default
-
-            cls = getattr(container, '__class__', None)
-            if cls is None:
-                return default
-            
-            roles = getattr(cls, name+'__roles__', _noroles)
-            if roles is _noroles:
-                return default
+            return default
 
-            value = container
-
-        if roles is None or isinstance(roles, tuple_or_list):
-            return roles
-        
-        rolesForPermissionOn = getattr(roles, 'rolesForPermissionOn', None)
-        if rolesForPermissionOn is not None:
-            roles = rolesForPermissionOn(value)
+        value = container
 
+    if roles is None or isinstance(roles, tuple_or_list):
         return roles
-            
-
-    class ZopeSecurityPolicy:
-
-        def __init__(self, ownerous=1, authenticated=1):
-            """Create a Zope security policy.
-
-            Two optional keyword arguments may be provided:
+    
+    rolesForPermissionOn = getattr(roles, 'rolesForPermissionOn', None)
+    if rolesForPermissionOn is not None:
+        roles = rolesForPermissionOn(value)
 
-            ownerous -- Untrusted users can create code
-                        (e.g. Python scripts or templates),
-                        so check that code owners can access resources.
-                        The argument must have a truth value.
-                        The default is true.
-
-            authenticated -- Allow access to resources based on the
-                        privaledges of the authenticated user.
-                        The argument must have a truth value.
-                        The default is true.
-
-                        This (somewhat experimental) option can be set
-                        to false on sites that allow only public
-                        (unauthenticated) access. An anticipated
-                        scenario is a ZEO configuration in which some
-                        clients allow only public access and other
-                        clients allow full management.
-            """
-
-            self._ownerous=ownerous
-            self._authenticated=authenticated
-
-        def validate(self, accessed, container, name, value, context,
-                     roles=_noroles, type=type, IntType=type(0),
-                     DictType=type({}), getattr=getattr, _noroles=_noroles,
-                     StringType=type(''),
-                     Containers=SimpleObjectPolicies.Containers,
-                     valid_aq_=('aq_parent','aq_inner', 'aq_explicit')):
-
-            # Note: accessed is not used.
-
-            ############################################################
-            # Provide special rules for the acquisition attributes
-            if type(name) is StringType:
-                if name.startswith('aq_') and name not in valid_aq_:
-                    raise Unauthorized(name, value)
-
-            ############################################################
-            # If roles weren't passed in, we'll try to get them from the object
-
-            if roles is _noroles:
-                roles = getRoles(container, name, value, _noroles)
-
-            ############################################################
-            # We still might not have any roles
-
-            if roles is _noroles:
-
-                ############################################################
-                # We have an object without roles and we didn't get a list
-                # of roles passed in. Presumably, the value is some simple
-                # object like a string or a list.  We'll try to get roles
-                # from its container.
-                if container is None:
-                    # Either container or a list of roles is required
-                    # for ZopeSecurityPolicy to know whether access is
-                    # allowable.
-                    raise Unauthorized(name, value)
-
-                roles=getattr(container, '__roles__', _noroles)
-                if roles is _noroles:
-                    # Try to acquire __roles__.  If __roles__ can't be
-                    # acquired, the value is unprotected and roles is
-                    # left set to _noroles.
-                    if aq_base(container) is not container:
-                        try:
-                            roles = container.aq_acquire('__roles__')
-                        except AttributeError:
-                            pass
-
-                # We need to make sure that we are allowed to
-                # get unprotected attributes from the container. We are
-                # allowed for certain simple containers and if the
-                # container says we can. Simple containers
-                # may also impose name restrictions.
-                p=Containers(type(container), None)
-                if p is None:
-                    p=getattr(container,
-                              '__allow_access_to_unprotected_subobjects__', None)
-
-                if p is not None:
-                    tp=type(p)
-                    if tp is not IntType:
-                        if tp is DictType:
-                            p=p.get(name, None)
-                        else:
-                            p=p(name, value)
-
-                if not p:
-                    raise Unauthorized(name, value)
-
-                if roles is _noroles: return 1
-
-                # We are going to need a security-aware object to pass
-                # to allowed(). We'll use the container.
-                value=container
-
-            # Short-circuit tests if we can:
-            try:
-                if roles is None or 'Anonymous' in roles: return 1
-            except TypeError:
-                # 'roles' isn't a sequence
-                LOG('Zope Security Policy', PROBLEM, "'%s' passed as roles"
-                    " during validation of '%s' is not a sequence." % (
-                    `roles`, name))
-                raise
-
-            # Check executable security
-            stack=context.stack
-            if stack:
-                eo=stack[-1]
-
-                # If the executable had an owner, can it execute?
-                if self._ownerous:
-                    owner=eo.getOwner()
-                    if (owner is not None) and not owner.allowed(value, roles):
-                        # We don't want someone to acquire if they can't
-                        # get an unacquired!
-                        raise Unauthorized(name, value)
-
-                # Proxy roles, which are a lot safer now.
-                proxy_roles=getattr(eo, '_proxy_roles', None)
-                if proxy_roles:
-                    for r in proxy_roles:
-                        if r in roles: return 1
-
-                    # Proxy roles actually limit access!
-                    raise Unauthorized(name, value)
-
-
-            try:
-                if self._authenticated and context.user.allowed(value, roles):
-                    return 1
-            except AttributeError: pass
-
-            raise Unauthorized(name, value)
-
-
-        def checkPermission(self, permission, object, context):
-            # XXX proxy roles and executable owner are not checked
-            roles=rolesForPermissionOn(permission, object)
-            if type(roles) in (StringType, UnicodeType):
-                roles=[roles]
-            return context.user.allowed(object, roles)
+    return roles


=== Zope/lib/python/AccessControl/__init__.py 1.16 => 1.17 ===
--- Zope/lib/python/AccessControl/__init__.py:1.16	Fri Nov 28 11:44:09 2003
+++ Zope/lib/python/AccessControl/__init__.py	Thu Jan 15 18:09:03 2004
@@ -13,6 +13,9 @@
 
 from unauthorized import Unauthorized
 
+# This has to happen early so things get initialized properly
+from Implementation import setImplementation
+
 from SecurityManagement import getSecurityManager, setSecurityPolicy
 from SecurityInfo import ClassSecurityInfo, ModuleSecurityInfo
 from SecurityInfo import ACCESS_PRIVATE
@@ -20,7 +23,7 @@
 from SecurityInfo import ACCESS_NONE
 from SecurityInfo import secureModule, allow_module, allow_class
 from SimpleObjectPolicies import allow_type
-from ZopeGuards import full_read_guard, full_write_guard, safe_builtins
+from ZopeGuards import full_write_guard, safe_builtins
 
 ModuleSecurityInfo('AccessControl').declarePublic('getSecurityManager')
 




More information about the Zope-Checkins mailing list