Zope Subversion Repository |
|
Zope: Zope3/trunk/src/zope/security/_zope_security_checker.c
File:
[Zope] /
Zope3 /
trunk /
src /
zope /
security / _zope_security_checker.c
(
download)
Revision:
25968,
Thu Jun 24 15:12:01 2004 UTC (8 years, 11 months ago) by
jim
File size: 16404 byte(s)
Added comments explaining the slot-performance hacks I added.
Thanks to Tim for pointing out my omission.
/*
Copyright (c) 2004 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.
*/
#include <Python.h>
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): */
/* Note that we have an int version gere because we will use it for
__setitem__, as describd below */
static int
Checker_check_int(Checker *self, PyObject *object, PyObject *name)
{
PyObject *permission=NULL;
int operator;
/* 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)
return 0;
if (checkPermission(permission, object, name) < 0)
return -1;
return 0;
}
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 -1;
if (ic)
return 0;
/* 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. */
return 0;
}
{
PyObject *args;
args = Py_BuildValue("OO", name, object);
if (args != NULL)
{
PyErr_SetObject(ForbiddenAttribute, args);
Py_DECREF(args);
}
return -1;
}
}
/* Here we have the non-int version, implemented using the int
version, which is exposed as a method */
static PyObject *
Checker_check(Checker *self, PyObject *args)
{
PyObject *object, *name;
if (!PyArg_ParseTuple(args, "OO", &object, &name))
return NULL;
if (Checker_check_int(self, object, name) < 0)
return NULL;
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 */
};
/* We create operator aliases for check and proxy. Why? Because
calling operator slots is much faster than calling methods and
security checks are done so often that speed matters. So we have
this hack of using almost-arbitrary operations to represent methods
that we call alot. The security proxy implementation participates
in the same hack. */
static PyMappingMethods Checker_as_mapping = {
/* mp_length */ (inquiry)NULL,
/* mp_subscript */ (binaryfunc)Checker_proxy,
/* mp_ass_subscript */ (objobjargproc)Checker_check_int,
};
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 */ &Checker_as_mapping,
/* 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 */
/* return value is None, then object should not be wrapped in a proxy. */
/* """ */
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"
"return value is None, then object should not be wrapped in a proxy.\n"
;
static PyObject *
selectChecker(PyObject *ignored, PyObject *object)
{
PyObject *checker;
/* checker = _getChecker(type(object), _defaultChecker) */
checker = PyDict_GetItem(_checkers, (PyObject*)(object->ob_type));
if (checker == NULL)
checker = _defaultChecker;
/* if checker is NoProxy: */
/* return None */
if (checker == NoProxy)
{
Py_INCREF(Py_None);
return Py_None;
}
/* if checker is _defaultChecker and isinstance(object, Exception): */
/* return None */
if (checker == _defaultChecker
&& PyObject_IsInstance(object, PyExc_Exception))
{
Py_INCREF(Py_None);
return Py_None;
}
/* while not isinstance(checker, Checker): */
/* checker = checker(object) */
/* if checker is NoProxy or checker is None: */
/* return None */
Py_INCREF(checker);
while (! PyObject_TypeCheck(checker, &CheckerType))
{
PyObject *newchecker;
newchecker = PyObject_CallFunctionObjArgs(checker, object, NULL);
Py_DECREF(checker);
if (newchecker == NULL)
return NULL;
checker = newchecker;
if (checker == NoProxy || checker == Py_None)
{
Py_DECREF(checker);
Py_INCREF(Py_None);
return Py_None;
}
}
/* return checker */
return checker;
}
static PyMethodDef module_methods[] = {
{"selectChecker", (PyCFunction)selectChecker, METH_O, selectChecker_doc},
{NULL} /* Sentinel */
};
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_zope_security_checker(void)
{
PyObject* m;
CheckerType.tp_new = PyType_GenericNew;
if (PyType_Ready(&CheckerType) < 0)
return;
_defaultChecker = PyObject_CallFunction((PyObject*)&CheckerType, "{}");
if (_defaultChecker == NULL)
return;
#define INIT_STRING(S) \
if((str_##S = PyString_InternFromString(#S)) == NULL) return
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);
}