[Zope-Checkins] CVS: Zope/lib/python/AccessControl - unauthorized.py:1.2 PermissionRole.py:1.12 SecurityManager.py:1.7 User.py:1.160 ZopeGuards.py:1.7 ZopeSecurityPolicy.py:1.14 __init__.py:1.12 cAccessControl.c:1.11 cPermissionRole.py:NONE cZopeSecurityPolicy.py:NONE pPermissionRole.py:NONE pZopeSecurityPolicy.py:NONE

Shane Hathaway shane@digicool.com
Fri, 19 Oct 2001 11:12:56 -0400


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

Modified Files:
	PermissionRole.py SecurityManager.py User.py ZopeGuards.py 
	ZopeSecurityPolicy.py __init__.py cAccessControl.c 
Added Files:
	unauthorized.py 
Removed Files:
	cPermissionRole.py cZopeSecurityPolicy.py pPermissionRole.py 
	pZopeSecurityPolicy.py 
Log Message:
- Merged cAccessControl-review-branch.

- Made some corrections to the DTML tests, which aren't currently working
in testrunner but work when run directly. ??  


=== Zope/lib/python/AccessControl/unauthorized.py 1.1 => 1.2 ===
+# 
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+# 
+# Copyright (c) Digital Creations.  All rights reserved.
+# 
+# This license has been certified as Open Source(tm).
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+# 1. Redistributions in source code must retain the above copyright
+#    notice, this list of conditions, and the following disclaimer.
+# 
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions, and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+# 
+# 3. Digital Creations requests that attribution be given to Zope
+#    in any manner possible. Zope includes a "Powered by Zope"
+#    button that is installed by default. While it is not a license
+#    violation to remove this button, it is requested that the
+#    attribution remain. A significant investment has been put
+#    into Zope, and this effort will continue if the Zope community
+#    continues to grow. This is one way to assure that growth.
+# 
+# 4. All advertising materials and documentation mentioning
+#    features derived from or use of this software must display
+#    the following acknowledgement:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    In the event that the product being advertised includes an
+#    intact Zope distribution (with copyright and license included)
+#    then this clause is waived.
+# 
+# 5. Names associated with Zope or Digital Creations must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission from Digital Creations.
+# 
+# 6. Modified redistributions of any form whatsoever must retain
+#    the following acknowledgment:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    Intact (re-)distributions of any official Zope release do not
+#    require an external acknowledgement.
+# 
+# 7. Modifications are encouraged but must be packaged separately as
+#    patches to official Zope releases.  Distributions that do not
+#    clearly separate the patches from the original work must be clearly
+#    labeled as unofficial distributions.  Modifications which do not
+#    carry the name Zope may be packaged in any form, as long as they
+#    conform to all of the clauses above.
+# 
+# 
+# Disclaimer
+# 
+#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#   SUCH DAMAGE.
+# 
+# 
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations.  Specific
+# attributions are listed in the accompanying credits file.
+# 
+##############################################################################
+"""Access control exceptions
+"""
+
+import zExceptions
+
+class Unauthorized(zExceptions.Unauthorized):
+                            
+    def getValueName(self):
+        v=self.value
+        n=getattr(v, 'getId', v)
+        if n is v:  n=getattr(v, 'id', v)
+        if n is v:  n=getattr(v, '__name__', v)
+        if n is not v:
+            if callable(n):
+                try: n = n()
+                except TypeError: pass
+            return n
+
+        c = getattr(v, '__class__', type(v))
+        c = getattr(c, '__name__', 'object')
+        return "a particular %s" % c
+        


=== Zope/lib/python/AccessControl/PermissionRole.py 1.11 => 1.12 ===
 __version__='$Revision$'[11:-2]
 
-if 0: # cAccessControl is not working
-    import cAccessControl
-    rolesForPermissionOn=cAccessControl.rolesForPermissionOn
-    PermissionRole=cAccessControl.PermissionRole
-    imPermissionRole=cAccessControl.imPermissionRole
-    _what_not_even_god_should_do= cAccessControl._what_not_even_god_should_do
+_use_python_impl = 0
+import os
+if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
+    _use_python_impl = 1
 else:
-    import pPermissionRole
-    from pPermissionRole import rolesForPermissionOn, PermissionRole
-    from pPermissionRole import imPermissionRole, _what_not_even_god_should_do
+    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(string.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=default
+
+        def __of__(self, parent, None=None, 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(''),getattr=getattr,None=None):
+            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 is st:
+                        # 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)
 
 ############################################################################## 
 # Test functions:


=== Zope/lib/python/AccessControl/SecurityManager.py 1.6 => 1.7 ===
         policy=self._policy
         if policy is None: policy=_defaultPolicy
-        return policy.validate(accessed, container, name, value,
-                               self._context, roles)
+        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):
@@ -175,15 +179,19 @@
         policy=self._policy
         if policy is None: policy=_defaultPolicy
         return policy.validate(accessed, container, name, value,
-                               self._context, _noroles)
+                               self._context)
 
     def validateValue(self, value, roles=_noroles):
         """Convenience for common case of simple value validation.
         """
         policy=self._policy
         if policy is None: policy=_defaultPolicy
-        return policy.validate(None, None, None, value,
-                               self._context, roles)
+        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


=== Zope/lib/python/AccessControl/User.py 1.159 => 1.160 ===
 from PermissionRole import _what_not_even_god_should_do, rolesForPermissionOn
 import AuthEncoding
-from AccessControl import getSecurityManager, Unauthorized
+from AccessControl import getSecurityManager
+from zExceptions import Unauthorized
 from AccessControl.SecurityManagement import newSecurityManager
 from AccessControl.SecurityManagement import noSecurityManager
 from AccessControl.ZopeSecurityPolicy import _noroles


=== Zope/lib/python/AccessControl/ZopeGuards.py 1.6 => 1.7 ===
 from SecurityInfo import secureModule
 from SimpleObjectPolicies import Containers
-
-Unauthorized = 'Unauthorized'
+from zExceptions import Unauthorized
 
 _marker = []  # Create a new marker object.
 


=== Zope/lib/python/AccessControl/ZopeSecurityPolicy.py 1.13 => 1.14 ===
+##############################################################################
+# 
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+# 
+# Copyright (c) Digital Creations.  All rights reserved.
+# 
+# This license has been certified as Open Source(tm).
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+# 1. Redistributions in source code must retain the above copyright
+#    notice, this list of conditions, and the following disclaimer.
+# 
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions, and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+# 
+# 3. Digital Creations requests that attribution be given to Zope
+#    in any manner possible. Zope includes a "Powered by Zope"
+#    button that is installed by default. While it is not a license
+#    violation to remove this button, it is requested that the
+#    attribution remain. A significant investment has been put
+#    into Zope, and this effort will continue if the Zope community
+#    continues to grow. This is one way to assure that growth.
+# 
+# 4. All advertising materials and documentation mentioning
+#    features derived from or use of this software must display
+#    the following acknowledgement:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    In the event that the product being advertised includes an
+#    intact Zope distribution (with copyright and license included)
+#    then this clause is waived.
+# 
+# 5. Names associated with Zope or Digital Creations must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission from Digital Creations.
+# 
+# 6. Modified redistributions of any form whatsoever must retain
+#    the following acknowledgment:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    Intact (re-)distributions of any official Zope release do not
+#    require an external acknowledgement.
+# 
+# 7. Modifications are encouraged but must be packaged separately as
+#    patches to official Zope releases.  Distributions that do not
+#    clearly separate the patches from the original work must be clearly
+#    labeled as unofficial distributions.  Modifications which do not
+#    carry the name Zope may be packaged in any form, as long as they
+#    conform to all of the clauses above.
+# 
+# 
+# Disclaimer
+# 
+#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#   SUCH DAMAGE.
+# 
+# 
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations.  Specific
+# attributions are listed in the accompanying credits file.
+# 
+##############################################################################
+__doc__='''Define Zope\'s default security policy
 
-from SimpleObjectPolicies import _noroles
 
-if 0: # cAccessControl is not working
-    import cAccessControl
+$Id$'''
+__version__='$Revision$'[11:-2]
 
-    ZopeSecurityPolicy = cAccessControl.ZopeSecurityPolicy
+
+_use_python_impl = 0
+import os
+if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
+    _use_python_impl = 1
 else:
-    from pZopeSecurityPolicy import ZopeSecurityPolicy
+    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
+
+    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):
+            self._ownerous=ownerous
+
+        def validate(self, accessed, container, name, value, context,
+                     roles=_noroles, None=None, type=type, IntType=type(0),
+                     DictType=type({}), getattr=getattr, _noroles=_noroles,
+                     StringType=type(''),
+                     Containers=SimpleObjectPolicies.Containers,
+                     valid_aq_=('aq_parent','aq_explicit')):
+
+
+            ############################################################
+            # Provide special rules for the acquisition attributes
+            if type(name) is StringType:
+                if name[:3]=='aq_' and name not in valid_aq_:
+                    return 0
+
+            containerbase = aq_base(container)
+            accessedbase=getattr(accessed, 'aq_base', container)
+
+            ############################################################
+            # 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: return 0 # Bail if no container
+
+                roles=getattr(container, '__roles__', _noroles)
+                if roles is _noroles:
+                    aq=getattr(container, 'aq_acquire', None)
+                    if aq is None:
+                        roles=_noroles
+                        if containerbase is not accessedbase: return 0
+                    else:
+                        # Try to acquire roles
+                        try: roles=aq('__roles__')
+                        except AttributeError:
+                            roles=_noroles
+                            if containerbase is not accessedbase: return 0
+
+                # 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:
+                    if (containerbase is accessedbase):
+                        raise Unauthorized(name, value)
+                    else:
+                        return 0
+
+                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!
+                        if accessedbase is containerbase:
+                            raise Unauthorized(name, value)
+                        return 0
+
+                # 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!
+                    if accessedbase is containerbase:
+                        raise Unauthorized(name, value)
+
+                    return 0
+
+
+            try:
+                if context.user.allowed(value, roles): return 1
+            except AttributeError: pass
+
+            # We don't want someone to acquire if they can't get an unacquired!
+            if accessedbase is containerbase:
+                raise Unauthorized(name, value)
+
+            return 0
+
+        def checkPermission(self, permission, object, context):
+            # XXX proxy roles and executable owner are not checked
+            roles=rolesForPermissionOn(permission, object)
+            if type(roles) is StringType:
+                roles=[roles]
+            return context.user.allowed(object, roles)
+


=== Zope/lib/python/AccessControl/__init__.py 1.11 => 1.12 ===
 ##############################################################################
 
-Unauthorized = 'Unauthorized'
+from unauthorized import Unauthorized
 
 import DTML
 del DTML


=== Zope/lib/python/AccessControl/cAccessControl.c 1.10 => 1.11 === (1568/1668 lines abridged)
 #include "Acquisition.h"
 
+
+static void
+PyVar_Assign(PyObject **v,  PyObject *e)
+{
+  Py_XDECREF(*v);
+  *v=e;
+}
+
+#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
+#define UNLESS(E) if (!(E))
 #define OBJECT(o) ((PyObject *) (o))
 
-#ifdef win32
-#define PUBLIC
-#else
-#define PUBLIC
-#endif
+static PyObject *
+callmethod1(PyObject *self, PyObject *name, PyObject *arg)
+{
+  UNLESS(self = PyObject_GetAttr(self,name)) return NULL;
+  name = PyTuple_New(1);
+  if (name == NULL) {
+    Py_DECREF(self);
+    return NULL;
+  }
+  Py_INCREF(arg);
+  PyTuple_SET_ITEM(name, 0, arg);
+  ASSIGN(self, PyObject_CallObject(self, name));
+  Py_DECREF(name);
+  return self;
+}
+
+static PyObject *
+callfunction2(PyObject *function, PyObject *arg0, PyObject *arg1)
+{
+  PyObject *t, *r;
+  t = PyTuple_New(2);
+  if (t == NULL)
+    return NULL;
+  Py_INCREF(arg0);
+  Py_INCREF(arg1);
+  PyTuple_SET_ITEM(t, 0, arg0);
+  PyTuple_SET_ITEM(t, 1, arg1);
+  r = PyObject_CallObject(function, t);
+  Py_DECREF(t);
+  return r;
+}

[-=- -=- -=- 1568 lines omitted -=- -=- -=-]

 	PyDict_SetItemString(dict, "__version__",
 		PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
 
-	if (!ZopeSecurityPolicy_setup()) {
-		Py_FatalError("Can't initialize module cAccessControl "
-			"-- dependancies failed to load.");
-		return;
-	}
-
 	PyDict_SetItemString(dict, "_what_not_even_god_should_do",
 		_what_not_even_god_should_do);
 
@@ -1831,13 +1489,31 @@
 	PyExtensionClass_Export(dict, "imPermissionRole",
 		imPermissionRoleType);
 
-	imPermissionRoleObj = PyDict_GetItemString(dict, "imPermissionRole");
+ 	imPermissionRoleObj = PyDict_GetItemString(dict, "imPermissionRole");
 
-#if ZSPCACHE
-	ZSPCacheInit();
-#endif
+	/*| from SimpleObjectPolicies import Containers
+	*/
 
-	if (PyErr_Occurred())
-		Py_FatalError("Can't initialize module cAccessControl");
+	IMPORT(module, "AccessControl.SimpleObjectPolicies");
+	GETATTR(module, Containers);
+	Py_DECREF(module);
+	module = NULL;
+
+	/*| from unauthorized import Unauthorized
+	*/
+
+	IMPORT(module, "AccessControl.unauthorized");
+	GETATTR(module, Unauthorized);
+	Py_DECREF(module);
+	module = NULL;
+
+	/*| from zLOG import LOG, PROBLEM
+	*/
+
+	IMPORT(module, "zLOG");
+	GETATTR(module, LOG);
+	GETATTR(module, PROBLEM);
+	Py_DECREF(module);
+	module = NULL;
 }
 

=== Removed File Zope/lib/python/AccessControl/cPermissionRole.py ===

=== Removed File Zope/lib/python/AccessControl/cZopeSecurityPolicy.py ===

=== Removed File Zope/lib/python/AccessControl/pPermissionRole.py ===

=== Removed File Zope/lib/python/AccessControl/pZopeSecurityPolicy.py ===