[Zope3-checkins] SVN: Zope3/trunk/src/zope/ Implemented Checker in C for performance

Jim Fulton jim at zope.com
Tue Jun 22 08:15:41 EDT 2004


Log message for revision 25933:
Implemented Checker in C for performance



-=-
Modified: Zope3/trunk/src/zope/app/tests/test_decorator.py
===================================================================
--- Zope3/trunk/src/zope/app/tests/test_decorator.py	2004-06-21 21:26:15 UTC (rev 25932)
+++ Zope3/trunk/src/zope/app/tests/test_decorator.py	2004-06-22 12:13:32 UTC (rev 25933)
@@ -108,8 +108,8 @@
     >>> print selectChecker(w)
     None
     >>> c = w.__Security_checker__
-    >>> print type(c)
-    <class 'zope.security.checker.Checker'>
+    >>> c.__class__.__module__, c.__class__.__name__
+    ('zope.security.checker', 'Checker')
     >>> c.check_getattr(w, 'a')
 
     >>> check_forbidden_call(c.check_getattr, w, 'b')
@@ -135,8 +135,9 @@
     >>> int(w.__Security_checker__ is None)
     1
     >>> w = MyDecorator2(None)
-    >>> type(w.__Security_checker__)
-    <class 'zope.security.checker.Checker'>
+    >>> c = w.__Security_checker__
+    >>> c.__class__.__module__, c.__class__.__name__
+    ('zope.security.checker', 'Checker')
     """
 
 

Modified: Zope3/trunk/src/zope/security/_zope_security_checker.c
===================================================================
--- Zope3/trunk/src/zope/security/_zope_security_checker.c	2004-06-21 21:26:15 UTC (rev 25932)
+++ Zope3/trunk/src/zope/security/_zope_security_checker.c	2004-06-22 12:13:32 UTC (rev 25933)
@@ -1,10 +1,397 @@
 #include <Python.h>
 
-static PyObject *_checkers, *NoProxy;
-static PyObject *Proxy=NULL, *_defaultChecker, *Checker=NULL;
+static PyObject *_checkers, *_defaultChecker, *_always_available, *NoProxy;
+static PyObject *Proxy, *getSecurityPolicy, *queryInteraction, *CheckerPublic;
+static PyObject *ForbiddenAttribute, *Unauthorized;
 
+#define DECLARE_STRING(N) static PyObject *str_##N
 
+DECLARE_STRING(checkPermission);
+DECLARE_STRING(__Security_checker__);
 
+#define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); }
+
+typedef struct {
+	PyObject_HEAD
+        PyObject *getperms, *setperms;
+} Checker;
+
+/*     def permission_id(self, name): */
+static PyObject *
+Checker_permission_id(Checker *self, PyObject *name)
+{
+/*         return self._permission_func(name) */
+  PyObject *result;
+
+  if (self->getperms)
+    {
+      result = PyDict_GetItem(self->getperms, name);
+      if (result == NULL)
+        result = Py_None;
+    }
+  else
+    result = Py_None;
+
+  Py_INCREF(result);
+  return result;
+}
+
+/*     def setattr_permission_id(self, name): */
+static PyObject *
+Checker_setattr_permission_id(Checker *self, PyObject *name)
+{
+/*         return self._setattr_permission_func(name) */
+  PyObject *result;
+
+  if (self->setperms)
+    {
+      result = PyDict_GetItem(self->setperms, name);
+      if (result == NULL)
+        result = Py_None;
+    }
+  else
+    result = Py_None;
+
+  Py_INCREF(result);
+  return result;
+}
+
+static int
+checkPermission(PyObject *permission, PyObject *object, PyObject *name)
+{
+      PyObject *policy, *interaction, *r;
+      int i;
+
+/*             policy = getSecurityPolicy() */
+      policy = PyObject_CallObject(getSecurityPolicy, NULL);
+      if (policy == NULL)
+        return -1;
+/*             interaction = queryInteraction() */
+      interaction = PyObject_CallObject(queryInteraction, NULL);
+      if (interaction == NULL)
+        {
+          Py_DECREF(policy);
+          return -1;
+        }
+/*             if policy.checkPermission(permission, object, interaction): */
+/*                 return */
+      r = PyObject_CallMethodObjArgs(policy, str_checkPermission,
+                                     permission, object, interaction, NULL);
+      Py_DECREF(policy);
+      Py_DECREF(interaction);
+      i = PyObject_IsTrue(r);
+      Py_DECREF(r);
+      if (i < 0)
+        return -1;
+      if (i) 
+        return 0;
+/*             else: */
+/*                 __traceback_supplement__ = (TracebackSupplement, object) */
+/*                 raise Unauthorized, name */
+      PyErr_SetObject(Unauthorized, name);
+      return -1;
+}
+
+
+/*     def check(self, object, name): */
+static PyObject *
+Checker_check(Checker *self, PyObject *args)
+{
+  PyObject *object, *name, *permission=NULL;
+  int operator;
+
+  if (!PyArg_ParseTuple(args, "OO", &object, &name))
+    return NULL;
+
+/*         permission = self._permission_func(name) */
+  if (self->getperms)
+    permission = PyDict_GetItem(self->getperms, name);
+
+/*         if permission is not None: */
+  if (permission != NULL)
+    {
+/*             if permission is CheckerPublic: */
+/*                 return # Public */
+      if (permission == CheckerPublic)
+        goto ok;
+
+      if (checkPermission(permission, object, name) < 0)
+        return NULL;
+      goto ok;
+    }
+
+
+  operator = (PyString_Check(name) 
+              && PyString_AS_STRING(name)[0] == '_'
+              && PyString_AS_STRING(name)[1] == '_');
+
+  if (operator)
+    {
+/*         elif name in _always_available: */
+/*             return */
+      int ic = PySequence_Contains(_always_available, name);
+      if (ic < 0)
+        return NULL;
+      if (ic)
+        goto ok;
+
+/*         if name != '__iter__' or hasattr(object, name): */
+/*             __traceback_supplement__ = (TracebackSupplement, object) */
+/*             raise ForbiddenAttribute, (name, object) */
+      
+      if (strcmp("__iter__", PyString_AS_STRING(name)) == 0
+          && ! PyObject_HasAttr(object, name))
+        /* We want an attr error if we're asked for __iter__ and we don't 
+           have it. We'll get one by allowing the access. */
+        goto ok;
+    }
+  
+  args = Py_BuildValue("OO", name, object);
+  if (args != NULL)
+    {
+      PyErr_SetObject(ForbiddenAttribute, args);
+      Py_DECREF(args);
+    }
+  return NULL;
+
+ ok:
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+
+/*     def check_setattr(self, object, name): */
+static PyObject *
+Checker_check_setattr(Checker *self, PyObject *args)
+{
+  PyObject *object, *name, *permission=NULL;
+
+  if (!PyArg_ParseTuple(args, "OO", &object, &name))
+    return NULL;
+
+/*         permission = self._permission_func(name) */
+  if (self->setperms)
+    permission = PyDict_GetItem(self->setperms, name);
+
+/*         if permission is not None: */
+  if (permission != NULL)
+    {
+/*             if permission is CheckerPublic: */
+/*                 return # Public */
+      if (permission != CheckerPublic
+          && checkPermission(permission, object, name) < 0)
+        return NULL;
+
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+  
+/*         __traceback_supplement__ = (TracebackSupplement, object) */
+/*         raise ForbiddenAttribute, (name, object) */
+  args = Py_BuildValue("OO", name, object);
+  if (args != NULL)
+    {
+      PyErr_SetObject(ForbiddenAttribute, args);
+      Py_DECREF(args);
+    }
+  return NULL;
+}
+
+
+static PyObject *
+selectChecker(PyObject *ignored, PyObject *object);
+
+/*     def proxy(self, value): */
+static PyObject *
+Checker_proxy(Checker *self, PyObject *value)
+{
+  PyObject *checker, *r;
+/*         checker = getattr(value, '__Security_checker__', None) */
+  checker = PyObject_GetAttr(value, str___Security_checker__);
+/*         if checker is None: */
+  if (checker == NULL)
+    {
+      PyErr_Clear();
+
+/*             checker = selectChecker(value) */
+      checker = selectChecker(NULL, value);
+      if (checker == NULL)
+        return NULL;
+
+/*             if checker is None: */
+/*                 return value */
+      if (checker == Py_None)
+        {
+          Py_DECREF(checker);
+          Py_INCREF(value);
+          return value;
+        }
+    }
+    
+  r = PyObject_CallFunctionObjArgs(Proxy, value, checker, NULL);
+  Py_DECREF(checker);
+  return r;
+}
+
+/*         return Proxy(value, checker) */
+
+
+static struct PyMethodDef Checker_methods[] = {
+  {"permission_id", (PyCFunction)Checker_permission_id, METH_O,
+   "permission_id(name) -- Return the permission neded to get the name"},
+  {"setattr_permission_id", (PyCFunction)Checker_setattr_permission_id, 
+   METH_O,
+   "setattr_permission_id(name) -- Return the permission neded to set the name"
+  },
+  {"check_getattr", (PyCFunction)Checker_check, METH_VARARGS,
+   "check_getattr(object, name) -- Check whether a getattr is allowes"},
+  {"check_setattr", (PyCFunction)Checker_check_setattr, METH_VARARGS,
+   "check_setattr(object, name) -- Check whether a setattr is allowes"},
+  {"check", (PyCFunction)Checker_check, METH_VARARGS,
+   "check(object, opname) -- Check whether an operation is allowes"},
+  {"proxy", (PyCFunction)Checker_proxy, METH_O,
+   "proxy(object) -- Security-proxy an object"},
+
+  {NULL,  NULL} 	/* sentinel */
+};
+
+static int
+Checker_clear(Checker *self)
+{
+  CLEAR(self->getperms);
+  CLEAR(self->setperms);
+  return 0;
+}
+
+static void
+Checker_dealloc(Checker *self)
+{
+  Checker_clear(self);
+  self->ob_type->tp_free((PyObject*)self);
+}
+
+static int
+Checker_traverse(Checker *self, visitproc visit, void *arg)
+{
+  if (self->getperms != NULL && visit(self->getperms, arg) < 0)
+    return -1;
+  if (self->setperms != NULL && visit(self->setperms, arg) < 0)
+    return -1;
+	
+  return 0;
+}
+
+static int
+Checker_init(Checker *self, PyObject *args, PyObject *kwds)
+{
+  PyObject *getperms, *setperms=NULL;
+  static char *kwlist[] = {"get_permissions", "set_permissions", NULL};
+
+  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!:Checker", kwlist, 
+                                    &PyDict_Type, &getperms, 
+                                    &PyDict_Type, &setperms))
+    return -1; 
+
+  Py_INCREF(getperms);
+  self->getperms = getperms;
+  Py_XINCREF(setperms);
+  self->setperms = setperms;
+
+  return 0;
+}
+
+static PyObject *
+Checker_get_get_permissions(Checker *self, void *closure)
+{
+  if (self->getperms == NULL)
+    {
+      self->getperms = PyDict_New();
+      if (self->getperms == NULL)
+        return NULL;
+    }
+
+  Py_INCREF(self->getperms);
+  return self->getperms;
+}
+
+static PyObject *
+Checker_get_set_permissions(Checker *self, void *closure)
+{
+  if (self->setperms == NULL)
+    {
+      self->setperms = PyDict_New();
+      if (self->setperms == NULL)
+        return NULL;
+    }
+
+  Py_INCREF(self->setperms);
+  return self->setperms;
+}
+
+static PyGetSetDef Checker_getset[] = {
+    {"get_permissions", 
+     (getter)Checker_get_get_permissions, NULL,
+     "getattr name to permission dictionary",
+     NULL},
+    {"set_permissions", 
+     (getter)Checker_get_set_permissions, NULL,
+     "setattr name to permission dictionary",
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+
+static PyTypeObject CheckerType = {
+	PyObject_HEAD_INIT(NULL)
+	/* ob_size           */ 0,
+	/* tp_name           */ "zope.security.checker."
+                                "Checker",
+	/* tp_basicsize      */ sizeof(Checker),
+	/* tp_itemsize       */ 0,
+	/* tp_dealloc        */ (destructor)&Checker_dealloc,
+	/* tp_print          */ (printfunc)0,
+	/* tp_getattr        */ (getattrfunc)0,
+	/* tp_setattr        */ (setattrfunc)0,
+	/* tp_compare        */ (cmpfunc)0,
+	/* tp_repr           */ (reprfunc)0,
+	/* tp_as_number      */ 0,
+	/* tp_as_sequence    */ 0,
+	/* tp_as_mapping     */ 0,
+	/* tp_hash           */ (hashfunc)0,
+	/* tp_call           */ (ternaryfunc)0,
+	/* tp_str            */ (reprfunc)0,
+        /* tp_getattro       */ (getattrofunc)0,
+        /* tp_setattro       */ (setattrofunc)0,
+        /* tp_as_buffer      */ 0,
+        /* tp_flags          */ Py_TPFLAGS_DEFAULT
+				| Py_TPFLAGS_BASETYPE 
+                          	| Py_TPFLAGS_HAVE_GC,
+	/* tp_doc            */ "Security checker",
+        /* tp_traverse       */ (traverseproc)Checker_traverse,
+        /* tp_clear          */ (inquiry)Checker_clear,
+        /* tp_richcompare    */ (richcmpfunc)0,
+        /* tp_weaklistoffset */ (long)0,
+        /* tp_iter           */ (getiterfunc)0,
+        /* tp_iternext       */ (iternextfunc)0,
+        /* tp_methods        */ Checker_methods,
+        /* tp_members        */ 0,
+        /* tp_getset         */ Checker_getset,
+        /* tp_base           */ 0,
+        /* tp_dict           */ 0, /* internal use */
+        /* tp_descr_get      */ (descrgetfunc)0,
+        /* tp_descr_set      */ (descrsetfunc)0,
+        /* tp_dictoffset     */ 0,
+        /* tp_init           */ (initproc)Checker_init,
+        /* tp_alloc          */ (allocfunc)0,
+        /* tp_new            */ (newfunc)0,
+	/* tp_free           */ 0, /* Low-level free-mem routine */
+	/* tp_is_gc          */ (inquiry)0, /* For PyObject_IS_GC */
+};
+
+
+
+
+
 /* def selectChecker(object): */
 /*     """Get a checker for the given object */
 /*     The appropriate checker is returned or None is returned. If the */
@@ -23,28 +410,6 @@
 {
   PyObject *checker;
 
-  /* Import names from checker is hasn't been done before */
-  if (_defaultChecker == NULL)
-    {
-      checker = PyImport_ImportModule("zope.security.checker");
-      if (checker == NULL)
-        return NULL;
-
-      Proxy = PyObject_GetAttrString(checker, "Proxy");
-      if (Proxy == NULL)
-        return NULL;
-
-      Checker = PyObject_GetAttrString(checker, "Checker");
-      if (Checker == NULL)
-        return NULL;
-
-      _defaultChecker = PyObject_GetAttrString(checker, "_defaultChecker");
-      if (_defaultChecker == NULL)
-        return NULL;
-
-      Py_DECREF(checker);
-    }
-
 /*     checker = _getChecker(type(object), _defaultChecker) */
 
   checker = PyDict_GetItem(_checkers, (PyObject*)(object->ob_type));
@@ -76,7 +441,7 @@
 /*             return None */
 
   Py_INCREF(checker);
-  while (! PyObject_TypeCheck(checker, (PyTypeObject*)Checker))
+  while (! PyObject_TypeCheck(checker, &CheckerType))
     {
       PyObject *newchecker;
       newchecker = PyObject_CallFunctionObjArgs(checker, object, NULL);
@@ -109,21 +474,67 @@
 PyMODINIT_FUNC
 init_zope_security_checker(void) 
 {
-    PyObject* m;
+  PyObject* m;
 
-    if ((_checkers = PyDict_New()) == NULL) return;
-    NoProxy = PyObject_CallObject((PyObject*)&PyBaseObject_Type, NULL);
-    if (NoProxy == NULL)
-      return;
+  CheckerType.tp_new = PyType_GenericNew;
+  if (PyType_Ready(&CheckerType) < 0)
+    return;
 
-    m = Py_InitModule3("_zope_security_checker", module_methods,
-                       "C optimizations for zope.security.checker");
+  _defaultChecker = PyObject_CallFunction((PyObject*)&CheckerType, "{}");
+  if (_defaultChecker == NULL)
+    return;
 
-    if (m == NULL)
-      return;
+#define INIT_STRING(S) \
+if((str_##S = PyString_InternFromString(#S)) == NULL) return
 
-    Py_INCREF(_checkers);
-    PyModule_AddObject(m, "_checkers", _checkers);
-    Py_INCREF(NoProxy);
-    PyModule_AddObject(m, "NoProxy", NoProxy);
+  INIT_STRING(checkPermission);
+  INIT_STRING(__Security_checker__);
+
+  if ((_checkers = PyDict_New()) == NULL) 
+    return;
+
+  NoProxy = PyObject_CallObject((PyObject*)&PyBaseObject_Type, NULL);
+  if (NoProxy == NULL)
+    return;
+
+  if ((m = PyImport_ImportModule("zope.security._proxy")) == NULL) return;
+  if ((Proxy = PyObject_GetAttrString(m, "_Proxy")) == NULL) return;
+  Py_DECREF(m);
+
+  if ((m = PyImport_ImportModule("zope.security.management")) == NULL) return;
+  getSecurityPolicy = PyObject_GetAttrString(m, "getSecurityPolicy");
+  if (getSecurityPolicy == NULL) return;
+  queryInteraction = PyObject_GetAttrString(m, "queryInteraction");
+  if (queryInteraction == NULL) return;
+  Py_DECREF(m);
+
+  if ((m = PyImport_ImportModule("zope.exceptions")) == NULL) return;
+  ForbiddenAttribute = PyObject_GetAttrString(m, "ForbiddenAttribute");
+  if (ForbiddenAttribute == NULL) return;
+  Unauthorized = PyObject_GetAttrString(m, "Unauthorized");
+  if (Unauthorized == NULL) return;
+  Py_DECREF(m);
+
+  if ((m = PyImport_ImportModule("zope.security.checker")) == NULL) return;
+  CheckerPublic = PyObject_GetAttrString(m, "CheckerPublic");
+  if (CheckerPublic == NULL) return;
+  Py_DECREF(m);
+
+  if ((_always_available = PyList_New(0)) == NULL) return;
+
+  m = Py_InitModule3("_zope_security_checker", module_methods,
+                     "C optimizations for zope.security.checker");
+  
+  if (m == NULL)
+    return;
+
+#define EXPORT(N) Py_INCREF(N); PyModule_AddObject(m, #N, N)
+
+  EXPORT(_checkers);
+  EXPORT(NoProxy);
+  EXPORT(_defaultChecker);
+  EXPORT(_always_available);
+    
+  Py_INCREF(&CheckerType);
+  PyModule_AddObject(m, "Checker", (PyObject *)&CheckerType);
 }

Modified: Zope3/trunk/src/zope/security/checker.py
===================================================================
--- Zope3/trunk/src/zope/security/checker.py	2004-06-21 21:26:15 UTC (rev 25932)
+++ Zope3/trunk/src/zope/security/checker.py	2004-06-22 12:13:32 UTC (rev 25933)
@@ -91,11 +91,7 @@
 directlyProvides(ProxyFactory, ISecurityProxyFactory)
 
 
-class TrustedCheckerBase:
-    """Marker type used by zope.security.proxy.trustedRemoveSecurityProxy"""
-
-
-class Checker(TrustedCheckerBase):
+class Checker(object):
     implements(INameBasedChecker)
 
     def __init__(self, get_permissions, set_permissions=None):
@@ -184,139 +180,7 @@
         return Proxy(value, checker)
 
 
-class CombinedChecker(Checker):
-    """A checker that combines two other checkers in a logical-or fashion.
 
-    The following table describes the result of a combined checker in detail.
-
-    checker1           checker2           CombinedChecker(checker1, checker2)
-    ------------------ ------------------ -----------------------------------
-    ok                 anything           ok (checker2 is never called)
-    Unauthorized       ok                 ok
-    Unauthorized       Unauthorized       Unauthorized
-    Unauthorized       ForbiddenAttribute Unauthorized
-    ForbiddenAttribute ok                 ok
-    ForbiddenAttribute Unauthorized       Unauthorized
-    ForbiddenAttribute ForbiddenAttribute ForbiddenAttribute
-    ------------------ ------------------ -----------------------------------
-    """
-    implements(IChecker)
-
-    def __init__(self, checker1, checker2):
-        """Create a combined checker."""
-        Checker.__init__(self,
-                         checker1.get_permissions,
-                         checker1.set_permissions)
-        self._checker2 = checker2
-
-    def check(self, object, name):
-        'See IChecker'
-        try:
-            Checker.check(self, object, name)
-        except ForbiddenAttribute:
-            self._checker2.check(object, name)
-        except Unauthorized, unauthorized_exception:
-            try: self._checker2.check(object, name)
-            except ForbiddenAttribute:
-                raise unauthorized_exception
-
-    def check_getattr(self, object, name):
-        'See IChecker'
-        try:
-            Checker.check_getattr(self, object, name)
-        except ForbiddenAttribute:
-            self._checker2.check_getattr(object, name)
-        except Unauthorized, unauthorized_exception:
-            try: self._checker2.check_getattr(object, name)
-            except ForbiddenAttribute:
-                raise unauthorized_exception
-
-    def check_setattr(self, object, name):
-        'See IChecker'
-        try:
-            Checker.check_setattr(self, object, name)
-        except ForbiddenAttribute:
-            self._checker2.check_setattr(object, name)
-        except Unauthorized, unauthorized_exception:
-            try: self._checker2.check_setattr(object, name)
-            except ForbiddenAttribute:
-                raise unauthorized_exception
-
-class CheckerLoggingMixin:
-    """Debugging mixin for checkers.
-
-    Prints verbose debugging information about every performed check to
-    sys.stderr.
-
-    If verbosity is set to 1, only displays Unauthorized and Forbidden messages.
-    If verbosity is set to a larger number, displays all messages.
-    """
-
-    verbosity = 1
-
-    def check(self, object, name):
-        try:
-            super(CheckerLoggingMixin, self).check(object, name)
-            if self.verbosity > 1:
-                if name in _always_available:
-                    print >> sys.stderr, (
-                        '[CHK] + Always available: %s on %r' % (name, object))
-                else:
-                    print >> sys.stderr, (
-                        '[CHK] + Granted: %s on %r' % (name, object))
-        except Unauthorized:
-            print >> sys.stderr, (
-                '[CHK] - Unauthorized: %s on %r' % (name, object))
-            raise
-        except ForbiddenAttribute:
-            print >> sys.stderr, (
-                '[CHK] - Forbidden: %s on %r' % (name, object))
-            raise
-
-    def check_getattr(self, object, name):
-        try:
-            super(CheckerLoggingMixin, self).check(object, name)
-            if self.verbosity > 1:
-                if name in _always_available:
-                    print >> sys.stderr, (
-                        '[CHK] + Always available getattr: %s on %r'
-                        % (name, object))
-                else:
-                    print >> sys.stderr, (
-                        '[CHK] + Granted getattr: %s on %r'
-                        % (name, object))
-        except Unauthorized:
-            print >> sys.stderr, (
-                '[CHK] - Unauthorized getattr: %s on %r' % (name, object))
-            raise
-        except ForbiddenAttribute:
-            print >> sys.stderr, (
-                '[CHK] - Forbidden getattr: %s on %r' % (name, object))
-            raise
-
-    def check_setattr(self, object, name):
-        try:
-            super(CheckerLoggingMixin, self).check_setattr(object, name)
-            if self.verbosity > 1:
-                print >> sys.stderr, (
-                    '[CHK] + Granted setattr: %s on %r' % (name, object))
-        except Unauthorized:
-            print >> sys.stderr, (
-                '[CHK] - Unauthorized setattr: %s on %r' % (name, object))
-            raise
-        except ForbiddenAttribute:
-            print >> sys.stderr, (
-                '[CHK] - Forbidden setattr: %s on %r' % (name, object))
-            raise
-
-
-if WATCH_CHECKERS:
-    class Checker(CheckerLoggingMixin, Checker):
-        verbosity = WATCH_CHECKERS
-    class CombinedChecker(CheckerLoggingMixin, CombinedChecker):
-        verbosity = WATCH_CHECKERS
-
-
 # Helper class for __traceback_supplement__
 class TracebackSupplement:
 
@@ -504,6 +368,9 @@
 #
 _checkers = {}
 
+_defaultChecker = Checker({})
+_always_available = []
+
 # Get optimized versions
 try:
     import zope.security._zope_security_checker
@@ -511,12 +378,150 @@
     pass
 else:
     from zope.security._zope_security_checker import _checkers, selectChecker
-    from zope.security._zope_security_checker import NoProxy
+    from zope.security._zope_security_checker import NoProxy, Checker
+    from zope.security._zope_security_checker import _defaultChecker
+    from zope.security._zope_security_checker import _always_available
+    zope.interface.classImplements(Checker, INameBasedChecker)
 
+
 _getChecker = _checkers.get
 
-_defaultChecker = Checker({})
 
+# Marker type used by bogus zope.security.proxy.trustedRemoveSecurityProxy
+TrustedCheckerBase = Checker
+
+class CombinedChecker(Checker):
+    """A checker that combines two other checkers in a logical-or fashion.
+
+    The following table describes the result of a combined checker in detail.
+
+    checker1           checker2           CombinedChecker(checker1, checker2)
+    ------------------ ------------------ -----------------------------------
+    ok                 anything           ok (checker2 is never called)
+    Unauthorized       ok                 ok
+    Unauthorized       Unauthorized       Unauthorized
+    Unauthorized       ForbiddenAttribute Unauthorized
+    ForbiddenAttribute ok                 ok
+    ForbiddenAttribute Unauthorized       Unauthorized
+    ForbiddenAttribute ForbiddenAttribute ForbiddenAttribute
+    ------------------ ------------------ -----------------------------------
+    """
+    implements(IChecker)
+
+    def __init__(self, checker1, checker2):
+        """Create a combined checker."""
+        Checker.__init__(self,
+                         checker1.get_permissions,
+                         checker1.set_permissions)
+        self._checker2 = checker2
+
+    def check(self, object, name):
+        'See IChecker'
+        try:
+            Checker.check(self, object, name)
+        except ForbiddenAttribute:
+            self._checker2.check(object, name)
+        except Unauthorized, unauthorized_exception:
+            try: self._checker2.check(object, name)
+            except ForbiddenAttribute:
+                raise unauthorized_exception
+
+    def check_getattr(self, object, name):
+        'See IChecker'
+        try:
+            Checker.check_getattr(self, object, name)
+        except ForbiddenAttribute:
+            self._checker2.check_getattr(object, name)
+        except Unauthorized, unauthorized_exception:
+            try: self._checker2.check_getattr(object, name)
+            except ForbiddenAttribute:
+                raise unauthorized_exception
+
+    def check_setattr(self, object, name):
+        'See IChecker'
+        try:
+            Checker.check_setattr(self, object, name)
+        except ForbiddenAttribute:
+            self._checker2.check_setattr(object, name)
+        except Unauthorized, unauthorized_exception:
+            try: self._checker2.check_setattr(object, name)
+            except ForbiddenAttribute:
+                raise unauthorized_exception
+
+class CheckerLoggingMixin:
+    """Debugging mixin for checkers.
+
+    Prints verbose debugging information about every performed check to
+    sys.stderr.
+
+    If verbosity is set to 1, only displays Unauthorized and Forbidden messages.
+    If verbosity is set to a larger number, displays all messages.
+    """
+
+    verbosity = 1
+
+    def check(self, object, name):
+        try:
+            super(CheckerLoggingMixin, self).check(object, name)
+            if self.verbosity > 1:
+                if name in _always_available:
+                    print >> sys.stderr, (
+                        '[CHK] + Always available: %s on %r' % (name, object))
+                else:
+                    print >> sys.stderr, (
+                        '[CHK] + Granted: %s on %r' % (name, object))
+        except Unauthorized:
+            print >> sys.stderr, (
+                '[CHK] - Unauthorized: %s on %r' % (name, object))
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, (
+                '[CHK] - Forbidden: %s on %r' % (name, object))
+            raise
+
+    def check_getattr(self, object, name):
+        try:
+            super(CheckerLoggingMixin, self).check(object, name)
+            if self.verbosity > 1:
+                if name in _always_available:
+                    print >> sys.stderr, (
+                        '[CHK] + Always available getattr: %s on %r'
+                        % (name, object))
+                else:
+                    print >> sys.stderr, (
+                        '[CHK] + Granted getattr: %s on %r'
+                        % (name, object))
+        except Unauthorized:
+            print >> sys.stderr, (
+                '[CHK] - Unauthorized getattr: %s on %r' % (name, object))
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, (
+                '[CHK] - Forbidden getattr: %s on %r' % (name, object))
+            raise
+
+    def check_setattr(self, object, name):
+        try:
+            super(CheckerLoggingMixin, self).check_setattr(object, name)
+            if self.verbosity > 1:
+                print >> sys.stderr, (
+                    '[CHK] + Granted setattr: %s on %r' % (name, object))
+        except Unauthorized:
+            print >> sys.stderr, (
+                '[CHK] - Unauthorized setattr: %s on %r' % (name, object))
+            raise
+        except ForbiddenAttribute:
+            print >> sys.stderr, (
+                '[CHK] - Forbidden setattr: %s on %r' % (name, object))
+            raise
+
+
+if WATCH_CHECKERS:
+    class Checker(CheckerLoggingMixin, Checker):
+        verbosity = WATCH_CHECKERS
+    class CombinedChecker(CheckerLoggingMixin, CombinedChecker):
+        verbosity = WATCH_CHECKERS
+
 def _instanceChecker(inst):
     checker = _checkers.get(inst.__class__, _defaultChecker)
     if checker is _defaultChecker and isinstance(inst, Exception):
@@ -536,12 +541,12 @@
 # The variable '_always_available' should really be called
 # '_available_by_default', as that would better reflect its meaning.
 # XXX: Fix the name.
-_always_available = ['__lt__', '__le__', '__eq__',
-                     '__gt__', '__ge__', '__ne__',
-                     '__hash__', '__nonzero__',
-                     '__class__', '__providedBy__', '__implements__',
-                     '__repr__', '__conform__',
-                     ]
+_always_available [:] = ['__lt__', '__le__', '__eq__',
+                         '__gt__', '__ge__', '__ne__',
+                         '__hash__', '__nonzero__',
+                         '__class__', '__providedBy__', '__implements__',
+                         '__repr__', '__conform__',
+                         ]
 
 _callableChecker = NamesChecker(['__str__', '__name__', '__call__'])
 _typeChecker = NamesChecker(




More information about the Zope3-Checkins mailing list