[Zope-Checkins] CVS: Zope/lib/python/AccessControl - ImplC.py:1.1.2.1 ImplPython.py:1.1.2.1 Implementation.py:1.1.2.1 DTML.py:1.10.70.1 PermissionRole.py:1.18.2.2 Role.py:1.56.14.2 SecurityInfo.py:1.16.32.3 SecurityManagement.py:1.8.4.1 SecurityManager.py:1.13.70.1 SimpleObjectPolicies.py:1.12.70.1 ZopeGuards.py:1.16.2.1 ZopeSecurityPolicy.py:1.23.2.2 __init__.py:1.15.70.1 cAccessControl.c:1.20.2.3

Tres Seaver tseaver at zope.com
Thu Jan 8 18:34:16 EST 2004


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

Modified Files:
      Tag: Zope-2_7-branch
	DTML.py PermissionRole.py Role.py SecurityInfo.py 
	SecurityManagement.py SecurityManager.py 
	SimpleObjectPolicies.py ZopeGuards.py ZopeSecurityPolicy.py 
	__init__.py cAccessControl.c 
Added Files:
      Tag: Zope-2_7-branch
	ImplC.py ImplPython.py Implementation.py 
Log Message:


  Merge security audit work for the 2.7 branch:

    - Collector #1140: setting the access control implementation from
      the configuration file didn't work.  The ZOPE_SECURITY_POLICY
      environment variable is no longer honored.

    - Browsers that do not escape html in query strings such as 
      Internet Explorer 5.5 could potentially send a script tag in a 
      query string to the ZSearch interface for cross-site scripting.

    - FilteredSets (used within TopicIndex) are defined via an expression,
      which was naievely eval'ed.

    - The ZTUtils SimpleTree decompressed tree state data from the 
      request without checking for final size, which could allow for 
      certain types of DoS attacks.

    - Inadequate security assertions on administrative "find" methods 
      could potentially be abused.

    - Some improper security assertions on DTMLDocument objects could 
      potentially allow access to members that should be 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.

    - The dtml-tree tag used an "eval" of user-supplied data; its 
      efforts to prevent abuse were ineffective.

    - XML-RPC marshalling of class instances used the instance 
      __dict__ to marshal the object, and could include attributes 
      prefixed with an underscore name. These attributes are considered 
      private in Zope and should generally not be disclosed.

    - Some property types were stored in a mutable data type (list) which 
      could potentially allow untrusted code to effect changes on those 
      properties without going through appropriate security checks in 
      particular scenarios.

    - Inadequate type checking could allow unicode values passed to 
      RESPONSE.write() to be passed into deeper layers of asyncore, 
      where an exception would eventually be generated at a level that 
      would cause the Zserver main loop to terminate.

    - The variables bound to page templates and Python scripts such as 
      "context" and "container" were not checked adequately, allowing 
      a script to potentially access those objects without ensuring the 
      necessary permissions on the part of the executing user.

    - 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 (min, max, enumerate, iter, sum)
      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.

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



=== Added File Zope/lib/python/AccessControl/ImplC.py ===
##############################################################################
#
# 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
    """


=== Added File Zope/lib/python/AccessControl/ImplPython.py ===
##############################################################################
#
# 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))


def rolesForPermissionOn(perm, object, default=('Manager',)):
    """Return the roles that have the given permission on the given object
    """
    im = imPermissionRole()
    im._p = '_' + string.translate(perm, name_trans) + "_Permission"
    im._d = default
    return im.__of__(object)


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


class imPermissionRole(Base):
    """Implement permission-based roles"""

    def __of__(self, parent):
        obj = parent
        n = self._p
        r = None
        while 1:
            if hasattr(obj, n):
                roles = getattr(obj, n)

                if roles is None:
                    return 'Anonymous',

                t = roles.__class__

                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 issubclass(t, basestring):
                    # 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: r = self._d

        return r

    # 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
# --------------------------------

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 = getattr(value, '__roles__', roles)

        ############################################################
        # 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, 'validateValue': 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 validateValue(self, value, roles=_noroles):
        """Convenience for common case of simple value validation.
        """
        policy = self._policy
        if roles is _noroles:
            return policy.validate(None, None, None, value,
                                   self._context)
        else:
            return policy.validate(None, None, None, value,
                                   self._context, roles)

    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, obj, name, v, validate):
    return validate(inst, obj, 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


=== Added File Zope/lib/python/AccessControl/Implementation.py ===
##############################################################################
#
# 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'.
    """
    import sys
    global _implementation_name
    #
    name = name.upper()
    if name == _implementation_name:
        return
    if name == "C":
        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")


=== Zope/lib/python/AccessControl/DTML.py 1.10 => 1.10.70.1 ===
--- Zope/lib/python/AccessControl/DTML.py:1.10	Wed Aug 14 17:29:07 2002
+++ Zope/lib/python/AccessControl/DTML.py	Thu Jan  8 18:33:43 2004
@@ -19,33 +19,8 @@
 import SecurityManagement, string, math, whrandom, random
 import DocumentTemplate.sequence
 
-from ZopeGuards import guarded_getattr, guarded_getitem
 
-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


=== Zope/lib/python/AccessControl/PermissionRole.py 1.18.2.1 => 1.18.2.2 ===
--- Zope/lib/python/AccessControl/PermissionRole.py:1.18.2.1	Thu Oct 23 21:25:52 2003
+++ Zope/lib/python/AccessControl/PermissionRole.py	Thu Jan  8 18:33:43 2004
@@ -10,143 +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 _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, object, default=('Manager',)):
-        """Return the roles that have the given permission on the given object
-        """
-        im=imPermissionRole()
-        im._p='_'+string.translate(perm, name_trans)+"_Permission"
-        im._d=default
-        return im.__of__(object)
-
-
-    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
-
-
-    # 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, parent,tt=type(()),st=type(''),ut=type(u''),
-                   getattr=getattr):
-            obj=parent
-            n=self._p
-            r=None
-            while 1:
-                if hasattr(obj,n):
-                    roles=getattr(obj, n)
-
-                    if roles is None: return 'Anonymous',
-
-                    t=type(roles)
-
-                    if t is tt:
-                        # If we get a tuple, then we don't acquire
-                        if r is None: return roles
-                        return r+list(roles)
-
-                    if t in (st, ut):
-                        # 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: r=self._d
-
-            return r
-
-        # 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)
+$Id$
+"""
 
 ##############################################################################
 # Test functions:
@@ -166,8 +34,6 @@
         def x(self): pass
         def y(self): pass
         def z(self): pass
-
-
 
     a=I()
     a.b=I()


=== Zope/lib/python/AccessControl/Role.py 1.56.14.1 => 1.56.14.2 ===
--- Zope/lib/python/AccessControl/Role.py:1.56.14.1	Mon Nov 17 17:34:01 2003
+++ Zope/lib/python/AccessControl/Role.py	Thu Jan  8 18:33:43 2004
@@ -33,7 +33,7 @@
     return not aq_get(self, '_isBeingUsedAsAMethod_', 0)
 
 class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
-    """An obect that has configurable permissions"""
+    """An object that has configurable permissions"""
 
     __ac_permissions__=(
         ('Change permissions',
@@ -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.16.32.2 => 1.16.32.3 ===
--- Zope/lib/python/AccessControl/SecurityInfo.py:1.16.32.2	Tue Oct 21 09:41:01 2003
+++ Zope/lib/python/AccessControl/SecurityInfo.py	Thu Jan  8 18:33:43 2004
@@ -36,19 +36,19 @@
 
    ACCESS_NONE:    no access
 
+$Id$
 """
 
-__version__='$Revision$'[11:-2]
-
-
-import Acquisition, PermissionRole, sys
+import sys
+import Acquisition
 from zLOG import LOG, WARNING
+from AccessControl.ImplPython import _what_not_even_god_should_do
 
 
 # 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.8.4.1 ===
--- Zope/lib/python/AccessControl/SecurityManagement.py:1.8	Wed May 14 17:51:23 2003
+++ Zope/lib/python/AccessControl/SecurityManagement.py	Thu Jan  8 18:33:43 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.13 => 1.13.70.1 ===
--- Zope/lib/python/AccessControl/SecurityManager.py:1.13	Wed Aug 14 17:29:07 2002
+++ Zope/lib/python/AccessControl/SecurityManager.py	Thu Jan  8 18:33:43 2004
@@ -10,203 +10,22 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''short description
+"""API Module for access to the security policy / manager.
 
+$Id$
+"""
+from AccessControl import ImplPython as _ImplPython
+from AccessControl.SimpleObjectPolicies import _noroles
 
-$Id$'''
-__version__='$Revision$'[11:-2]
 
-import ZopeSecurityPolicy, os
-
-_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, 'validateValue': 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 validateValue(self, value, roles=_noroles):
-        """Convenience for common case of simple value validation.
-        """
-        policy=self._policy
-        if roles is _noroles:
-            return policy.validate(None, None, None, value,
-                                   self._context)
-        else:
-            return policy.validate(None, None, None, value,
-                                   self._context, roles)
-
-    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.12.70.1 ===
--- Zope/lib/python/AccessControl/SimpleObjectPolicies.py:1.12	Wed Aug 14 17:29:07 2002
+++ Zope/lib/python/AccessControl/SimpleObjectPolicies.py	Thu Jan  8 18:33:43 2004
@@ -10,22 +10,70 @@
 # 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.
 
-$Id$'''
-__version__='$Revision$'[11:-2]
+The rules are expressed as a mapping from type -> assertion
 
-_noroles=[] # this is imported from various places
+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$
+"""
+
+_noroles = [] # this is imported from 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 +93,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.16.2.1 ===
--- Zope/lib/python/AccessControl/ZopeGuards.py:1.16	Tue Jun 10 11:39:04 2003
+++ Zope/lib/python/AccessControl/ZopeGuards.py	Thu Jan  8 18:33:43 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,160 @@
     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 the implementation can be reused.
+
+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 +238,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 +267,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 +294,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 +338,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.23.2.1 => 1.23.2.2 ===
--- Zope/lib/python/AccessControl/ZopeSecurityPolicy.py:1.23.2.1	Thu Oct 23 21:25:52 2003
+++ Zope/lib/python/AccessControl/ZopeSecurityPolicy.py	Thu Jan  8 18:33:43 2004
@@ -10,189 +10,11 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-__doc__='''Define Zope\'s default security policy
+"""Define Zope\'s default security policy
 
+$Id$
+"""
 
-$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 ZopeSecurityPolicy
-        from SimpleObjectPolicies import _noroles
-    except ImportError:
-        # Fall back to Python implementation.
-        _use_python_impl = 1
-
-
-if _use_python_impl:
-
-    from types import StringType, UnicodeType
-
-    import SimpleObjectPolicies
-    from AccessControl import Unauthorized
-    _noroles=SimpleObjectPolicies._noroles
-    from zLOG import LOG, PROBLEM
-    from Acquisition import aq_base
-
-    from PermissionRole import _what_not_even_god_should_do, \
-         rolesForPermissionOn
-
-
-    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, 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=getattr(value, '__roles__', _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)
+# AccessControl.Implementation inserts ZopeSecurityPolicy.
+  
+from AccessControl.SimpleObjectPolicies import _noroles


=== Zope/lib/python/AccessControl/__init__.py 1.15 => 1.15.70.1 ===
--- Zope/lib/python/AccessControl/__init__.py:1.15	Wed Aug 14 17:29:07 2002
+++ Zope/lib/python/AccessControl/__init__.py	Thu Jan  8 18:33:43 2004
@@ -16,6 +16,9 @@
 import DTML
 del DTML
 
+# 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
@@ -23,6 +26,10 @@
 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
 
 ModuleSecurityInfo('AccessControl').declarePublic('getSecurityManager')
+
+from ZopeGuards import full_write_guard, safe_builtins
+
+# XXX There used to be a top-level full_read_guard name, but we
+# believe it was a decoy.


=== Zope/lib/python/AccessControl/cAccessControl.c 1.20.2.2 => 1.20.2.3 ===
--- Zope/lib/python/AccessControl/cAccessControl.c:1.20.2.2	Thu Oct 23 21:25:52 2003
+++ Zope/lib/python/AccessControl/cAccessControl.c	Thu Jan  8 18:33:43 2004
@@ -354,6 +354,7 @@
 static PyObject *rolesForPermissionOn(PyObject *self, PyObject *args);
 static PyObject *module_guarded_getattr(PyObject *self, PyObject *args);
 static PyObject *module_aq_validate(PyObject *self, PyObject *args);
+static PyObject *module_setDefaultBehaviors(PyObject *self, PyObject *args);
 static PyObject *c_rolesForPermissionOn(PyObject *self, PyObject *perm,
                                         PyObject *object, PyObject *deflt);
 
@@ -395,7 +396,12 @@
 		(PyCFunction)module_aq_validate,
 		METH_VARARGS,
 		""
-        },               
+        },
+	{"setDefaultBehaviors",
+		(PyCFunction)module_setDefaultBehaviors,
+		METH_VARARGS,
+		""
+	},
 	{ NULL, NULL }
 };
 
@@ -642,6 +648,7 @@
 */
 
 static PyObject *Containers = NULL;
+static PyObject *ContainerAssertions = NULL;
 static PyObject *Unauthorized = NULL;
 static PyObject *LOG = NULL;
 static PyObject *PROBLEM = NULL;
@@ -660,9 +667,14 @@
 static PyObject *_proxy_roles_str = NULL;
 static PyObject *allowed_str = NULL;
 static PyObject *getOwner_str = NULL;
+static PyObject *getPhysicalRoot_str = NULL;
 static PyObject *checkPermission_str = NULL;
 static PyObject *getSecurityManager = NULL;
+static PyObject *unrestrictedTraverse_str = NULL;
 static PyObject *aq_validate = NULL;
+static PyObject *aq_parent_str = NULL;
+static PyObject *_check_context_str = NULL;
+
 static int ownerous = 1;
 static int authenticated = 1;
 
@@ -697,6 +709,15 @@
           return -1;
 	UNLESS (allowed_str = PyString_FromString("allowed")) return -1;
 	UNLESS (getOwner_str = PyString_FromString("getOwner")) return -1;
+	UNLESS (getPhysicalRoot_str = PyString_FromString("getPhysicalRoot")) 
+	  return -1;
+	UNLESS (aq_parent_str = PyString_FromString("aq_parent")) return -1;
+	UNLESS (_check_context_str = PyString_FromString("_check_context")) 
+	  return -1;
+	UNLESS (unrestrictedTraverse_str = PyString_FromString(
+					   "unrestrictedTraverse")) 
+	  return -1;
+
 	UNLESS (checkPermission_str = PyString_FromString("checkPermission")) 
           return -1;
         UNLESS (__allow_access_to_unprotected_subobjects__ = 
@@ -704,10 +725,6 @@
                 "__allow_access_to_unprotected_subobjects__"))
           return -1;
 
-	if (getenv("ZSP_OWNEROUS_SKIP") != NULL) ownerous = 0;
-	if (getenv("ZSP_AUTHENTICATED_SKIP") != NULL) authenticated = 0;
-
-
 	return 0;
 }
 
@@ -746,8 +763,18 @@
 	PyObject *rval = NULL;
 	PyObject *stack = NULL;
 	PyObject *user = NULL;
+
+
+	PyObject *method = NULL;
+	PyObject *tmp = NULL;
+	PyObject *udb = NULL;
+	PyObject *root = NULL;
+	PyObject *item = NULL;
+
 	char *sname;
 
+        int i, l, contains;
+        PyObject *r;
 
 	/*| def validate(self, accessed, container, name, value, context
 	**|	roles=_noroles ...
@@ -867,24 +894,38 @@
 		**|    tp = type(p)
 		**|    if tp is not IntType:
 		**|       if tp is DictType:
-		**|          p = p.get(name, None)
+                **|         if (isinstance(name, StringType) or
+                **|             isinstance(name, UnicodeType)):
+                **|             p=p.get(name, None)
+                **|         else:
+                **|             p = 1
 		**|       else:
 		**|          p = p(name, value)
 		*/
 
-		if (p) {
-			if (! PyInt_Check(p)) {
-				if (PyDict_Check(p)) {
-                                        ASSIGN(p, PyObject_GetItem(p, name));
-                                        if (p == NULL)
-                                          PyErr_Clear();
-				} else {
-                                  ASSIGN(p, callfunction2(p, name, value));
-                                  if (p == NULL)
-                                    goto err;
-				}
-			}
-		}
+		if (p) 
+                  {
+                    if (! PyInt_Check(p)) 
+                      {
+                        if (PyDict_Check(p)) 
+                          {
+                            if (PyString_Check(name) || PyUnicode_Check(name))
+                              {
+                                ASSIGN(p, PyObject_GetItem(p, name));
+                                if (p == NULL)
+                                  PyErr_Clear();
+                              }
+                            else
+                              p = PyInt_FromLong(1);
+                          } 
+                        else 
+                          {
+                            ASSIGN(p, callfunction2(p, name, value));
+                            if (p == NULL)
+                              goto err;
+                          }
+                      }
+                  }
 
 		/*| if not p:
 		**|     raise Unauthorized, cleanupName(name, value)
@@ -1019,6 +1060,24 @@
 	/*|    # 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
 	**|
@@ -1034,8 +1093,82 @@
                   }
                 else if (PyObject_IsTrue(proxy_roles)) 
                   {
-                    int i, l, contains=0;
-                    PyObject *r;
+
+		    /* patch!! --------------------------------  */
+
+		    method = PyObject_GetAttr(eo, getOwner_str);
+		    if (method == NULL) {
+		      goto err;
+		    }
+
+		    owner = PyObject_CallObject(method, NULL);
+		    Py_DECREF(method);
+
+		    if (owner == NULL) {
+		      goto err;
+		    }
+
+
+		    if (PyObject_IsTrue(owner)) {
+		      if (!PyObject_HasAttr(owner, aq_parent_str)) {
+			item = PyInt_FromLong(1);
+		        tmp = callmethod1(eo, getOwner_str, item);
+			Py_XDECREF(item);
+			udb = PySequence_GetItem(tmp, 0);
+			Py_XDECREF(tmp);
+			if (udb == NULL) {
+			  Py_DECREF(owner);
+			  goto err;
+			}
+
+			method = PyObject_GetAttr(container, 
+						  getPhysicalRoot_str);
+			if (method == NULL) {
+			  Py_DECREF(owner);
+			  Py_DECREF(udb);
+			}
+			root = PyObject_CallObject(method, NULL);
+			Py_DECREF(method);
+			
+			ASSIGN(udb, callmethod1(root, unrestrictedTraverse_str,
+						udb)); 
+			if (udb == NULL) {
+			  Py_DECREF(owner);
+			  Py_DECREF(udb);
+			}
+
+			ASSIGN(owner, callmethod1(owner, __of__, udb));
+			Py_DECREF(udb);
+
+
+		      }
+		    }
+
+
+		    if (owner != Py_None) {
+		      PyObject *tmp = callmethod1(owner, 
+						  _check_context_str,
+						  container
+						  );
+		      if (tmp == NULL) {
+			Py_DECREF(owner);
+			goto err;
+		      }
+
+		      if (!PyObject_IsTrue(tmp)) {
+			Py_DECREF(owner);
+			Py_DECREF(tmp);
+			unauthErr(name, value);
+			goto err;
+		      }
+		      Py_DECREF(owner);
+		    }
+		    		    
+		    /* ------------------------------------------- */
+
+
+
+                    contains = 0;
                     if (PyTuple_Check(proxy_roles)) 
                       {
                         l=PyTuple_GET_SIZE(proxy_roles);
@@ -1942,16 +2075,74 @@
         }
 
       /*
-        if Containers(type(inst)):
-            # Simple type.  Short circuit.
-            return v
+            assertion = Containers(type(inst))
+            if type(assertion) is DictType:
+                # 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
       */
-      t=callfunction1(Containers, OBJECT(inst->ob_type));
-      if (t==NULL) goto err;
-      i=PyObject_IsTrue(t);
-      if (i < 0) goto err;
-      Py_DECREF(t);
-      if (i) return v;
+      t = PyDict_GetItem(ContainerAssertions, OBJECT(inst->ob_type));
+      if (t != NULL)
+        {
+          if (PyDict_Check(t))
+            {
+              PyObject *attrv;
+              
+              attrv = PyDict_GetItem(t, name);
+              if (attrv != NULL)
+                {
+                  i=PyObject_IsTrue(attrv);
+                  if (i < 0) goto err;
+                  if (i) 
+                    {
+                      if (attrv->ob_type->tp_call)
+                        {
+                          Py_DECREF(v);
+                          v = callfunction2(attrv, inst, name);
+                          return v;
+                        }
+                      return v;
+                    }
+                }
+              Py_DECREF(v);
+              goto unauth;
+            }
+          
+          i = PyObject_IsTrue(t);
+          if (i < 0) goto err;
+          if (i)
+            {
+              if (t->ob_type->tp_call)
+                {
+                  PyObject *ignored;
+                  ignored = callfunction2(t, name, v);
+                  if (ignored == NULL)
+                    {
+                      /* veto */
+                      Py_DECREF(v);
+                      return NULL;
+                    }
+                  Py_DECREF(ignored);
+                }
+              return v;
+            }
+        }
 
       /*
         # Filter out the objects we can't access.
@@ -1982,6 +2173,7 @@
       return NULL;
     }
 
+ unauth:
   /* raise Unauthorized, name */
   PyErr_SetObject(Unauthorized, name);
   return NULL;
@@ -2022,6 +2214,21 @@
 }
 
 static PyObject *
+module_setDefaultBehaviors(PyObject *ignored, PyObject *args)
+{
+  PyObject *result = NULL;
+  int own, auth;
+
+  if (PyArg_ParseTuple(args, "ii:setDefaultBehaviors", &own, &auth)) {
+    ownerous = own;
+    authenticated = authenticated;
+    result = Py_None;
+    Py_INCREF(result);
+  }
+  return result;
+}
+
+static PyObject *
 dtml_guarded_getattr(PyObject *self, PyObject *args)
 {
   PyObject *ob, *name, *default_=0, *validate;
@@ -2118,6 +2325,7 @@
 
 	IMPORT(module, "AccessControl.SimpleObjectPolicies");
 	GETATTR(module, Containers);
+	GETATTR(module, ContainerAssertions);
 	Py_DECREF(module);
 	module = NULL;
 




More information about the Zope-Checkins mailing list