[Zope3-checkins] SVN: Zope3/trunk/src/zope/security/ No longer
re-proxying objects that provide an explicit __Security_checker__.
Garrett Smith
garrett at mojave-corp.com
Tue Mar 15 15:17:08 EST 2005
Log message for revision 29482:
No longer re-proxying objects that provide an explicit __Security_checker__.
Changed:
U Zope3/trunk/src/zope/security/_zope_security_checker.c
U Zope3/trunk/src/zope/security/checker.py
U Zope3/trunk/src/zope/security/tests/test_checker.py
-=-
Modified: Zope3/trunk/src/zope/security/_zope_security_checker.c
===================================================================
--- Zope3/trunk/src/zope/security/_zope_security_checker.c 2005-03-15 18:05:14 UTC (rev 29481)
+++ Zope3/trunk/src/zope/security/_zope_security_checker.c 2005-03-15 20:17:08 UTC (rev 29482)
@@ -90,7 +90,7 @@
Py_DECREF(r);
if (i < 0)
return -1;
- if (i)
+ if (i)
return 0;
/* else: */
/* __traceback_supplement__ = (TracebackSupplement, object) */
@@ -133,7 +133,7 @@
}
- operator = (PyString_Check(name)
+ operator = (PyString_Check(name)
&& PyString_AS_STRING(name)[0] == '_'
&& PyString_AS_STRING(name)[1] == '_');
@@ -150,10 +150,10 @@
/* 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
+ /* 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. */
return 0;
}
@@ -214,7 +214,7 @@
Py_INCREF(Py_None);
return Py_None;
}
-
+
/* __traceback_supplement__ = (TracebackSupplement, object) */
/* raise ForbiddenAttribute, (name, object) */
args = Py_BuildValue("OO", name, object);
@@ -235,6 +235,15 @@
Checker_proxy(Checker *self, PyObject *value)
{
PyObject *checker, *r;
+
+/* if type(value) is Proxy: */
+/* return value */
+ if ((PyObject*)(value->ob_type) == Proxy)
+ {
+ Py_INCREF(value);
+ return value;
+ }
+
/* checker = getattr(value, '__Security_checker__', None) */
checker = PyObject_GetAttr(value, str___Security_checker__);
/* if checker is None: */
@@ -267,10 +276,10 @@
PyErr_SetObject(PyExc_ValueError, errv);
Py_DECREF(errv);
}
-
+
return NULL;
}
-
+
r = PyObject_CallFunctionObjArgs(Proxy, value, checker, NULL);
Py_DECREF(checker);
return r;
@@ -282,7 +291,7 @@
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,
+ {"setattr_permission_id", (PyCFunction)Checker_setattr_permission_id,
METH_O,
"setattr_permission_id(name) -- Return the permission neded to set the name"
},
@@ -320,7 +329,7 @@
return -1;
if (self->setperms != NULL && visit(self->setperms, arg) < 0)
return -1;
-
+
return 0;
}
@@ -330,10 +339,10 @@
PyObject *getperms, *setperms=NULL;
static char *kwlist[] = {"get_permissions", "set_permissions", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!:Checker", kwlist,
- &PyDict_Type, &getperms,
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!:Checker", kwlist,
+ &PyDict_Type, &getperms,
&PyDict_Type, &setperms))
- return -1;
+ return -1;
Py_INCREF(getperms);
self->getperms = getperms;
@@ -372,11 +381,11 @@
}
static PyGetSetDef Checker_getset[] = {
- {"get_permissions",
+ {"get_permissions",
(getter)Checker_get_get_permissions, NULL,
"getattr name to permission dictionary",
NULL},
- {"set_permissions",
+ {"set_permissions",
(getter)Checker_get_set_permissions, NULL,
"setattr name to permission dictionary",
NULL},
@@ -421,7 +430,7 @@
/* tp_setattro */ (setattrofunc)0,
/* tp_as_buffer */ 0,
/* tp_flags */ Py_TPFLAGS_DEFAULT
- | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GC,
/* tp_doc */ "Security checker",
/* tp_traverse */ (traverseproc)Checker_traverse,
@@ -455,7 +464,7 @@
/* return value is None, then object should not be wrapped in a proxy. */
/* """ */
-static char selectChecker_doc[] =
+static char selectChecker_doc[] =
"Get a checker for the given object\n"
"\n"
"The appropriate checker is returned or None is returned. If the\n"
@@ -485,7 +494,7 @@
/* if checker is _defaultChecker and isinstance(object, Exception): */
/* return None */
- if (checker == _defaultChecker
+ if (checker == _defaultChecker
&& PyObject_IsInstance(object, PyExc_Exception))
{
Py_INCREF(Py_None);
@@ -529,7 +538,7 @@
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
-init_zope_security_checker(void)
+init_zope_security_checker(void)
{
PyObject* m;
@@ -548,7 +557,7 @@
INIT_STRING(__Security_checker__);
INIT_STRING(interaction);
- if ((_checkers = PyDict_New()) == NULL)
+ if ((_checkers = PyDict_New()) == NULL)
return;
NoProxy = PyObject_CallObject((PyObject*)&PyBaseObject_Type, NULL);
@@ -580,7 +589,7 @@
m = Py_InitModule3("_zope_security_checker", module_methods,
"C optimizations for zope.security.checker");
-
+
if (m == NULL)
return;
@@ -590,7 +599,7 @@
EXPORT(NoProxy);
EXPORT(_defaultChecker);
EXPORT(_available_by_default);
-
+
Py_INCREF(&CheckerType);
PyModule_AddObject(m, "Checker", (PyObject *)&CheckerType);
}
Modified: Zope3/trunk/src/zope/security/checker.py
===================================================================
--- Zope3/trunk/src/zope/security/checker.py 2005-03-15 18:05:14 UTC (rev 29481)
+++ Zope3/trunk/src/zope/security/checker.py 2005-03-15 20:17:08 UTC (rev 29482)
@@ -193,6 +193,8 @@
def proxy(self, value):
'See IChecker'
+ if type(value) is Proxy:
+ return value
checker = getattr(value, '__Security_checker__', None)
if checker is None:
checker = selectChecker(value)
Modified: Zope3/trunk/src/zope/security/tests/test_checker.py
===================================================================
--- Zope3/trunk/src/zope/security/tests/test_checker.py 2005-03-15 18:05:14 UTC (rev 29481)
+++ Zope3/trunk/src/zope/security/tests/test_checker.py 2005-03-15 20:17:08 UTC (rev 29482)
@@ -20,6 +20,7 @@
from zope.interface.verify import verifyObject
from zope.security.checker import Checker, NamesChecker, CheckerPublic
from zope.testing.cleanup import CleanUp
+from zope.proxy import getProxiedObject
from zope.security.interfaces import ISecurityPolicy
from zope.security.interfaces import Forbidden, ForbiddenAttribute
from zope.security.interfaces import Unauthorized
@@ -245,6 +246,24 @@
# proxy2 = checker.proxy(proxy)
# self.failUnless(proxy2 is proxy, [proxy, proxy2])
+ def testLayeredProxies(self):
+ """Tests that a Proxy will not be re-proxied."""
+ class Base:
+ __Security_checker__ = NamesChecker(['__Security_checker__'])
+ base = Base()
+ checker = Checker({})
+
+ # base is not proxied, so we expect a proxy
+ proxy1 = checker.proxy(base)
+ self.assert_(type(proxy1) is Proxy)
+ self.assert_(getProxiedObject(proxy1) is base)
+
+ # proxy is a proxy, so we don't expect to get another
+ proxy2 = checker.proxy(proxy1)
+ self.assert_(proxy2 is proxy1)
+ self.assert_(getProxiedObject(proxy2) is base)
+
+
def testMultiChecker(self):
from zope.interface import Interface
@@ -366,39 +385,39 @@
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
+ # 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
+ # 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
+ # 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'
+ # 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
+
+ # 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
+ # 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(
@@ -408,19 +427,19 @@
{'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
+
+ # 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.
More information about the Zope3-Checkins
mailing list