[Zope-Checkins] SVN: Zope/branches/tseaver-collector_1774/lib/python/AccessControl/ My original patch, with tests.

Tres Seaver tseaver at palladion.com
Wed Nov 30 17:43:02 EST 2005


Log message for revision 40431:
  My original patch, with tests.

Changed:
  U   Zope/branches/tseaver-collector_1774/lib/python/AccessControl/ImplPython.py
  U   Zope/branches/tseaver-collector_1774/lib/python/AccessControl/cAccessControl.c
  U   Zope/branches/tseaver-collector_1774/lib/python/AccessControl/interfaces.py
  U   Zope/branches/tseaver-collector_1774/lib/python/AccessControl/tests/testZopeSecurityPolicy.py

-=-
Modified: Zope/branches/tseaver-collector_1774/lib/python/AccessControl/ImplPython.py
===================================================================
--- Zope/branches/tseaver-collector_1774/lib/python/AccessControl/ImplPython.py	2005-11-30 22:40:15 UTC (rev 40430)
+++ Zope/branches/tseaver-collector_1774/lib/python/AccessControl/ImplPython.py	2005-11-30 22:43:01 UTC (rev 40431)
@@ -34,6 +34,7 @@
 
 from AccessControl import SecurityManagement
 from AccessControl import Unauthorized
+from AccessControl.interfaces import ISecurityPolicy
 from AccessControl.interfaces import ISecurityManager
 from AccessControl.SimpleObjectPolicies import Containers, _noroles
 from AccessControl.ZopeGuards import guarded_getitem
@@ -199,6 +200,8 @@
 
 class ZopeSecurityPolicy:
 
+    implements(ISecurityPolicy)
+
     def __init__(self, ownerous=1, authenticated=1, verbose=0):
         """Create a Zope security policy.
 
@@ -459,12 +462,28 @@
         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)
+        result = context.user.allowed(object, roles)
 
+        # check executable owner and proxy roles
+        stack = context.stack
+        if stack:
+            eo = stack[-1]
+            if self._ownerous:
+                owner = eo.getOwner()
+                if (owner is not None) and not owner.allowed(object, roles):
+                    return 0
+            proxy_roles = getattr(eo, '_proxy_roles', None)
+            if proxy_roles:
+                if object is not aq_base(object):
+                    if not owner._check_context(object):
+                        return 0
+                for r in proxy_roles:
+                    if r in roles:
+                        return 1
+        return result
 
 # AccessControl.SecurityManager
 # -----------------------------

Modified: Zope/branches/tseaver-collector_1774/lib/python/AccessControl/cAccessControl.c
===================================================================
--- Zope/branches/tseaver-collector_1774/lib/python/AccessControl/cAccessControl.c	2005-11-30 22:40:15 UTC (rev 40430)
+++ Zope/branches/tseaver-collector_1774/lib/python/AccessControl/cAccessControl.c	2005-11-30 22:43:01 UTC (rev 40431)
@@ -1292,61 +1292,200 @@
 */
 
 static PyObject *ZopeSecurityPolicy_checkPermission(PyObject *self,
-	PyObject *args) {
+                                                    PyObject *args) {
 
-	PyObject *permission = NULL;
-	PyObject *object = NULL;
-	PyObject *context = NULL;
-	PyObject *roles;
-	PyObject *result = NULL;
-	PyObject *user;
+    /* return value */
+    PyObject *rval = NULL;
 
-	/*| def checkPermission(self, permission, object, context)
-	*/
+    /* arguments, not increfe'd */
+    PyObject *permission = NULL;
+    PyObject *object = NULL;
+    PyObject *context = NULL;
 
-	if (unpacktuple3(args, "checkPermission", 3, 
+    /* locals, XDECREF at exit */
+    PyObject *roles = NULL;
+    PyObject *user = NULL;
+    PyObject *stack = NULL;
+    PyObject *eo = NULL;
+    PyObject *owner = NULL;
+    PyObject *proxy_roles = NULL;
+    PyObject *method = NULL;
+    PyObject *wrappedowner = NULL;
+    PyObject *objectbase = NULL;
+    PyObject *incontext = NULL;
+
+    int contains = 0;
+    int iRole;
+    int length;
+
+    /*| def checkPermission(self, permission, object, context)
+    */
+
+    if (unpacktuple3(args, "checkPermission", 3, 
                          &permission, &object, &context) < 0)
-		return NULL;
+        return NULL;
 
-	/*| roles = rolesForPermissionOn(permission, object)
-	*/
+    /*| roles = rolesForPermissionOn(permission, object)
+    */
 
-	roles = c_rolesForPermissionOn(permission, object, NULL, NULL);
-	if (roles == NULL)
+    roles = c_rolesForPermissionOn(permission, object, NULL, NULL);
+    if (roles == NULL)
           return NULL;
 
-	/*| if type(roles) in (StringType, UnicodeType):
-	**|	roles = [roles]
-	*/
+    /*| if type(roles) in (StringType, UnicodeType):
+    **|     roles = [roles]
+    */
 
-	if ( PyString_Check(roles) || PyUnicode_Check(roles) ) {
-          PyObject *r;
+    if ( PyString_Check(roles) || PyUnicode_Check(roles) ) {
+          PyObject *role_list;
 
-          r = PyList_New(1);
-          if (r == NULL) {
-            Py_DECREF(roles);
-            return NULL;
-          }
+          role_list = PyList_New(1);
+          if (role_list == NULL) goto cP_done;
           /* Note: ref to roles is passed to the list object. */
-          PyList_SET_ITEM(r, 0, roles);
-          roles = r;
-	}
+          PyList_SET_ITEM(role_list, 0, roles);
+          roles = role_list;
+    }
 
-	/*| return context.user.allowed(object, roles)
-	*/
+    /*| result = context.user.allowed(object, roles)
+    */
 
-	user = PyObject_GetAttr(context, user_str);
-	if (user != NULL) {
-          ASSIGN(user, PyObject_GetAttr(user, allowed_str));
-          if (user != NULL) {
-            result = callfunction2(user, object, roles);
-            Py_DECREF(user);
-          }
-	}
+    user = PyObject_GetAttr(context, user_str);
+    if (user != NULL) {
+        ASSIGN(user, PyObject_GetAttr(user, allowed_str));
+        if (user != NULL) {
+            rval = callfunction2(user, object, roles);
+        }
+    }
 
-	Py_DECREF(roles);
+  
+    /*| # Check executable security
+    **| stack = context.stack
+    **| if stack:
+    */
+ 
+    stack = PyObject_GetAttr(context, stack_str);
+    if (stack == NULL) goto cP_done;
+ 
+    if (PyObject_IsTrue(stack)) {
+ 
+    /*|    eo = stack[-1]
+    **|    # If the executable had an owner, can it execute?
+    **|    owner = eo.getOwner()
+    **|    if (owner is not None) and not owner.allowed(object, roles)
+    **|       # We don't want someone to acquire if they can't 
+    **|       # get an unacquired!
+    **|       return 0
+    */
+ 
+        eo = PySequence_GetItem(stack, -1);
+        if (eo == NULL) goto cP_done;
+ 
+        if (ownerous) {
+            owner = PyObject_GetAttr(eo, getOwner_str);
+            if (owner) ASSIGN(owner, PyObject_CallObject(owner, NULL));
+            if (owner == NULL) goto cP_done;
+ 
+            if (owner != Py_None) {
+                ASSIGN(owner, PyObject_GetAttr(owner, allowed_str));
+                if (owner) ASSIGN(owner, callfunction2(owner, object, roles));
+                if (owner == NULL) goto cP_done;
+ 
+                if (! PyObject_IsTrue(owner)) {
+                   rval = 0;
+                   goto cP_done;
+                }
+            }
+        }
+ 
+    /*|    # 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.getWrappedOwner()
+    **|                        
+    **|        if owner is not None:
+    **|            if object is not aq_base(object):
+    **|                if not owner._check_context(object):
+    **|                    # object is higher up than the owner, 
+    **|                    # deny access
+    **|                    return 0
+    **|
+    **|        for r in proxy_roles:
+    **|            if r in roles:
+    **|                return 1
+    **|
+    **|    return result
+    */
+        proxy_roles = PyObject_GetAttr(eo, _proxy_roles_str);
+ 
+        if (proxy_roles == NULL) {
+            PyErr_Clear();
+            goto cP_done;
+        }
 
-	return result;
+        if (PyObject_IsTrue(proxy_roles)) {
+            method = PyObject_GetAttr(eo, getWrappedOwner_str);
+            if (method == NULL) goto cP_done;
+ 
+            wrappedowner = PyObject_CallObject(method, NULL);
+            if (wrappedowner == NULL) goto cP_done;
+ 
+            if (wrappedowner != Py_None) {
+                objectbase = aq_base(object);
+ 
+                if (object != objectbase) {
+ 
+                    incontext = callmethod1(wrappedowner,
+                                            _check_context_str, object);
+                    if (incontext == NULL) goto cP_done;
+                }
+ 
+                if ( ! PyObject_IsTrue(incontext)) goto cP_done;
+            }
+        }
+ 
+        if (PyTuple_Check(proxy_roles)) {
+            PyObject *proxy_role;
+            length = PyTuple_GET_SIZE(proxy_roles);
+            for (iRole=0; iRole < length; iRole++) {
+                proxy_role = PyTuple_GET_ITEM(proxy_roles, iRole);
+                /* proxy_role is not increfed */
+                if ((contains = PySequence_Contains(roles, proxy_role)))
+                    break;
+            }
+        } else {
+            PyObject *proxy_role;
+            length = PySequence_Size(proxy_roles);
+            if (length < 0) contains = -1;
+            for (iRole=0; contains == 0 && iRole < length; iRole++) {
+                proxy_role = PySequence_GetItem(proxy_roles, iRole);
+                if (proxy_role == NULL) goto cP_done;
+                /* proxy_role is increfed */
+                contains = PySequence_Contains(roles, proxy_role);
+                Py_DECREF(proxy_role);
+            }
+        }
+ 
+        if (contains > 0)
+            rval = PyInt_FromLong(contains);
+    } /* End of stack check */
+
+cP_done:
+    Py_XDECREF(roles);
+    Py_XDECREF(user);
+    Py_XDECREF(stack);
+    Py_XDECREF(eo);
+    Py_XDECREF(owner);
+    Py_XDECREF(proxy_roles);
+    Py_XDECREF(method);
+    Py_XDECREF(wrappedowner);
+    Py_XDECREF(objectbase);
+    Py_XDECREF(incontext);
+
+    return rval;
 }
 
 /*

Modified: Zope/branches/tseaver-collector_1774/lib/python/AccessControl/interfaces.py
===================================================================
--- Zope/branches/tseaver-collector_1774/lib/python/AccessControl/interfaces.py	2005-11-30 22:40:15 UTC (rev 40430)
+++ Zope/branches/tseaver-collector_1774/lib/python/AccessControl/interfaces.py	2005-11-30 22:43:01 UTC (rev 40431)
@@ -17,8 +17,37 @@
 from zope.interface import Interface
 from zope.interface import Attribute
 
+class ISecurityPolicy(Interface):
+    """Plug-in policy for checking access to objects within untrusted code.
+    """
+    def validate(accessed, container, name, value, context, roles=_noroles):
+        """Check that the current user (from context) has access.
+
+        o Raise Unauthorized if access is not allowed;  otherwise, return
+          a true value.
+
+        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.
+
+        context -- the security context (normally supplied by the security
+                   manager).
+
+        roles -- The roles of the object if already known.
+        """
+
+    def checkPermission(permission, object, context):
+        """Check whether the current user has a permission w.r.t. an object.
+        """
+
 class ISecurityManager(Interface):
-    """Checks access and manages executable context and policies.
+    """Check access and manages executable context and policies.
     """
     _policy = Attribute(u'Current Security Policy')
 

Modified: Zope/branches/tseaver-collector_1774/lib/python/AccessControl/tests/testZopeSecurityPolicy.py
===================================================================
--- Zope/branches/tseaver-collector_1774/lib/python/AccessControl/tests/testZopeSecurityPolicy.py	2005-11-30 22:40:15 UTC (rev 40430)
+++ Zope/branches/tseaver-collector_1774/lib/python/AccessControl/tests/testZopeSecurityPolicy.py	2005-11-30 22:43:01 UTC (rev 40431)
@@ -23,7 +23,6 @@
     from zExceptions import Unauthorized
 except ImportError:
     Unauthorized = 'Unauthorized'
-from AccessControl.ZopeSecurityPolicy import ZopeSecurityPolicy
 from AccessControl.User import UserFolder
 from AccessControl.SecurityManagement import SecurityContext
 from Acquisition import Implicit, Explicit, aq_base
@@ -152,10 +151,8 @@
     attr = 1
 
 
-class ZopeSecurityPolicyTests (unittest.TestCase):
+class ZopeSecurityPolicyTestBase(unittest.TestCase):
 
-    policy = ZopeSecurityPolicy()
-
     def setUp(self):
         a = App()
         self.a = a
@@ -174,7 +171,11 @@
         self.user = user
         context = SecurityContext(user)
         self.context = context
+        self.policy = self._makeOne()
 
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
     def assertPolicyAllows(self, ob, attrname):
         res = self.policy.validate(ob, ob, attrname, getattr(ob, attrname),
                                    self.context)
@@ -276,6 +277,17 @@
         v = self.policy.checkPermission('View', r_item, o_context)
         self.assert_(v, '_View_Permission should grant access to theowner')
 
+    def test_checkPermission_respects_proxy_roles(self):
+        r_item = self.a.r_item
+        context = self.context
+        self.failIf(self.policy.checkPermission('View', r_item, context))
+        o_context = SecurityContext(self.uf.getUserById('joe'))
+        # Push an executable with proxy roles on the stack
+        eo = OwnedSetuidMethod().__of__(r_item)
+        eo._proxy_roles = eo_roles
+        context.stack.append(eo)
+        self.failUnless(self.policy.checkPermission('View', r_item, context))
+
     def testUnicodeRolesForPermission(self):
         r_item = self.a.r_item
         context = self.context
@@ -351,6 +363,27 @@
                 self.fail('Policy accepted bad __roles__')
 
 
+class ISecurityPolicyConformance:
+
+    def test_conforms_to_ISecurityPolicy(self):
+        from AccessControl.interfaces import ISecurityPolicy
+        from zope.interface.verify import verifyClass
+        verifyClass(ISecurityPolicy, self._getTargetClass())
+
+class Python_ZSPTests(ZopeSecurityPolicyTestBase,
+                      ISecurityPolicyConformance,
+                     ):
+    def _getTargetClass(self):
+        from AccessControl.ImplPython import ZopeSecurityPolicy
+        return ZopeSecurityPolicy
+
+class C_ZSPTests(ZopeSecurityPolicyTestBase,
+               # ISecurityPolicyConformance, #XXX C version, how?
+                ):
+    def _getTargetClass(self):
+        from AccessControl.ImplC import ZopeSecurityPolicy
+        return ZopeSecurityPolicy
+
 def test_getRoles():
     """
 
@@ -445,6 +478,7 @@
 
 def test_zsp_gets_right_roles_for_methods():
     """
+    >>> from AccessControl.ZopeSecurityPolicy import ZopeSecurityPolicy
     >>> zsp = ZopeSecurityPolicy()
     >>> from ExtensionClass import Base
     >>> class C(Base):
@@ -499,7 +533,8 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(ZopeSecurityPolicyTests, 'test'))
+    suite.addTest(unittest.makeSuite(Python_ZSPTests, 'test'))
+    suite.addTest(unittest.makeSuite(C_ZSPTests, 'test'))
     suite.addTest(DocTestSuite())
     return suite
 



More information about the Zope-Checkins mailing list