[Zope3-checkins] SVN: Zope3/trunk/src/zope/security/ Add two convenience functions: canWrite and canAccess. While the tests for this package are not doc tests, I included doctest-like comments in the tests. Since these functions are primarily conveniences for app code, I imported them in the package __init__.

Gary Poster gary at zope.com
Mon Jan 24 22:23:41 EST 2005


Log message for revision 28952:
  Add two convenience functions: canWrite and canAccess.  While the tests for this package are not doc tests, I included doctest-like comments in the tests.  Since these functions are primarily conveniences for app code, I imported them in the package __init__.
  

Changed:
  U   Zope3/trunk/src/zope/security/__init__.py
  U   Zope3/trunk/src/zope/security/checker.py
  U   Zope3/trunk/src/zope/security/tests/test_checker.py

-=-
Modified: Zope3/trunk/src/zope/security/__init__.py
===================================================================
--- Zope3/trunk/src/zope/security/__init__.py	2005-01-25 03:19:31 UTC (rev 28951)
+++ Zope3/trunk/src/zope/security/__init__.py	2005-01-25 03:23:41 UTC (rev 28952)
@@ -22,3 +22,4 @@
 import zope.proxy
 
 from zope.security.management import checkPermission
+from zope.security.checker import canWrite, canAccess

Modified: Zope3/trunk/src/zope/security/checker.py
===================================================================
--- Zope3/trunk/src/zope/security/checker.py	2005-01-25 03:19:31 UTC (rev 28951)
+++ Zope3/trunk/src/zope/security/checker.py	2005-01-25 03:23:41 UTC (rev 28952)
@@ -84,7 +84,40 @@
 
 directlyProvides(ProxyFactory, ISecurityProxyFactory)
 
+def canWrite(obj, name):
+    """Check whether the interaction may write an attribute named name on obj.
+    
+    Convenience method.  Rather than using checkPermission in high level code,
+    use canWrite and canAccess to avoid binding code to permissions.
+    """
+    obj = ProxyFactory(obj)
+    checker = getChecker(obj)
+    try:
+        checker.check_setattr(obj, name)
+    except Unauthorized:
+        return False
+    # if it is Forbidden (or anything else), let it be raised: it probably 
+    # indicates a programming or configuration error
+    return True
 
+def canAccess(obj, name):
+    """Check whether the interaction may access an attribute named name on obj.
+    
+    Convenience method.  Rather than using checkPermission in high level code,
+    use canWrite and canAccess to avoid binding code to permissions.
+    """
+    # access attributes and methods, including, in the current checker 
+    # implementation, special names like __getitem__
+    obj = ProxyFactory(obj)
+    checker = getChecker(obj)
+    try:
+        checker.check_getattr(obj, name)
+    except Unauthorized:
+        return False
+    # if it is Forbidden (or anything else), let it be raised: it probably 
+    # indicates a programming or configuration error
+    return True
+
 class Checker(object):
     implements(INameBasedChecker)
 
@@ -92,7 +125,7 @@
         """Create a checker
 
         A dictionary must be provided for computing permissions for
-        names. The disctionary get will be called with attribute names
+        names. The dictionary get will be called with attribute names
         and must return a permission id, None, or the special marker,
         CheckerPublic. If None is returned, then access to the name is
         forbidden. If CheckerPublic is returned, then access will be

Modified: Zope3/trunk/src/zope/security/tests/test_checker.py
===================================================================
--- Zope3/trunk/src/zope/security/tests/test_checker.py	2005-01-25 03:19:31 UTC (rev 28951)
+++ Zope3/trunk/src/zope/security/tests/test_checker.py	2005-01-25 03:23:41 UTC (rev 28952)
@@ -28,6 +28,7 @@
 from zope.security.proxy import removeSecurityProxy
 from zope.security.proxy import getChecker
 from zope.security.checker import defineChecker, ProxyFactory
+from zope.security.checker import canWrite, canAccess
 from zope.security.proxy import Proxy
 import types, pickle
 
@@ -365,8 +366,67 @@
         proxy1 = ProxyFactory(obj, checker)
         proxy2 = ProxyFactory(proxy1, checker)
         self.assert_(proxy1 is proxy2)
+    
+    def test_canWrite_canAccess(self):
+        # the canWrite and canAccess functions are conveniences.  Often code
+        # wants to check if a certain option is open to a user before 
+        # presenting it.  If the code relies on a certain permission, the
+        # Zope 3 goal of keeping knowledge of security assertions out of the
+        # code and only in the zcml assertions is broken.  Instead, ask if the
+        # current user canAccess or canWrite some pertinent aspect of the 
+        # object.  canAccess is used for both read access on an attribute
+        # and call access to methods.
+        
+        # For example, consider this humble pair of class and object.
+        class SomeClass(object):
+            pass
+        obj = SomeClass()
+        
+        # We will establish a checker for the class.  This is the standard
+        # name-based checker, and works by specifying two dicts, one for read
+        # and one for write.  Each item in the dictionary should be an
+        # attribute name and the permission required to read or write it.  
+        
+        # For these tests, the SecurityPolicy defined at the top of this file 
+        # is in place.  It is a stub.  Normally, the security policy would
+        # have knowledge of interactions and participants, and would determine
+        # on the basis of the particpants and the object if a certain permission
+        # were authorized.  This stub simply says that the 'test_allowed' 
+        # permission is authorized and nothing else is, for any object you pass
+        # it.
+        
+        # Therefore, according to the checker created here, the current 
+        # 'interaction' (as stubbed out in the security policy) will be allowed
+        # to access and write foo, and access bar.  The interaction is 
+        # unauthorized for accessing baz and writing bar.  Any other access or 
+        # write is not merely unauthorized but forbidden--including write access
+        # for baz.
+        checker = Checker(
+            {'foo':'test_allowed',
+             'bar':'test_allowed',
+             'baz':'you_will_not_have_this_permission'},
+            {'foo':'test_allowed',
+             'bar':'you_will_not_have_this_permission'})
+        defineChecker(SomeClass, checker)
+        
+        # so, our hapless interaction may write and access foo...
+        self.assert_(canWrite(obj, 'foo'))
+        self.assert_(canAccess(obj, 'foo'))
+        
+        # ...may access, but not write, bar...
+        self.assert_(not canWrite(obj, 'bar'))
+        self.assert_(canAccess(obj, 'bar'))
+        
+        # ...and may access baz.
+        self.assert_(not canAccess(obj, 'baz'))
+        
+        # there are no security assertions for writing baz or accessing 
+        # anything else, so these actually raise Forbidden.  The rationale
+        # behind exposing the Forbidden exception is primarily that it is
+        # usually indicative of programming or configuration errors.
+        self.assertRaises(Forbidden, canWrite, obj, 'baz')
+        self.assertRaises(Forbidden, canAccess, obj, 'shazam')
 
-
 class TestCheckerPublic(TestCase):
 
     def test_that_pickling_CheckerPublic_retains_identity(self):



More information about the Zope3-Checkins mailing list