[Zope-Checkins] SVN: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ Add interface and tests for AccessControl.SecurityManager.

Tres Seaver tseaver at palladion.com
Tue Nov 29 19:53:35 EST 2005


Log message for revision 40418:
  Add interface and tests for AccessControl.SecurityManager.
  
  o The new tests are amphibious:  they exercise both the Python and the C
    implementations, ensuring that they remain in sync.
  

Changed:
  U   Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ImplPython.py
  A   Zope/branches/Zope-2_8-branch/lib/python/AccessControl/interfaces.py
  A   Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testSecurityManager.py

-=-
Modified: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ImplPython.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ImplPython.py	2005-11-29 20:40:33 UTC (rev 40417)
+++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ImplPython.py	2005-11-30 00:53:34 UTC (rev 40418)
@@ -22,6 +22,7 @@
 from Acquisition import aq_acquire
 from ExtensionClass import Base
 from zLOG import LOG, BLATHER, PROBLEM
+from zope.interface import implements
 
 # 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
@@ -33,6 +34,7 @@
 
 from AccessControl import SecurityManagement
 from AccessControl import Unauthorized
+from AccessControl.interfaces import ISecurityManager
 from AccessControl.SimpleObjectPolicies import Containers, _noroles
 from AccessControl.ZopeGuards import guarded_getitem
 
@@ -491,7 +493,7 @@
     """A security manager provides methods for checking access and managing
     executable context and policies
     """
-
+    implements(ISecurityManager)
     __allow_access_to_unprotected_subobjects__ = {
         'validate': 1, 'checkPermission': 1,
         'getUser': 1, 'calledByExecutable': 1

Added: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/interfaces.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/interfaces.py	2005-11-29 20:40:33 UTC (rev 40417)
+++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/interfaces.py	2005-11-30 00:53:34 UTC (rev 40418)
@@ -0,0 +1,119 @@
+##############################################################################
+#
+# Copyright (c) 2005 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
+#
+##############################################################################
+"""Access control interfaces
+"""
+
+from AccessControl.SimpleObjectPolicies import _noroles
+from zope.interface import Interface
+from zope.interface import Attribute
+
+class ISecurityManager(Interface):
+    """Checks access and manages executable context and policies.
+    """
+    _policy = Attribute(u'Current Security Policy')
+
+    def validate(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.
+        """
+
+    def DTMLValidate(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.
+
+        """
+
+    def checkPermission(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
+        """
+
+    def addContext(anExecutableObject):
+        """Add an ExecutableObject to the current security context.
+        
+        o If it declares a custom security policy,  make that policy
+          "current";  otherwise, make the "default" security policy
+          current.
+        """
+
+    def removeContext(anExecutableObject):
+        """Remove an ExecutableObject from the current security context.
+        
+        o Remove all objects from the top of the stack "down" to the
+          supplied object.
+
+        o If the top object on the stack declares a custom security policy,
+          make that policy "current".
+
+        o If the stack is empty, or if the top declares no custom security
+          policy, restore the 'default" security policy as current.
+        """
+
+    def getUser():
+        """Get the currently authenticated user
+        """
+
+    def calledByExecutable():
+        """Return a boolean value indicating whether this context was called
+           in the context of an by an executable (i.e., one added via
+           'addContext').
+        """


Property changes on: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testSecurityManager.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testSecurityManager.py	2005-11-29 20:40:33 UTC (rev 40417)
+++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testSecurityManager.py	2005-11-30 00:53:34 UTC (rev 40418)
@@ -0,0 +1,273 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Tests for the SecurityManager implementations
+
+$Id$
+"""
+
+import unittest
+
+_THREAD_ID = 123
+
+class DummyContext:
+
+    def __init__(self):
+        self.user = object()
+        self.stack = []
+
+class DummyPolicy:
+
+    CHECK_PERMISSION_ARGS = None
+    CHECK_PERMISSION_RESULT = object()
+
+    VALIDATE_ARGS = None
+
+    def checkPermission(self, *args):
+        self.CHECK_PERMISSION_ARGS = args
+        return self.CHECK_PERMISSION_RESULT
+
+    def validate(self, *args):
+        self.VALIDATE_ARGS = args
+        return True
+
+class ExecutableObject:
+    def __init__(self, new_policy):
+        self._new_policy = new_policy
+
+    def _customSecurityPolicy(self):
+        return self._new_policy
+
+class ISecurityManagerConformance:
+
+    def test_conforms_to_ISecurityManager(self):
+        from AccessControl.interfaces import ISecurityManager
+        from zope.interface.verify import verifyClass
+        verifyClass(ISecurityManager, self._getTargetClass())
+
+class SecurityManagerTestBase(unittest.TestCase):
+
+    def _makeOne(self, thread_id, context):
+        return self._getTargetClass()(thread_id, context)
+
+    def test_getUser(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        self.failUnless(mgr.getUser() is context.user)
+
+    def test_calledByExecutable_no_stack(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        self.failIf(mgr.calledByExecutable())
+
+    def test_calledByExecutable_with_stack(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        executableObject = object()
+        mgr.addContext(executableObject)
+        self.failUnless(mgr.calledByExecutable())
+
+    def test_addContext_no_custom_policy(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        original_policy = mgr._policy
+        executableObject = object()
+        mgr.addContext(executableObject)
+        self.failUnless(mgr._policy is original_policy)
+
+    def test_addContext_with_custom_policy(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = DummyPolicy()
+        executableObject = ExecutableObject(new_policy)
+        mgr.addContext(executableObject)
+        self.failUnless(mgr._policy is new_policy)
+
+    def test_addContext_with_custom_policy_then_none(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        original_policy = mgr._policy
+        new_policy = DummyPolicy()
+        executableObject = ExecutableObject(new_policy)
+        mgr.addContext(executableObject)
+        mgr.addContext(object())
+        self.failUnless(mgr._policy is original_policy)
+
+    def test_removeContext_pops_items_above_EO(self):
+        context = DummyContext()
+        ALPHA, BETA, GAMMA, DELTA = object(), object(), object(), object()
+        context.stack.append(ALPHA)
+        context.stack.append(BETA)
+        context.stack.append(GAMMA)
+        context.stack.append(DELTA)
+        mgr = self._makeOne(_THREAD_ID, context)
+
+        mgr.removeContext(GAMMA)
+
+        self.assertEqual(len(context.stack), 2)
+        self.failUnless(context.stack[0] is ALPHA)
+        self.failUnless(context.stack[1] is BETA)
+
+    def test_removeContext_last_EO_restores_default_policy(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        original_policy = mgr._policy
+        new_policy = mgr._policy = DummyPolicy()
+        top = object()
+        context.stack.append(top)
+        mgr.removeContext(top)
+        self.failUnless(mgr._policy is original_policy)
+
+    def test_removeContext_with_top_having_custom_policy(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = DummyPolicy()
+        context.stack.append(ExecutableObject(new_policy))
+        top = object()
+        context.stack.append(top)
+        mgr.removeContext(top)
+        self.failUnless(mgr._policy is new_policy)
+
+    def test_removeContext_with_top_having_no_custom_policy(self):
+        context = DummyContext()
+        mgr = self._makeOne(_THREAD_ID, context)
+        original_policy = mgr._policy
+        new_policy = DummyPolicy()
+        executableObject = ExecutableObject(new_policy)
+        context.stack.append(executableObject)
+        top = object()
+        context.stack.append(top)
+        mgr.removeContext(executableObject)
+        self.failUnless(mgr._policy is original_policy)
+
+    def test_checkPermission_delegates_to_policy(self):
+        context = DummyContext()
+        PERMISSION = 'PERMISSION'
+        TARGET = object()
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = mgr._policy = DummyPolicy()
+        result = mgr.checkPermission(PERMISSION, TARGET)
+        self.failUnless(result is DummyPolicy.CHECK_PERMISSION_RESULT)
+        self.failUnless(new_policy.CHECK_PERMISSION_ARGS[0] is PERMISSION)
+        self.failUnless(new_policy.CHECK_PERMISSION_ARGS[1] is TARGET)
+        self.failUnless(new_policy.CHECK_PERMISSION_ARGS[2] is context)
+
+    def test_validate_without_roles_delegates_to_policy(self):
+        from AccessControl.SimpleObjectPolicies import _noroles
+
+        context = DummyContext()
+        ACCESSED = object()
+        CONTAINER = object()
+        NAME = 'NAME'
+        VALUE = object()
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = mgr._policy = DummyPolicy()
+
+        result = mgr.validate(ACCESSED,
+                              CONTAINER,
+                              NAME,
+                              VALUE,
+                             )
+
+        self.failUnless(result)
+        self.assertEqual(len(new_policy.VALIDATE_ARGS), 5)
+        self.failUnless(new_policy.VALIDATE_ARGS[0] is ACCESSED)
+        self.failUnless(new_policy.VALIDATE_ARGS[1] is CONTAINER)
+        self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME)
+        self.failUnless(new_policy.VALIDATE_ARGS[3] is VALUE)
+        self.failUnless(new_policy.VALIDATE_ARGS[4] is context)
+
+    def test_validate_with_roles_delegates_to_policy(self):
+        from AccessControl.SimpleObjectPolicies import _noroles
+
+        context = DummyContext()
+        ACCESSED = object()
+        CONTAINER = object()
+        NAME = 'NAME'
+        VALUE = object()
+        ROLES = ('Hamlet', 'Othello')
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = mgr._policy = DummyPolicy()
+
+        result = mgr.validate(ACCESSED,
+                              CONTAINER,
+                              NAME,
+                              VALUE,
+                              ROLES,
+                             )
+
+        self.failUnless(result)
+        self.assertEqual(len(new_policy.VALIDATE_ARGS), 6)
+        self.failUnless(new_policy.VALIDATE_ARGS[0] is ACCESSED)
+        self.failUnless(new_policy.VALIDATE_ARGS[1] is CONTAINER)
+        self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME)
+        self.failUnless(new_policy.VALIDATE_ARGS[3] is VALUE)
+        self.failUnless(new_policy.VALIDATE_ARGS[4] is context)
+        self.assertEqual(new_policy.VALIDATE_ARGS[5], ROLES)
+
+    def test_DTMLValidate_delegates_to_policy_validate(self):
+        from AccessControl.SimpleObjectPolicies import _noroles
+
+        context = DummyContext()
+        ACCESSED = object()
+        CONTAINER = object()
+        NAME = 'NAME'
+        VALUE = object()
+        MD = {}
+        mgr = self._makeOne(_THREAD_ID, context)
+        new_policy = mgr._policy = DummyPolicy()
+
+        result = mgr.DTMLValidate(ACCESSED,
+                                  CONTAINER,
+                                  NAME,
+                                  VALUE,
+                                  MD,
+                                 )
+
+        self.failUnless(result)
+        self.assertEqual(len(new_policy.VALIDATE_ARGS), 5)
+        self.failUnless(new_policy.VALIDATE_ARGS[0] is ACCESSED)
+        self.failUnless(new_policy.VALIDATE_ARGS[1] is CONTAINER)
+        self.assertEqual(new_policy.VALIDATE_ARGS[2], NAME)
+        self.failUnless(new_policy.VALIDATE_ARGS[3] is VALUE)
+        self.failUnless(new_policy.VALIDATE_ARGS[4] is context)
+
+class PythonSecurityManagerTests(SecurityManagerTestBase,
+                                 ISecurityManagerConformance,
+                                ):
+
+    def _getTargetClass(self):
+        from AccessControl.ImplPython import SecurityManager
+        return SecurityManager
+
+
+# N.B.:  The C version mixes in the Python version, which is why we
+#        can test for conformance to ISecurityManager.
+class C_SecurityManagerTests(SecurityManagerTestBase,
+                             ISecurityManagerConformance,
+                            ):
+
+    def _getTargetClass(self):
+        from AccessControl.ImplC import SecurityManager
+        return SecurityManager
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest( unittest.makeSuite( PythonSecurityManagerTests ) )
+    suite.addTest( unittest.makeSuite( C_SecurityManagerTests ) )
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testSecurityManager.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Zope-Checkins mailing list