[Zope3-checkins] CVS: Zope3/src/zope/security - checker.py:1.22.2.1

Marius Gedminas mgedmin@codeworks.lt
Wed, 14 May 2003 06:39:36 -0400


Update of /cvs-repository/Zope3/src/zope/security
In directory cvs.zope.org:/tmp/cvs-serv2024/src/zope/security

Modified Files:
      Tag: stevea-decorators-branch
	checker.py 
Log Message:
Refactored WATCH_CHECKERS from base Checker code into a separate mixin class.
New class: DecoratedChecker.
Some work on the global decorator service.



=== Zope3/src/zope/security/checker.py 1.22 => 1.22.2.1 ===
--- Zope3/src/zope/security/checker.py:1.22	Sat May  3 12:38:17 2003
+++ Zope3/src/zope/security/checker.py	Wed May 14 06:39:05 2003
@@ -20,7 +20,7 @@
 import types
 import datetime
 
-from zope.interface import directlyProvides, Interface
+from zope.interface import directlyProvides, implements, Interface
 from zope.interface.interfaces import IInterface, IInterfaceSpecification
 from zope.interface.declarations import ObjectSpecification
 from zope.interface.declarations import ProvidesSpecification
@@ -34,6 +34,8 @@
 from zope.exceptions \
      import Unauthorized, ForbiddenAttribute, DuplicationError
 
+__metaclass__ = type
+
 if os.environ.get('ZOPE_WATCH_CHECKERS'):
     WATCH_CHECKERS = True
 else:
@@ -117,64 +119,36 @@
     def check_setattr(self, object, name):
         'See IChecker'
 
-        if WATCH_CHECKERS:
-            print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
-
-        # We have the information we need already
         permission = self._setattr_permission_func(name)
-        if permission:
+        if permission is not None:
             if permission is CheckerPublic:
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Public.'
                 return # Public
             manager = getSecurityManager()
             if manager.checkPermission(permission, object):
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Granted.'
                 return
             else:
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Unauthorized.'
                 __traceback_supplement__ = (TracebackSupplement, object)
                 raise Unauthorized(name=name)
 
-        if WATCH_CHECKERS:
-            print >> sys.stderr, 'Forbidden.'
-
         __traceback_supplement__ = (TracebackSupplement, object)
         raise ForbiddenAttribute(name)
 
     def check(self, object, name):
         'See IChecker'
 
-        if WATCH_CHECKERS:
-            print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
-
-        # We have the information we need already
         permission = self._permission_func(name)
-        if permission:
+        if permission is not None:
             if permission is CheckerPublic:
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Public.'
                 return # Public
             manager = getSecurityManager()
             if manager.checkPermission(permission, object):
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Granted.'
                 return
             else:
-                if WATCH_CHECKERS:
-                    print >> sys.stderr, 'Unauthorized.'
                 __traceback_supplement__ = (TracebackSupplement, object)
                 raise Unauthorized(name=name)
         elif name in _always_available:
-            if WATCH_CHECKERS:
-                print >> sys.stderr, 'Always available.'
             return
 
-        if WATCH_CHECKERS:
-            print >> sys.stderr, 'Forbidden.'
-
         __traceback_supplement__ = (TracebackSupplement, object)
         raise ForbiddenAttribute(name)
 
@@ -190,6 +164,164 @@
 
         return Proxy(value, checker)
 
+class CheckerLoggingMixin:
+    """Debugging mixin for Checker.
+
+    Prints verbose debugging information about every performed check to
+    sys.stderr.
+
+    This class relies on the class it's mixed into having permission_id
+    and setattr_permission_id methods.
+    """
+
+    def check(self, object, name):
+        print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
+        try:
+            super(CheckerLoggingMixin, self).check(object, name)
+            if name in _always_available:
+                print >> sys.stderr, 'Always available.'
+            elif self.permission_id(name) is CheckerPublic:
+                print >> sys.stderr, 'Public.'
+            else:
+                print >> sys.stderr, 'Granted.'
+        except Unauthorized:
+            print >> sys.stderr, 'Unauthorized.'
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, 'Forbidden.'
+            raise
+
+    def check_getattr(self, object, name):
+        print >> sys.stderr, ('Checking get %r.%s:' % (object, name)),
+        try:
+            super(CheckerLoggingMixin, self).check(object, name)
+            if name in _always_available:
+                print >> sys.stderr, 'Always available.'
+            elif self.permission_id(name) is CheckerPublic:
+                print >> sys.stderr, 'Public.'
+            else:
+                print >> sys.stderr, 'Granted.'
+        except Unauthorized:
+            print >> sys.stderr, 'Unauthorized.'
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, 'Forbidden.'
+            raise
+
+    def check_setattr(self, object, name):
+        print >> sys.stderr, ('Checking set %r.%s:' % (object, name)),
+        try:
+            super(CheckerLoggingMixin, self).check_setattr(object, name)
+            if self.setattr_permission_id(name) is CheckerPublic:
+                print >> sys.stderr, 'Public.'
+            else:
+                print >> sys.stderr, 'Granted.'
+        except Unauthorized:
+            print >> sys.stderr, 'Unauthorized.'
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, 'Forbidden.'
+            raise
+
+
+class DecoratedChecker:
+    """A checker using further permissions relative to an original checker.
+    """
+
+    implements(IChecker)
+
+    def __init__(self, original_checker, permission_func,
+                 setattr_permission_func=lambda name: None
+                 ):
+        """Create a checker
+
+        A dictionary or a callable must be provided for computing permissions
+        for names. The callable 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 decided by
+        original_checker. If CheckerPublic is returned, then access will be
+        granted without checking a permission.
+
+        An optional setattr permission function or dictionary may be
+        provided for checking set attribute access.
+        """
+        self._original_checker = original_checker
+
+        if type(permission_func) is dict:
+            permission_func = permission_func.get
+        self._permission_func = permission_func
+
+        if type(setattr_permission_func) is dict:
+            setattr_permission_func = setattr_permission_func.get
+        self._setattr_permission_func = setattr_permission_func
+
+    def permission_id(self, name):
+        permission = self._permission_func(name)
+        if permission is None:
+            permission = self._original_checker.permission_id(name)
+        return permission
+
+    def setattr_permission_id(self, name):
+        permission = self._setattr_permission_func(name)
+        if permission is None:
+            permission = self._original_checker.setattr_permission_id(name)
+        return permission
+
+    def check(self, object, name):
+        permission = self._permission_func(name)
+        if permission is not None:
+            if permission is CheckerPublic:
+                return # Public
+            manager = getSecurityManager()
+            if manager.checkPermission(permission, object):
+                return
+            else:
+                __traceback_supplement__ = (TracebackSupplement, object)
+                raise Unauthorized(name=name)
+        else:
+            # let the original checker decide
+            self._original_checker.check(object, name)
+            return
+
+    def check_getattr(self, object, name):
+        permission = self._permission_func(name)
+        if permission is not None:
+            if permission is CheckerPublic:
+                return # Public
+            manager = getSecurityManager()
+            if manager.checkPermission(permission, object):
+                return
+            else:
+                __traceback_supplement__ = (TracebackSupplement, object)
+                raise Unauthorized(name=name)
+        else:
+            # let the original checker decide
+            self._original_checker.check_getattr(object, name)
+            return
+
+    def check_setattr(self, object, name):
+        permission = self._setattr_permission_func(name)
+        if permission is not None:
+            if permission is CheckerPublic:
+                return # Public
+            manager = getSecurityManager()
+            if manager.checkPermission(permission, object):
+                return
+            else:
+                __traceback_supplement__ = (TracebackSupplement, object)
+                raise Unauthorized(name=name)
+        else:
+            # let the original checker decide
+            self._original_checker.check_setattr(object, name)
+            return
+
+
+if WATCH_CHECKERS:
+    class Checker(CheckerLoggingMixin, Checker):
+        pass
+    class DecoratedChecker(CheckerLoggingMixin, DecoratedChecker):
+        pass
+
 # Helper class for __traceback_supplement__
 class TracebackSupplement:
 
@@ -398,6 +530,7 @@
 
 
 
+# It should really be called _available_by_default
 _always_available = ['__lt__', '__le__', '__eq__',
                      '__gt__', '__ge__', '__ne__',
                      '__hash__', '__nonzero__',