[Zope-Checkins] CVS: Zope/lib/python/AccessControl - PermissionRole.py:1.10.30.1 User.py:1.158.18.1 ZopeGuards.py:1.6.28.1 ZopeSecurityPolicy.py:1.12.30.1 __init__.py:1.11.58.1 cAccessControl.c:1.10.12.1 cPermissionRole.py:NONE cZopeSecurityPolicy.py:NONE pPermissionRole.py:NONE pZopeSecurityPolicy.py:NONE

Jim Fulton jim@zope.com
Mon, 15 Oct 2001 17:23:14 -0400


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

Modified Files:
      Tag: cAccessControl-review-branch
	PermissionRole.py User.py ZopeGuards.py ZopeSecurityPolicy.py 
	__init__.py cAccessControl.c 
Removed Files:
      Tag: cAccessControl-review-branch
	cPermissionRole.py cZopeSecurityPolicy.py pPermissionRole.py 
	pZopeSecurityPolicy.py 
Log Message:
Checking in changes made during review of new cAccessControl
implementation. The changes fall into two classes:

- Changes made to C code

- Switching to use of Unauthorized class exception.
  This one is especially significant. :)


=== Zope/lib/python/AccessControl/PermissionRole.py 1.10 => 1.10.30.1 ===
 __version__='$Revision$'[11:-2]
 
-import cAccessControl
-rolesForPermissionOn=cAccessControl.rolesForPermissionOn
-PermissionRole=cAccessControl.PermissionRole
-imPermisionRole=cAccessControl.imPermissionRole
-_what_not_even_god_should_do= cAccessControl._what_not_even_god_should_do
+try:
+    import cAccessControl
+
+except ImportError:
+    # Fall back to Python implementation
+
+    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)
+
+else:
+    # C Optimizations:
+    rolesForPermissionOn=cAccessControl.rolesForPermissionOn
+    PermissionRole=cAccessControl.PermissionRole
+    imPermisionRole=cAccessControl.imPermissionRole
+    _what_not_even_god_should_do= cAccessControl._what_not_even_god_should_do
 
 ############################################################################## 
 # Test functions:


=== Zope/lib/python/AccessControl/User.py 1.158 => 1.158.18.1 ===
 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.6.28.1 ===
 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.12 => 1.12.30.1 ===
+##############################################################################
+# 
+# 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
 
-import cAccessControl
+$Id$'''
+__version__='$Revision$'[11:-2]
 
-ZopeSecurityPolicy = cAccessControl.ZopeSecurityPolicy
+
+try:
+    import cAccessControl
+except:
+    # Fall back on Python implementation
+
+    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):
+            roles=rolesForPermissionOn(permission, object)
+            if type(roles) is StringType:
+                roles=[roles]
+            return context.user.allowed(object, roles)
+
+else:
+    # C Optimization
+    from SimpleObjectPolicies import _noroles
+    ZopeSecurityPolicy = cAccessControl.ZopeSecurityPolicy
 
 


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


=== Zope/lib/python/AccessControl/cAccessControl.c 1.10 => 1.10.12.1 === (1121/1221 lines abridged)
 #include "Acquisition.h"
 
-#define OBJECT(o) ((PyObject *) (o))
 
-#ifdef win32
-#define PUBLIC
-#else
-#define PUBLIC
-#endif
+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))
 
 /*
 ** Structures 
@@ -84,23 +88,6 @@
 	PyObject *_v;
 } imPermissionRole;
 
-/* ZSPCACHE does NOT work securely; don't turn it on! */
-#define ZSPCACHE 0
-
-#if ZSPCACHE
-typedef struct { 
-	long accessed;
-	long container;
-	long name;
-	long value;
-	long context;
-	long roles;
-	PyObject *result;
-} ZSPCacheLine;
-
-#define ZSPCACHEENTRIES 512
-#endif
-
 /*
 ** Prototypes
 */
@@ -111,20 +98,19 @@
 static void ZopeSecurityPolicy_dealloc(ZopeSecurityPolicy *self);
 
 

[-=- -=- -=- 1121 lines omitted -=- -=- -=-]

 ** ----------------------------------------------------------------
 */
-
-PUBLIC void initcAccessControl(void) {
+void initcAccessControl(void) {
 	PyObject *module;
 	PyObject *dict;
 	char *rev = "$Revision$";
@@ -1787,9 +1514,15 @@
 
 	aq_init();
 
+	if (ZopeSecurityPolicy_setup() < 0) return;
+
 	ZopeSecurityPolicyType.tp_getattro =
 		(getattrofunc) PyExtensionClassCAPI->getattro;
 
+
+
+
+
 	module = Py_InitModule4("cAccessControl",
 		cAccessControl_methods,
 		"$Id: %\n",
@@ -1813,12 +1546,6 @@
 	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);
 
@@ -1832,12 +1559,5 @@
 		imPermissionRoleType);
 
 	imPermissionRoleObj = PyDict_GetItemString(dict, "imPermissionRole");
-
-#if ZSPCACHE
-	ZSPCacheInit();
-#endif
-
-	if (PyErr_Occurred())
-		Py_FatalError("Can't initialize module cAccessControl");
 }
 

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