zope.security-3.8.3/0000755000076600000240000000000011637266524014250 5ustar macstaff00000000000000zope.security-3.8.3/bootstrap.py0000644000076600000240000000733011637266447016646 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib2 from optparse import OptionParser tmpeggs = tempfile.mkdtemp() is_jython = sys.platform.startswith('java') # parsing arguments parser = OptionParser() parser.add_option("-v", "--version", dest="version", help="use a specific zc.buildout version") parser.add_option("-d", "--distribute", action="store_true", dest="distribute", default=False, help="Use Disribute rather than Setuptools.") parser.add_option("-c", None, action="store", dest="config_file", help=("Specify the path to the buildout configuration " "file to be used.")) options, args = parser.parse_args() # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args += ['-c', options.config_file] if options.version is not None: VERSION = '==%s' % options.version else: VERSION = '' USE_DISTRIBUTE = options.distribute args = args + ['bootstrap'] to_reload = False try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): to_reload = True raise ImportError except ImportError: ez = {} if USE_DISTRIBUTE: exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) else: exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) if to_reload: reload(pkg_resources) else: import pkg_resources if sys.platform == 'win32': def quote(c): if ' ' in c: return '"%s"' % c # work around spawn lamosity on windows else: return c else: def quote (c): return c cmd = 'from setuptools.command.easy_install import main; main()' ws = pkg_resources.working_set if USE_DISTRIBUTE: requirement = 'distribute' else: requirement = 'setuptools' if is_jython: import subprocess assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', quote(tmpeggs), 'zc.buildout' + VERSION], env=dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ).wait() == 0 else: assert os.spawnle( os.P_WAIT, sys.executable, quote (sys.executable), '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout' + VERSION) import zc.buildout.buildout zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) zope.security-3.8.3/buildout.cfg0000644000076600000240000000100611637266447016561 0ustar macstaff00000000000000[buildout] develop = . parts = test python coverage-test coverage-report [test] recipe = zc.recipe.testrunner eggs = zope.security [test,untrustedpython,zcml,pytz] [python] recipe = zc.recipe.egg eggs = zope.security [untrustedpython] interpreter = python [coverage-test] recipe = zc.recipe.testrunner eggs = zope.security [test] defaults = ['--coverage', '../../coverage'] [coverage-report] recipe = zc.recipe.egg eggs = z3c.coverage scripts = coverage=coverage-report arguments = ('coverage', 'coverage/report') zope.security-3.8.3/CHANGES.txt0000644000076600000240000002252011637266447016066 0ustar macstaff00000000000000======= CHANGES ======= 3.8.3 (2011-09-24) ------------------ - Fixed a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ------------------ - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ------------------ - Fixed circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ------------------ - Added tests for our own ``configure.zcml``. - Added ``zcml`` extra dependencies, run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ------------------ - Prefer the standard libraries doctest module to the one from zope.testing. - Fixed directlyProvides IVocabularyFactory for PermissionIdsVocabulary in Python code, even if it's unnecessary because IVocabularyFactory is provided in zcml. - Removed the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ------------------ - Added compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ------------------ - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ------------------ - Made ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ------------------ - Ensure that simple zope.schema's VocabularyRegistry is used for PermissionVocabulary tests, because it's replaced implicitly in environments with zope.app.schema installed that makes that tests fail. - Fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ------------------ - Add zope.i18nmessageid.Message to non-proxied basic types. It's okay, because messages are immutable. It was done by zope.app.security before. - Add "__name__" and "__parent__" attributes to list of available by default. This was also done by zope.app.security package before. - Added PermissionsVocabulary and PermissionIdsVocabulary vocabularies to the ``zope.security.permission`` module. They were moved from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like "zope.View" and "zope.ManageContent", as well as for the special "zope.Public" permission. They are placed in a separate "permissions.zcml" file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many zope.* packages. - Add `addCheckerPublic` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ------------------ - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise NoInteraction when zope.security.checkPermission is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and `set` and `frozenset` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ------------------ - Install decorated security checker support on LocationProxy from the outside. - Added support to bootstrap on Jython. - Moved the `protectclass` module from `zope.app.security` to this package to reduce the number of dependencies on `zope.app.security`. - Moved the directive implementation from `zope.app.security` to this package. - Moved the directive implementation from `zope.app.component` to this package. 3.5.2 (2008-07-27) ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ------------------ - Add `frozenset`, `set`, `reversed`, and `sorted` to the list of safe builtins. 3.5.0 (2008-03-05) ------------------ - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) -------------------- - Backported a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) -------------------- - Added dependency 'zope.thread' to setup.py, without the tests were failing. - Backported a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ------------------ - Updated meta-data. 3.4.0b5 (2007-08-15) -------------------- - Bug: Fixed a circular import in the C implementation. 3.4.0b4 (2007-08-14) -------------------- - Bug: ``zope.security.management.system_user`` had an ugly/brittle id. 3.4.0b3 (2007-08-14) -------------------- - ``zope.security`` now works on Python 2.5 - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fixed inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) -------------------- - Bug: Removed stack extraction in newInteraction. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ------- - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.2.0 release. - Removed deprecated helper functions, 'proxy.trustedRemoveSecurityProxy' and 'proxy.getProxiedObject'. - Made handling of 'management.{end,restore}Interaction' more careful w.r.t. edge cases. - Made behavior of 'canWrite' consistent with 'canAccess': if 'canAccess' does not raise 'ForbiddenAttribute', then neither will 'canWrite'. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ------------------ - Added support for use of the new Python 2.4 datatypes, 'set' and 'frozenset', within checked code. - C security proxy acquired a dependency on the 'proxy.h' header from the 'zope.proxy' package. - XXX: the spelling of the '#include' is bizarre! It seems to be related to 'zpkg'-based builds, and should likely be revisited. For the moment, I have linked in the 'zope.proxy' package into our own 'include' directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Updated checker to avoid re-proxying objects which have and explicit '__Security_checker__' assigned. - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.1.0 release. - Clarified contract of 'IChecker' to indicate that its 'check*' methods may raise only 'Forbidden' or 'Unauthorized' exceptions. - Added interfaces, ('IPrincipal', 'IGroupAwarePrincipal', 'IGroup', and 'IPermission') specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ------------------ - Corresponds to the version of the zope.security package shipped as part of the Zope X3.0.0 release. zope.security-3.8.3/COPYRIGHT.txt0000644000076600000240000000004011637266447016357 0ustar macstaff00000000000000Zope Foundation and Contributorszope.security-3.8.3/include/0000755000076600000240000000000011637266524015673 5ustar macstaff00000000000000zope.security-3.8.3/include/zope.proxy/0000755000076600000240000000000011637266524020030 5ustar macstaff00000000000000zope.security-3.8.3/include/zope.proxy/__init__.py0000644000076600000240000000216611637266452022146 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """More convenience functions for dealing with proxies. """ from zope.interface import moduleProvides from zope.proxy.interfaces import IProxyIntrospection from zope.proxy._zope_proxy_proxy import * from zope.proxy._zope_proxy_proxy import _CAPI moduleProvides(IProxyIntrospection) __all__ = tuple(IProxyIntrospection) def ProxyIterator(p): yield p while isProxy(p): p = getProxiedObject(p) yield p def non_overridable(func): return property(lambda self: func.__get__(self)) zope.security-3.8.3/include/zope.proxy/_zope_proxy_proxy.c0000644000076600000240000007533511637266452024027 0ustar macstaff00000000000000/*############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################*/ /* * This file is also used as a really extensive macro in * ../container/_zope_container_contained.c. If you need to * change this file, you need to "svn copy" it to ../container/. * * This approach is taken to allow the sources for the two packages * to be compilable when the relative locations of these aren't * related in the same way as they are in a checkout. * * This will be revisited in the future, but works for now. */ #include "Python.h" #include "modsupport.h" #define PROXY_MODULE #include "proxy.h" static PyTypeObject ProxyType; #define Proxy_Check(wrapper) (PyObject_TypeCheck((wrapper), &ProxyType)) static PyObject * empty_tuple = NULL; // Compatibility with Python 2 #if PY_MAJOR_VERSION < 3 #define IS_STRING PyString_Check #define MAKE_STRING(name) PyString_AS_STRING(name) #define MOD_ERROR_VAL #define MOD_SUCCESS_VAL(val) #define MOD_INIT(name) void init##name(void) #define MOD_DEF(ob, name, doc, methods) \ ob = Py_InitModule3(name, methods, doc); #define PyCapsule_New(pointer, name, destr) \ PyCObject_FromVoidPtr(pointer, destr) #else #define IS_STRING PyUnicode_Check #define MAKE_STRING(name) PyBytes_AS_STRING( \ PyUnicode_AsUTF8String(name)) #define MOD_ERROR_VAL NULL #define MOD_SUCCESS_VAL(val) val #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #endif /* * Slot methods. */ static PyObject * wrap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *result = NULL; PyObject *object; if (PyArg_UnpackTuple(args, "__new__", 1, 1, &object)) { if (kwds != NULL && PyDict_Size(kwds) != 0) { PyErr_SetString(PyExc_TypeError, "proxy.__new__ does not accept keyword args"); return NULL; } result = PyType_GenericNew(type, args, kwds); if (result != NULL) { ProxyObject *wrapper = (ProxyObject *) result; Py_INCREF(object); wrapper->proxy_object = object; } } return result; } static int wrap_init(PyObject *self, PyObject *args, PyObject *kwds) { int result = -1; PyObject *object; if (PyArg_UnpackTuple(args, "__init__", 1, 1, &object)) { ProxyObject *wrapper = (ProxyObject *)self; if (kwds != NULL && PyDict_Size(kwds) != 0) { PyErr_SetString(PyExc_TypeError, "proxy.__init__ does not accept keyword args"); return -1; } /* If the object in this proxy is not the one we * received in args, replace it with the new one. */ if (wrapper->proxy_object != object) { PyObject *temp = wrapper->proxy_object; Py_INCREF(object); wrapper->proxy_object = object; Py_DECREF(temp); } result = 0; } return result; } static int wrap_traverse(PyObject *self, visitproc visit, void *arg) { PyObject *ob = Proxy_GET_OBJECT(self); if (ob != NULL) return visit(ob, arg); else return 0; } static int wrap_clear(PyObject *self) { ProxyObject *proxy = (ProxyObject *)self; PyObject *temp = proxy->proxy_object; if (temp != NULL) { proxy->proxy_object = NULL; Py_DECREF(temp); } return 0; } static PyObject * wrap_richcompare(PyObject* self, PyObject* other, int op) { if (Proxy_Check(self)) { self = Proxy_GET_OBJECT(self); } else { other = Proxy_GET_OBJECT(other); } return PyObject_RichCompare(self, other, op); } static PyObject * wrap_iter(PyObject *self) { return PyObject_GetIter(Proxy_GET_OBJECT(self)); } static PyObject * wrap_iternext(PyObject *self) { return PyIter_Next(Proxy_GET_OBJECT(self)); } static void wrap_dealloc(PyObject *self) { (void) wrap_clear(self); self->ob_type->tp_free(self); } /* A variant of _PyType_Lookup that doesn't look in ProxyType. * * If argument search_wrappertype is nonzero, we can look in WrapperType. */ PyObject * WrapperType_Lookup(PyTypeObject *type, PyObject *name) { int i, n; PyObject *mro, *res, *base, *dict; /* Look in tp_dict of types in MRO */ mro = type->tp_mro; /* If mro is NULL, the type is either not yet initialized by PyType_Ready(), or already cleared by type_clear(). Either way the safest thing to do is to return NULL. */ if (mro == NULL) return NULL; assert(PyTuple_Check(mro)); n = PyTuple_GET_SIZE(mro) - 1; /* We don't want to look at the last item, which is object. */ for (i = 0; i < n; i++) { base = PyTuple_GET_ITEM(mro, i); if (((PyTypeObject *)base) != &ProxyType) { #if PY_MAJOR_VERSION < 3 if (PyClass_Check(base)) dict = ((PyClassObject *)base)->cl_dict; else #endif { assert(PyType_Check(base)); dict = ((PyTypeObject *)base)->tp_dict; } assert(dict && PyDict_Check(dict)); res = PyDict_GetItem(dict, name); if (res != NULL) return res; } } return NULL; } static PyObject * wrap_getattro(PyObject *self, PyObject *name) { PyObject *wrapped; PyObject *descriptor; PyObject *res = NULL; const char *name_as_string; int maybe_special_name; #if PY_MAJOR_VERSION < 3 && defined(Py_USING_UNICODE) /* The Unicode to string conversion is done here because the existing tp_setattro slots expect a string object as name (except under Python 3) and we wouldn't want to break those. */ if (PyUnicode_Check(name)) { name = PyUnicode_AsEncodedString(name, NULL, NULL); if (name == NULL) return NULL; } else #endif if (!IS_STRING(name)){ PyErr_SetString(PyExc_TypeError, "attribute name must be string"); return NULL; } else Py_INCREF(name); name_as_string = MAKE_STRING(name); wrapped = Proxy_GET_OBJECT(self); if (wrapped == NULL) { PyErr_Format(PyExc_RuntimeError, "object is NULL; requested to get attribute '%s'", name_as_string); goto finally; } maybe_special_name = name_as_string[0] == '_' && name_as_string[1] == '_'; if (!(maybe_special_name && strcmp(name_as_string, "__class__") == 0)) { descriptor = WrapperType_Lookup(self->ob_type, name); if (descriptor != NULL) { if (descriptor->ob_type->tp_descr_get != NULL #if PY_MAJOR_VERSION < 3 // Always true in Python 3 && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) #endif ){ if (descriptor->ob_type->tp_descr_set == NULL) { res = PyObject_GetAttr(wrapped, name); if (res != NULL) goto finally; if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); else goto finally; } res = descriptor->ob_type->tp_descr_get( descriptor, self, (PyObject *)self->ob_type); } else { Py_INCREF(descriptor); res = descriptor; } goto finally; } } res = PyObject_GetAttr(wrapped, name); finally: Py_DECREF(name); return res; } static int wrap_setattro(PyObject *self, PyObject *name, PyObject *value) { PyObject *wrapped; PyObject *descriptor; const char *name_as_string; int res = -1; #if PY_MAJOR_VERSION < 3 && defined(Py_USING_UNICODE) /* The Unicode to string conversion is done here because the existing tp_setattro slots expect a string object as name (except under Python 3) and we wouldn't want to break those. */ if (PyUnicode_Check(name)) { name = PyUnicode_AsEncodedString(name, NULL, NULL); if (name == NULL) return -1; } else #endif if (!IS_STRING(name)){ PyErr_SetString(PyExc_TypeError, "attribute name must be string"); return -1; } else Py_INCREF(name); descriptor = WrapperType_Lookup(self->ob_type, name); if (descriptor != NULL #if PY_MAJOR_VERSION < 3 // This is always true in Python 3 (I think) && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) #endif && descriptor->ob_type->tp_descr_set != NULL) { res = descriptor->ob_type->tp_descr_set(descriptor, self, value); goto finally; } name_as_string = MAKE_STRING(name); wrapped = Proxy_GET_OBJECT(self); if (wrapped == NULL) { PyErr_Format(PyExc_RuntimeError, "object is NULL; requested to set attribute '%s'", name_as_string); goto finally; } res = PyObject_SetAttr(wrapped, name, value); finally: Py_DECREF(name); return res; } static int wrap_print(PyObject *wrapper, FILE *fp, int flags) { return PyObject_Print(Proxy_GET_OBJECT(wrapper), fp, flags); } static PyObject * wrap_str(PyObject *wrapper) { return PyObject_Str(Proxy_GET_OBJECT(wrapper)); } static PyObject * wrap_repr(PyObject *wrapper) { return PyObject_Repr(Proxy_GET_OBJECT(wrapper)); } #if PY_MAJOR_VERSION < 3 static int wrap_compare(PyObject *wrapper, PyObject *v) { return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v); } #endif static long wrap_hash(PyObject *self) { return PyObject_Hash(Proxy_GET_OBJECT(self)); } static PyObject * wrap_call(PyObject *self, PyObject *args, PyObject *kw) { if (kw) return PyEval_CallObjectWithKeywords(Proxy_GET_OBJECT(self), args, kw); else return PyObject_CallObject(Proxy_GET_OBJECT(self), args); } /* * Number methods. */ static PyObject * call_int(PyObject *self) { PyNumberMethods *nb = self->ob_type->tp_as_number; if (nb == NULL || nb->nb_int == NULL) { PyErr_SetString(PyExc_TypeError, "object can't be converted to int"); return NULL; } return nb->nb_int(self); } #if PY_MAJOR_VERSION < 3 // Python 3 has no long, oct or hex methods. static PyObject * call_long(PyObject *self) { PyNumberMethods *nb = self->ob_type->tp_as_number; if (nb == NULL || nb->nb_long == NULL) { PyErr_SetString(PyExc_TypeError, "object can't be converted to long"); return NULL; } return nb->nb_long(self); } static PyObject * call_oct(PyObject *self) { PyNumberMethods *nb = self->ob_type->tp_as_number; if (nb == NULL || nb->nb_oct== NULL) { PyErr_SetString(PyExc_TypeError, "object can't be converted to oct"); return NULL; } return nb->nb_oct(self); } static PyObject * call_hex(PyObject *self) { PyNumberMethods *nb = self->ob_type->tp_as_number; if (nb == NULL || nb->nb_hex == NULL) { PyErr_SetString(PyExc_TypeError, "object can't be converted to hex"); return NULL; } return nb->nb_hex(self); } #endif static PyObject * call_float(PyObject *self) { PyNumberMethods *nb = self->ob_type->tp_as_number; if (nb == NULL || nb->nb_float== NULL) { PyErr_SetString(PyExc_TypeError, "object can't be converted to float"); return NULL; } return nb->nb_float(self); } static PyObject * call_ipow(PyObject *self, PyObject *other) { /* PyNumber_InPlacePower has three args. How silly. :-) */ return PyNumber_InPlacePower(self, other, Py_None); } typedef PyObject *(*function1)(PyObject *); static PyObject * check1(ProxyObject *self, char *opname, function1 operation) { PyObject *result = NULL; result = operation(Proxy_GET_OBJECT(self)); #if 0 if (result != NULL) /* ??? create proxy for result? */ ; #endif return result; } static PyObject * check2(PyObject *self, PyObject *other, char *opname, char *ropname, binaryfunc operation) { PyObject *result = NULL; PyObject *object; if (Proxy_Check(self)) { object = Proxy_GET_OBJECT(self); result = operation(object, other); } else if (Proxy_Check(other)) { object = Proxy_GET_OBJECT(other); result = operation(self, object); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } #if 0 if (result != NULL) /* ??? create proxy for result? */ ; #endif return result; } static PyObject * check2i(ProxyObject *self, PyObject *other, char *opname, binaryfunc operation) { PyObject *result = NULL; PyObject *object = Proxy_GET_OBJECT(self); result = operation(object, other); if (result == object) { /* If the operation was really carried out inplace, don't create a new proxy, but use the old one. */ Py_INCREF(self); Py_DECREF(object); result = (PyObject *)self; } #if 0 else if (result != NULL) /* ??? create proxy for result? */ ; #endif return result; } #define UNOP(NAME, CALL) \ static PyObject *wrap_##NAME(PyObject *self) \ { return check1((ProxyObject *)self, "__"#NAME"__", CALL); } #define BINOP(NAME, CALL) \ static PyObject *wrap_##NAME(PyObject *self, PyObject *other) \ { return check2(self, other, "__"#NAME"__", "__r"#NAME"__", CALL); } #define INPLACE(NAME, CALL) \ static PyObject *wrap_i##NAME(PyObject *self, PyObject *other) \ { return check2i((ProxyObject *)self, other, "__i"#NAME"__", CALL); } BINOP(add, PyNumber_Add) BINOP(sub, PyNumber_Subtract) BINOP(mul, PyNumber_Multiply) #if PY_MAJOR_VERSION < 3 // Python 3 doesn't support the old integer division BINOP(div, PyNumber_Divide) #endif BINOP(mod, PyNumber_Remainder) BINOP(divmod, PyNumber_Divmod) static PyObject * wrap_pow(PyObject *self, PyObject *other, PyObject *modulus) { PyObject *result = NULL; PyObject *object; if (Proxy_Check(self)) { object = Proxy_GET_OBJECT(self); result = PyNumber_Power(object, other, modulus); } else if (Proxy_Check(other)) { object = Proxy_GET_OBJECT(other); result = PyNumber_Power(self, object, modulus); } else if (modulus != NULL && Proxy_Check(modulus)) { object = Proxy_GET_OBJECT(modulus); result = PyNumber_Power(self, other, modulus); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return result; } BINOP(lshift, PyNumber_Lshift) BINOP(rshift, PyNumber_Rshift) BINOP(and, PyNumber_And) BINOP(xor, PyNumber_Xor) BINOP(or, PyNumber_Or) #if PY_MAJOR_VERSION < 3 // Coercion is gone in Python 3 static int wrap_coerce(PyObject **p_self, PyObject **p_other) { PyObject *self = *p_self; PyObject *other = *p_other; PyObject *object; PyObject *left; PyObject *right; int r; assert(Proxy_Check(self)); object = Proxy_GET_OBJECT(self); left = object; right = other; r = PyNumber_CoerceEx(&left, &right); if (r != 0) return r; /* Now left and right have been INCREF'ed. Any new value that comes out is proxied; any unchanged value is left unchanged. */ if (left == object) { /* Keep the old proxy */ Py_INCREF(self); Py_DECREF(left); left = self; } #if 0 else { /* ??? create proxy for left? */ } if (right != other) { /* ??? create proxy for right? */ } #endif *p_self = left; *p_other = right; return 0; } #endif UNOP(neg, PyNumber_Negative) UNOP(pos, PyNumber_Positive) UNOP(abs, PyNumber_Absolute) UNOP(invert, PyNumber_Invert) UNOP(int, call_int) UNOP(float, call_float) #if PY_MAJOR_VERSION < 3 // Python 3 has no long, oct or hex methods UNOP(long, call_long) UNOP(oct, call_oct) UNOP(hex, call_hex) #endif INPLACE(add, PyNumber_InPlaceAdd) INPLACE(sub, PyNumber_InPlaceSubtract) INPLACE(mul, PyNumber_InPlaceMultiply) #if PY_MAJOR_VERSION < 3 // The old integer division operator is gone in Python 3 INPLACE(div, PyNumber_InPlaceDivide) #endif INPLACE(mod, PyNumber_InPlaceRemainder) INPLACE(pow, call_ipow) INPLACE(lshift, PyNumber_InPlaceLshift) INPLACE(rshift, PyNumber_InPlaceRshift) INPLACE(and, PyNumber_InPlaceAnd) INPLACE(xor, PyNumber_InPlaceXor) INPLACE(or, PyNumber_InPlaceOr) BINOP(floordiv, PyNumber_FloorDivide) BINOP(truediv, PyNumber_TrueDivide) INPLACE(floordiv, PyNumber_InPlaceFloorDivide) INPLACE(truediv, PyNumber_InPlaceTrueDivide) static int wrap_nonzero(PyObject *self) { return PyObject_IsTrue(Proxy_GET_OBJECT(self)); } /* * Sequence methods */ static Py_ssize_t wrap_length(PyObject *self) { return PyObject_Length(Proxy_GET_OBJECT(self)); } static PyObject * wrap_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end) { PyObject *obj = Proxy_GET_OBJECT(self); if (PyList_Check(obj)) { return PyList_GetSlice(obj, start, end); } else if (PyTuple_Check(obj)) { return PyTuple_GetSlice(obj, start, end); } else { return PySequence_GetSlice(obj, start, end); } } static int wrap_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) { PyObject *obj = Proxy_GET_OBJECT(self); if (PyList_Check(obj)) { return PyList_SetSlice(obj, i, j, value); } else { return PySequence_SetSlice(obj, i, j, value); } } static int wrap_contains(PyObject *self, PyObject *value) { return PySequence_Contains(Proxy_GET_OBJECT(self), value); } /* * Mapping methods */ static PyObject * wrap_getitem(PyObject *wrapper, PyObject *v) { return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v); } static int wrap_setitem(PyObject *self, PyObject *key, PyObject *value) { if (value == NULL) return PyObject_DelItem(Proxy_GET_OBJECT(self), key); else return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value); } /* * Normal methods */ static char reduce__doc__[] = "__reduce__()\n" "Raise an exception; this prevents proxies from being picklable by\n" "default, even if the underlying object is picklable."; static PyObject * wrap_reduce(PyObject *self) { PyObject *pickle_error = NULL; PyObject *pickle = PyImport_ImportModule("pickle"); if (pickle == NULL) PyErr_Clear(); else { pickle_error = PyObject_GetAttrString(pickle, "PicklingError"); if (pickle_error == NULL) PyErr_Clear(); } if (pickle_error == NULL) { pickle_error = PyExc_RuntimeError; Py_INCREF(pickle_error); } PyErr_SetString(pickle_error, "proxy instances cannot be pickled"); Py_DECREF(pickle_error); return NULL; } static PyNumberMethods wrap_as_number = { wrap_add, /* nb_add */ wrap_sub, /* nb_subtract */ wrap_mul, /* nb_multiply */ #if PY_MAJOR_VERSION < 3 wrap_div, /* nb_divide */ #endif wrap_mod, /* nb_remainder */ wrap_divmod, /* nb_divmod */ wrap_pow, /* nb_power */ wrap_neg, /* nb_negative */ wrap_pos, /* nb_positive */ wrap_abs, /* nb_absolute */ wrap_nonzero, /* nb_nonzero */ wrap_invert, /* nb_invert */ wrap_lshift, /* nb_lshift */ wrap_rshift, /* nb_rshift */ wrap_and, /* nb_and */ wrap_xor, /* nb_xor */ wrap_or, /* nb_or */ #if PY_MAJOR_VERSION < 3 wrap_coerce, /* nb_coerce */ #endif wrap_int, /* nb_int */ #if PY_MAJOR_VERSION < 3 wrap_long, /* nb_long */ #else 0, /* The slot formerly known as nb_long */ #endif wrap_float, /* nb_float */ #if PY_MAJOR_VERSION < 3 wrap_oct, /* nb_oct */ wrap_hex, /* nb_hex */ #endif /* Added in release 2.0 */ /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ wrap_iadd, /* nb_inplace_add */ wrap_isub, /* nb_inplace_subtract */ wrap_imul, /* nb_inplace_multiply */ #if PY_MAJOR_VERSION < 3 wrap_idiv, /* nb_inplace_divide */ #endif wrap_imod, /* nb_inplace_remainder */ (ternaryfunc)wrap_ipow, /* nb_inplace_power */ wrap_ilshift, /* nb_inplace_lshift */ wrap_irshift, /* nb_inplace_rshift */ wrap_iand, /* nb_inplace_and */ wrap_ixor, /* nb_inplace_xor */ wrap_ior, /* nb_inplace_or */ /* Added in release 2.2 */ /* These require the Py_TPFLAGS_HAVE_CLASS flag */ wrap_floordiv, /* nb_floor_divide */ wrap_truediv, /* nb_true_divide */ wrap_ifloordiv, /* nb_inplace_floor_divide */ wrap_itruediv, /* nb_inplace_true_divide */ }; static PySequenceMethods wrap_as_sequence = { wrap_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ wrap_slice, /* sq_slice */ 0, /* sq_ass_item */ wrap_ass_slice, /* sq_ass_slice */ wrap_contains, /* sq_contains */ }; static PyMappingMethods wrap_as_mapping = { wrap_length, /* mp_length */ wrap_getitem, /* mp_subscript */ wrap_setitem, /* mp_ass_subscript */ }; static PyMethodDef wrap_methods[] = { {"__reduce__", (PyCFunction)wrap_reduce, METH_NOARGS, reduce__doc__}, {NULL, NULL}, }; /* * Note that the numeric methods are not supported. This is primarily * because of the way coercion-less operations are performed with * new-style numbers; since we can't tell which side of the operation * is 'self', we can't ensure we'd unwrap the right thing to perform * the actual operation. We also can't afford to just unwrap both * sides the way weakrefs do, since we don't know what semantics will * be associated with the wrapper itself. */ // Python < 2.6 support: #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #endif static PyTypeObject ProxyType = { PyVarObject_HEAD_INIT(NULL, 0) "zope.proxy.ProxyBase", sizeof(ProxyObject), 0, wrap_dealloc, /* tp_dealloc */ wrap_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ #if PY_MAJOR_VERSION < 3 wrap_compare, /* tp_compare */ #else 0, /* tp_reserved */ #endif wrap_repr, /* tp_repr */ &wrap_as_number, /* tp_as_number */ &wrap_as_sequence, /* tp_as_sequence */ &wrap_as_mapping, /* tp_as_mapping */ wrap_hash, /* tp_hash */ wrap_call, /* tp_call */ wrap_str, /* tp_str */ wrap_getattro, /* tp_getattro */ wrap_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ #if PY_MAJOR_VERSION < 3 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ #else // Py_TPFLAGS_CHECKTYPES is always true in Python 3 and removed. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ #endif 0, /* tp_doc */ wrap_traverse, /* tp_traverse */ wrap_clear, /* tp_clear */ wrap_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ wrap_iter, /* tp_iter */ wrap_iternext, /* tp_iternext */ wrap_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ wrap_init, /* tp_init */ 0, /* tp_alloc */ wrap_new, /* tp_new */ 0, /*PyObject_GC_Del,*/ /* tp_free */ }; static PyObject * create_proxy(PyObject *object) { PyObject *result = NULL; PyObject *args; args = PyTuple_New(1); if (args != NULL) { Py_INCREF(object); PyTuple_SET_ITEM(args, 0, object); result = PyObject_CallObject((PyObject *)&ProxyType, args); Py_DECREF(args); } return result; } static int api_check(PyObject *obj) { return obj ? Proxy_Check(obj) : 0; } static PyObject * api_create(PyObject *object) { if (object == NULL) { PyErr_SetString(PyExc_ValueError, "cannot create proxy around NULL"); return NULL; } return create_proxy(object); } static PyObject * api_getobject(PyObject *proxy) { if (proxy == NULL) { PyErr_SetString(PyExc_RuntimeError, "cannot pass NULL to ProxyAPI.getobject()"); return NULL; } if (Proxy_Check(proxy)) return Proxy_GET_OBJECT(proxy); else { PyErr_Format(PyExc_TypeError, "expected proxy object, got %s", proxy->ob_type->tp_name); return NULL; } } static ProxyInterface wrapper_capi = { &ProxyType, api_check, api_create, api_getobject, }; static PyObject *api_object = NULL; static char getobject__doc__[] = "getProxiedObject(proxy) --> object\n" "\n" "Get the underlying object for proxy, or the object itself, if it is\n" "not a proxy."; static PyObject * wrapper_getobject(PyObject *unused, PyObject *obj) { if (Proxy_Check(obj)) obj = Proxy_GET_OBJECT(obj); if (obj == NULL) obj = Py_None; Py_INCREF(obj); return obj; } static char setobject__doc__[] = "setProxiedObject(proxy, object) --> object\n" "\n" "Set the underlying object for proxy, returning the old proxied object.\n" "Raises TypeError if proxy is not a proxy.\n"; static PyObject * wrapper_setobject(PyObject *unused, PyObject *args) { PyObject *proxy; PyObject *object; PyObject *result = NULL; if (PyArg_ParseTuple(args, "O!O:setProxiedObject", &ProxyType, &proxy, &object)) { result = Proxy_GET_OBJECT(proxy); Py_INCREF(object); ((ProxyObject *) proxy)->proxy_object = object; } return result; } static char isProxy__doc__[] = "Check whether the given object is a proxy\n" "\n" "If proxytype is not None, checkes whether the object is\n" "proxied by the given proxytype.\n" ; static PyObject * wrapper_isProxy(PyObject *unused, PyObject *args) { PyObject *obj, *result; PyTypeObject *proxytype=&ProxyType; if (! PyArg_ParseTuple(args, "O|O!:isProxy", &obj, &PyType_Type, &proxytype) ) return NULL; while (obj && Proxy_Check(obj)) { if (PyObject_TypeCheck(obj, proxytype)) { result = Py_True; Py_INCREF(result); return result; } obj = Proxy_GET_OBJECT(obj); } result = Py_False; Py_INCREF(result); return result; } static char removeAllProxies__doc__[] = "removeAllProxies(proxy) --> object\n" "\n" "Get the proxied object with no proxies\n" "\n" "If obj is not a proxied object, return obj.\n" "\n" "The returned object has no proxies.\n" ; static PyObject * wrapper_removeAllProxies(PyObject *unused, PyObject *obj) { while (obj && Proxy_Check(obj)) obj = Proxy_GET_OBJECT(obj); if (obj == NULL) obj = Py_None; Py_INCREF(obj); return obj; } static char sameProxiedObjects__doc__[] = "Check whether two objects are the same or proxies of the same object"; static PyObject * wrapper_sameProxiedObjects(PyObject *unused, PyObject *args) { PyObject *ob1, *ob2; if (! PyArg_ParseTuple(args, "OO:sameProxiedObjects", &ob1, &ob2)) return NULL; while (ob1 && Proxy_Check(ob1)) ob1 = Proxy_GET_OBJECT(ob1); while (ob2 && Proxy_Check(ob2)) ob2 = Proxy_GET_OBJECT(ob2); if (ob1 == ob2) ob1 = Py_True; else ob1 = Py_False; Py_INCREF(ob1); return ob1; } static char queryProxy__doc__[] = "Look for a proxy of the given type around the object\n" "\n" "If no such proxy can be found, return the default.\n" ; static PyObject * wrapper_queryProxy(PyObject *unused, PyObject *args) { PyObject *obj, *result=Py_None; PyTypeObject *proxytype=&ProxyType; if (! PyArg_ParseTuple(args, "O|O!O:queryProxy", &obj, &PyType_Type, &proxytype, &result) ) return NULL; while (obj && Proxy_Check(obj)) { if (PyObject_TypeCheck(obj, proxytype)) { Py_INCREF(obj); return obj; } obj = Proxy_GET_OBJECT(obj); } Py_INCREF(result); return result; } static char queryInnerProxy__doc__[] = "Look for the inner-most proxy of the given type around the object\n" "\n" "If no such proxy can be found, return the default.\n" "\n" "If there is such a proxy, return the inner-most one.\n" ; static PyObject * wrapper_queryInnerProxy(PyObject *unused, PyObject *args) { PyObject *obj, *result=Py_None; PyTypeObject *proxytype=&ProxyType; if (! PyArg_ParseTuple(args, "O|O!O:queryInnerProxy", &obj, &PyType_Type, &proxytype, &result) ) return NULL; while (obj && Proxy_Check(obj)) { if (PyObject_TypeCheck(obj, proxytype)) result = obj; obj = Proxy_GET_OBJECT(obj); } Py_INCREF(result); return result; } /* Module initialization */ static char module___doc__[] = "Association between an object, a context object, and a dictionary.\n\ \n\ The context object and dictionary give additional context information\n\ associated with a reference to the basic object. The wrapper objects\n\ act as proxies for the original object."; static PyMethodDef module_functions[] = { {"getProxiedObject", wrapper_getobject, METH_O, getobject__doc__}, {"setProxiedObject", wrapper_setobject, METH_VARARGS, setobject__doc__}, {"isProxy", wrapper_isProxy, METH_VARARGS, isProxy__doc__}, {"sameProxiedObjects", wrapper_sameProxiedObjects, METH_VARARGS, sameProxiedObjects__doc__}, {"queryProxy", wrapper_queryProxy, METH_VARARGS, queryProxy__doc__}, {"queryInnerProxy", wrapper_queryInnerProxy, METH_VARARGS, queryInnerProxy__doc__}, {"removeAllProxies", wrapper_removeAllProxies, METH_O, removeAllProxies__doc__}, {NULL} }; MOD_INIT(_zope_proxy_proxy) { PyObject *m; MOD_DEF(m, "_zope_proxy_proxy", module___doc__, module_functions) if (m == NULL) return MOD_ERROR_VAL; if (empty_tuple == NULL) empty_tuple = PyTuple_New(0); ProxyType.tp_free = PyObject_GC_Del; if (PyType_Ready(&ProxyType) < 0) return MOD_ERROR_VAL; Py_INCREF(&ProxyType); PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType); if (api_object == NULL) { api_object = PyCapsule_New(&wrapper_capi, NULL, NULL); if (api_object == NULL) return MOD_ERROR_VAL; } Py_INCREF(api_object); PyModule_AddObject(m, "_CAPI", api_object); return MOD_SUCCESS_VAL(m); } zope.security-3.8.3/include/zope.proxy/decorator.py0000644000076600000240000000636511637266452022376 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Decorator support Decorators are proxies that are mostly transparent but that may provide additional features. """ __docformat__ = "reStructuredText" from zope.proxy import getProxiedObject, ProxyBase from zope.interface.declarations import ObjectSpecificationDescriptor from zope.interface.declarations import getObjectSpecification from zope.interface.declarations import ObjectSpecification from zope.interface import providedBy class DecoratorSpecificationDescriptor(ObjectSpecificationDescriptor): """Support for interface declarations on decorators >>> from zope.interface import * >>> class I1(Interface): ... pass >>> class I2(Interface): ... pass >>> class I3(Interface): ... pass >>> class I4(Interface): ... pass >>> class D1(SpecificationDecoratorBase): ... implements(I1) >>> class D2(SpecificationDecoratorBase): ... implements(I2) >>> class X(object): ... implements(I3) >>> x = X() >>> directlyProvides(x, I4) Interfaces of X are ordered with the directly-provided interfaces first >>> [interface.getName() for interface in list(providedBy(x))] ['I4', 'I3'] When we decorate objects, what order should the interfaces come in? One could argue that decorators are less specific, so they should come last. >>> [interface.getName() for interface in list(providedBy(D1(x)))] ['I4', 'I3', 'I1'] >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))] ['I4', 'I3', 'I1', 'I2'] SpecificationDecorators also work with old-style classes: >>> class X: ... implements(I3) >>> x = X() >>> directlyProvides(x, I4) >>> [interface.getName() for interface in list(providedBy(x))] ['I4', 'I3'] >>> [interface.getName() for interface in list(providedBy(D1(x)))] ['I4', 'I3', 'I1'] >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))] ['I4', 'I3', 'I1', 'I2'] """ def __get__(self, inst, cls=None): if inst is None: return getObjectSpecification(cls) else: provided = providedBy(getProxiedObject(inst)) # Use type rather than __class__ because inst is a proxy and # will return the proxied object's class. cls = type(inst) return ObjectSpecification(provided, cls) def __set__(self, inst, value): raise TypeError("Can't set __providedBy__ on a decorated object") class SpecificationDecoratorBase(ProxyBase): """Base class for a proxy that provides additional interfaces.""" __providedBy__ = DecoratorSpecificationDescriptor() zope.security-3.8.3/include/zope.proxy/interfaces.py0000644000076600000240000000414311637266452022527 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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 # ############################################################################## """Proxy-related interfaces. """ from zope.interface import Interface class IProxyIntrospection(Interface): """Provides methods for indentifying proxies and extracting proxied objects """ def isProxy(obj, proxytype=None): """Check whether the given object is a proxy If proxytype is not None, checkes whether the object is proxied by the given proxytype. """ def sameProxiedObjects(ob1, ob2): """Check whether ob1 and ob2 are the same or proxies of the same object """ def getProxiedObject(obj): """Get the proxied Object If the object isn't proxied, then just return the object. """ def setProxiedObject(ob1, ob2): """Set the underlying object for ob1 to ob2, returning the old object. Raises TypeError if ob1 is not a proxy. """ def removeAllProxies(obj): """Get the proxied object with no proxies If obj is not a proxied object, return obj. The returned object has no proxies. """ def queryProxy(obj, proxytype, default=None): """Look for a proxy of the given type around the object If no such proxy can be found, return the default. """ def queryInnerProxy(obj, proxytype, default=None): """Look for the inner-most proxy of the given type around the object If no such proxy can be found, return the default. If there is such a proxy, return the inner-most one. """ zope.security-3.8.3/include/zope.proxy/proxy.h0000644000076600000240000000361111637266452021363 0ustar macstaff00000000000000#ifndef _proxy_H_ #define _proxy_H_ 1 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN typedef Py_ssize_t (*lenfunc)(PyObject *); typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); #endif typedef struct { PyObject_HEAD PyObject *proxy_object; } ProxyObject; #define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) typedef struct { PyTypeObject *proxytype; int (*check)(PyObject *obj); PyObject *(*create)(PyObject *obj); PyObject *(*getobject)(PyObject *proxy); } ProxyInterface; #ifndef PROXY_MODULE /* These are only defined in the public interface, and are not * available within the module implementation. There we use the * classic Python/C API only. */ static ProxyInterface *_proxy_api = NULL; static int Proxy_Import(void) { if (_proxy_api == NULL) { PyObject *m = PyImport_ImportModule("zope.proxy"); if (m != NULL) { PyObject *tmp = PyObject_GetAttrString(m, "_CAPI"); if (tmp != NULL) { if (PyCObject_Check(tmp)) _proxy_api = (ProxyInterface *) PyCObject_AsVoidPtr(tmp); Py_DECREF(tmp); } } } return (_proxy_api == NULL) ? -1 : 0; } #define ProxyType (*_proxy_api->proxytype) #define Proxy_Check(obj) (_proxy_api->check((obj))) #define Proxy_CheckExact(obj) ((obj)->ob_type == ProxyType) #define Proxy_New(obj) (_proxy_api->create((obj))) #define Proxy_GetObject(proxy) (_proxy_api->getobject((proxy))) #endif /* PROXY_MODULE */ #endif /* _proxy_H_ */ zope.security-3.8.3/include/zope.proxy/tests/0000755000076600000240000000000011637266524021172 5ustar macstaff00000000000000zope.security-3.8.3/include/zope.proxy/tests/__init__.py0000644000076600000240000000007511637266452023305 0ustar macstaff00000000000000# # This file is necessary to make this directory a package. zope.security-3.8.3/include/zope.proxy/tests/test_decorator.py0000644000076600000240000000145111637266452024566 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test Harness """ from doctest import DocTestSuite def test_suite(): suite = DocTestSuite() suite.addTest(DocTestSuite('zope.proxy.decorator')) return suite zope.security-3.8.3/include/zope.proxy/tests/test_proxy.py0000644000076600000240000004615011637266452023772 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test base proxy class. """ from doctest import DocTestSuite import pickle import sys import unittest from zope.proxy import ProxyBase import zope.proxy class Thing: """This class is expected to be a classic class.""" class Comparable(object): def __init__(self, value): self.value = value def __eq__(self, other): if hasattr(other, "value"): other = other.value return self.value == other def __ne__(self, other): return not self.__eq__(other) def __lt__(self, other): if hasattr(other, "value"): other = other.value return self.value < other def __ge__(self, other): return not self.__lt__(other) def __le__(self, other): if hasattr(other, "value"): other = other.value return self.value <= other def __gt__(self, other): return not self.__le__(other) def __repr__(self): return "" % self.value class ProxyTestCase(unittest.TestCase): proxy_class = ProxyBase def setUp(self): self.x = Thing() self.p = self.new_proxy(self.x) def new_proxy(self, o): return self.proxy_class(o) def test_constructor(self): o = object() self.assertRaises(TypeError, self.proxy_class, o, o) self.assertRaises(TypeError, self.proxy_class, o, key='value') self.assertRaises(TypeError, self.proxy_class, key='value') def test_subclass_constructor(self): class MyProxy(self.proxy_class): def __new__(cls, *args, **kwds): return super(MyProxy, cls).__new__(cls, *args, **kwds) def __init__(self, *args, **kwds): super(MyProxy, self).__init__(*args, **kwds) o1 = object() o2 = object() o = MyProxy((o1, o2)) self.assertEquals(o1, o[0]) self.assertEquals(o2, o[1]) self.assertRaises(TypeError, MyProxy, o1, o2) self.assertRaises(TypeError, MyProxy, o1, key='value') self.assertRaises(TypeError, MyProxy, key='value') # Check that are passed to __init__() overrides what's passed # to __new__(). class MyProxy2(self.proxy_class): def __new__(cls, *args, **kwds): return super(MyProxy2, cls).__new__(cls, 'value') p = MyProxy2('splat!') self.assertEquals(list(p), list('splat!')) class MyProxy3(MyProxy2): def __init__(self, arg): if list(self) != list('value'): raise AssertionError("list(self) != list('value')") super(MyProxy3, self).__init__('another') p = MyProxy3('notused') self.assertEquals(list(p), list('another')) def test_proxy_attributes(self): o = Thing() o.foo = 1 w = self.new_proxy(o) self.assert_(w.foo == 1) def test___class__(self): o = object() w = self.new_proxy(o) self.assert_(w.__class__ is o.__class__) def test_pickle_prevention(self): # Proxies of old-style classes can't be pickled. if sys.version > '3': # No old-style classes in Python 3. return w = self.new_proxy(Thing()) self.assertRaises(pickle.PicklingError, pickle.dumps, w) def test_proxy_equality(self): w = self.new_proxy('foo') self.assertEquals(w, 'foo') o1 = Comparable(1) o2 = Comparable(1.0) o3 = Comparable("splat!") w1 = self.new_proxy(o1) w2 = self.new_proxy(o2) w3 = self.new_proxy(o3) self.assertEquals(o1, w1) self.assertEquals(o1, w2) self.assertEquals(o2, w1) self.assertEquals(w1, o2) self.assertEquals(w2, o1) self.assertNotEquals(o3, w1) self.assertNotEquals(w1, o3) self.assertNotEquals(w3, o1) self.assertNotEquals(o1, w3) def test_proxy_ordering_lt(self): o1 = Comparable(1) o2 = Comparable(2.0) w1 = self.new_proxy(o1) w2 = self.new_proxy(o2) self.assert_(w1 < w2) self.assert_(w1 <= w2) self.assert_(o1 < w2) self.assert_(o1 <= w2) self.assert_(w1 < o2) self.assert_(w2 <= o2) def test_proxy_callable(self): if sys.version < '3': # Gone in Python 3: w = self.new_proxy({}.get) self.assert_(callable(w)) def test_proxy_item_protocol(self): w = self.new_proxy({}) self.assertRaises(KeyError, lambda: w[1]) w[1] = 'a' self.assertEquals(w[1], 'a') del w[1] self.assertRaises(KeyError, lambda: w[1]) def del_w_1(): del w[1] self.assertRaises(KeyError, del_w_1) def test_wrapped_iterable(self): a = [1, 2, 3] b = [] for x in self.new_proxy(a): b.append(x) self.assertEquals(a, b) def test_iteration_over_proxy(self): # Wrap an iterator before starting iteration. # PyObject_GetIter() will still be called on the proxy. a = [1, 2, 3] b = [] for x in self.new_proxy(iter(a)): b.append(x) self.assertEquals(a, b) t = tuple(self.new_proxy(iter(a))) self.assertEquals(t, (1, 2, 3)) def test_iteration_using_proxy(self): # Wrap an iterator within the iteration protocol, expecting it # still to work. PyObject_GetIter() will not be called on the # proxy, so the tp_iter slot won't unwrap it. class Iterable(object): def __init__(self, test, data): self.test = test self.data = data def __iter__(self): return self.test.new_proxy(iter(self.data)) a = [1, 2, 3] b = [] for x in Iterable(self, a): b.append(x) self.assertEquals(a, b) def test_bool_wrapped_None(self): w = self.new_proxy(None) self.assertEquals(not w, 1) # Numeric ops. unops = [ "-x", "+x", "abs(x)", "~x", "int(x)", "float(x)", ] if sys.version < '3': # long is gone in Python 3 unops.append("long(x)") def test_unops(self): P = self.new_proxy for expr in self.unops: x = 1 y = eval(expr) x = P(1) z = eval(expr) self.assertEqual(z, y, "x=%r; expr=%r" % (x, expr)) def test_odd_unops(self): # unops that don't return a proxy P = self.new_proxy funcs = (lambda x: not x,) if sys.version < '3': funcs += (oct, hex) for func in funcs: self.assertEqual(func(P(100)), func(100)) binops = [ "x+y", "x-y", "x*y", "x/y", "divmod(x, y)", "x**y", "x//y", "x<>y", "x&y", "x|y", "x^y", ] def test_binops(self): P = self.new_proxy for expr in self.binops: first = 1 for x in [1, P(1)]: for y in [2, P(2)]: if first: z = eval(expr) first = 0 else: self.assertEqual(eval(expr), z, "x=%r; y=%r; expr=%r" % (x, y, expr)) def test_inplace(self): # TODO: should test all inplace operators... P = self.new_proxy pa = P(1) pa += 2 self.assertEqual(pa, 3) a = [1, 2, 3] pa = qa = P(a) pa += [4, 5, 6] self.failUnless(pa is qa) self.assertEqual(a, [1, 2, 3, 4, 5, 6]) pa = P(2) pa **= 2 self.assertEqual(pa, 4) def test_coerce(self): if sys.version > '3': # No coercion in Python 3 return P = self.new_proxy # Before 2.3, coerce() of two proxies returns them unchanged fixed_coerce = sys.version_info >= (2, 3, 0) x = P(1) y = P(2) a, b = coerce(x, y) self.failUnless(a is x and b is y) x = P(1) y = P(2.1) a, b = coerce(x, y) self.failUnless(a == 1.0) self.failUnless(b is y) if fixed_coerce: self.failUnless(a.__class__ is float, a.__class__) x = P(1.1) y = P(2) a, b = coerce(x, y) self.failUnless(a is x) self.failUnless(b == 2.0) if fixed_coerce: self.failUnless(b.__class__ is float, b.__class__) x = P(1) y = 2 a, b = coerce(x, y) self.failUnless(a is x) self.failUnless(b is y) x = P(1) y = 2.1 a, b = coerce(x, y) self.failUnless(a.__class__ is float, a.__class__) self.failUnless(b is y) x = P(1.1) y = 2 a, b = coerce(x, y) self.failUnless(a is x) self.failUnless(b.__class__ is float, b.__class__) x = 1 y = P(2) a, b = coerce(x, y) self.failUnless(a is x) self.failUnless(b is y) x = 1.1 y = P(2) a, b = coerce(x, y) self.failUnless(a is x) self.failUnless(b.__class__ is float, b.__class__) x = 1 y = P(2.1) a, b = coerce(x, y) self.failUnless(a.__class__ is float, a.__class__) self.failUnless(b is y) def test_getslice(self): # These tests are moot under Python 3 as __slice__ isn't supported. if sys.version > '3': return # Lists have special slicing behavior. pList = self.new_proxy([1, 2]) self.assertEqual(pList[-1:], [2]) self.assertEqual(pList[-2:], [1, 2]) self.assertEqual(pList[-3:], [1, 2]) # Tuples also have special slicing behavior. pTuple = self.new_proxy((1, 2)) self.assertEqual(pTuple[-1:], (2,)) self.assertEqual(pTuple[-2:], (1, 2)) self.assertEqual(pTuple[-3:], (1, 2)) # This behavior should be true for all list- and tuple-derived classes. class DerivedList(list): def __getslice__(self, start, end, step=None): return (start, end, step) pList = self.new_proxy(DerivedList([1, 2])) self.assertEqual(pList[-1:], [2]) self.assertEqual(pList[-2:], [1, 2]) self.assertEqual(pList[-3:], [1, 2]) # Another sort of sequence has a different slicing interpretation. class Slicer(object): def __len__(self): return 2 def __getslice__(self, start, end, step=None): return (start, end, step) pSlicer = self.new_proxy(Slicer()) self.assertEqual(pSlicer[-1:][0], 1) self.assertEqual(pSlicer[-2:][0], 0) # Note that for non-lists and non-tuples the slice is computed # differently self.assertEqual(pSlicer[-3:][0], 1) def test_setslice(self): # Lists have special slicing bahvior for assignment as well. pList = self.new_proxy([1, 2]) pList[-1:] = [3, 4] self.assertEqual(pList, [1, 3, 4]) pList = self.new_proxy([1, 2]) pList[-2:] = [3, 4] self.assertEqual(pList, [3, 4]) pList = self.new_proxy([1, 2]) pList[-3:] = [3, 4] self.assertEqual(pList, [3, 4]) # This behavior should be true for all list-derived classes. class DerivedList(list): pass pList = self.new_proxy(DerivedList([1, 2])) pList[-1:] = [3, 4] self.assertEqual(pList, [1, 3, 4]) pList = self.new_proxy(DerivedList([1, 2])) pList[-2:] = [3, 4] self.assertEqual(pList, [3, 4]) pList = self.new_proxy(DerivedList([1, 2])) pList[-3:] = [3, 4] self.assertEqual(pList, [3, 4]) def test_isProxy(): """ >>> from zope.proxy import ProxyBase, isProxy >>> class P1(ProxyBase): ... pass >>> class P2(ProxyBase): ... pass >>> class C(object): ... pass >>> c = C() >>> int(isProxy(c)) 0 >>> p = P1(c) >>> int(isProxy(p)) 1 >>> int(isProxy(p, P1)) 1 >>> int(isProxy(p, P2)) 0 >>> p = P2(p) >>> int(isProxy(p, P1)) 1 >>> int(isProxy(p, P2)) 1 """ def test_getProxiedObject(): """ >>> from zope.proxy import ProxyBase, getProxiedObject >>> class C(object): ... pass >>> c = C() >>> int(getProxiedObject(c) is c) 1 >>> p = ProxyBase(c) >>> int(getProxiedObject(p) is c) 1 >>> p2 = ProxyBase(p) >>> int(getProxiedObject(p2) is p) 1 """ def test_ProxyIterator(): """ >>> from zope.proxy import ProxyBase, ProxyIterator >>> class C(object): ... pass >>> c = C() >>> p1 = ProxyBase(c) >>> class P(ProxyBase): ... pass >>> p2 = P(p1) >>> p3 = ProxyBase(p2) >>> list(ProxyIterator(p3)) == [p3, p2, p1, c] 1 """ def test_removeAllProxies(): """ >>> from zope.proxy import ProxyBase, removeAllProxies >>> class C(object): ... pass >>> c = C() >>> int(removeAllProxies(c) is c) 1 >>> p = ProxyBase(c) >>> int(removeAllProxies(p) is c) 1 >>> p2 = ProxyBase(p) >>> int(removeAllProxies(p2) is c) 1 """ def test_queryProxy(): """ >>> from zope.proxy import ProxyBase, queryProxy >>> class P1(ProxyBase): ... pass >>> class P2(ProxyBase): ... pass >>> class C(object): ... pass >>> c = C() >>> queryProxy(c, P1) >>> queryProxy(c, P1, 42) 42 >>> p1 = P1(c) >>> int(queryProxy(p1, P1) is p1) 1 >>> queryProxy(c, P2) >>> queryProxy(c, P2, 42) 42 >>> p2 = P2(p1) >>> int(queryProxy(p2, P1) is p1) 1 >>> int(queryProxy(p2, P2) is p2) 1 >>> int(queryProxy(p2, ProxyBase) is p2) 1 """ def test_queryInnerProxy(): """ >>> from zope.proxy import ProxyBase, queryProxy, queryInnerProxy >>> class P1(ProxyBase): ... pass >>> class P2(ProxyBase): ... pass >>> class C(object): ... pass >>> c = C() >>> queryInnerProxy(c, P1) >>> queryInnerProxy(c, P1, 42) 42 >>> p1 = P1(c) >>> int(queryProxy(p1, P1) is p1) 1 >>> queryInnerProxy(c, P2) >>> queryInnerProxy(c, P2, 42) 42 >>> p2 = P2(p1) >>> int(queryInnerProxy(p2, P1) is p1) 1 >>> int(queryInnerProxy(p2, P2) is p2) 1 >>> int(queryInnerProxy(p2, ProxyBase) is p1) 1 >>> p3 = P1(p2) >>> int(queryProxy(p3, P1) is p3) 1 >>> int(queryInnerProxy(p3, P1) is p1) 1 >>> int(queryInnerProxy(p3, P2) is p2) 1 """ def test_sameProxiedObjects(): """ >>> from zope.proxy import ProxyBase, sameProxiedObjects >>> class C(object): ... pass >>> c1 = C() >>> c2 = C() >>> int(sameProxiedObjects(c1, c1)) 1 >>> int(sameProxiedObjects(ProxyBase(c1), c1)) 1 >>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c1))) 1 >>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c1)) 1 >>> int(sameProxiedObjects(c1, ProxyBase(c1))) 1 >>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c1)))) 1 >>> int(sameProxiedObjects(c1, c2)) 0 >>> int(sameProxiedObjects(ProxyBase(c1), c2)) 0 >>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c2))) 0 >>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c2)) 0 >>> int(sameProxiedObjects(c1, ProxyBase(c2))) 0 >>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c2)))) 0 """ def test_subclassing_proxies(): """You can subclass ProxyBase If you subclass a proxy, instances of the subclass have access to data defined in the class, including descriptors. Your subclass instances don't get instance dictionaries, but they can have slots. >>> class MyProxy(ProxyBase): ... __slots__ = 'x', 'y' ... ... def f(self): ... return self.x >>> l = [1, 2, 3] >>> p = MyProxy(l) I can use attributes defined by the class, including slots: >>> p.x = 'x' >>> p.x 'x' >>> p.f() 'x' I can also use attributes of the proxied object: >>> p [1, 2, 3] >>> p.pop() 3 >>> p [1, 2] """ def test_get_descriptors_in_proxy_class(): """ A non-data descriptor in a proxy class doesn't hide an attribute on a proxied object or prevent writing the attribute. >>> class ReadDescr(object): ... def __get__(self, i, c): ... return 'read' >>> class MyProxy(ProxyBase): ... __slots__ = () ... ... z = ReadDescr() ... q = ReadDescr() >>> class MyOb: ... q = 1 >>> o = MyOb() >>> p = MyProxy(o) >>> p.q 1 >>> p.z 'read' >>> p.z = 1 >>> o.z, p.z (1, 1) """ def test_non_overridable(): """ Normally, methods defined in proxies are overridden by methods of proxied objects. This applies to all non-data descriptors. The non_overridable function can be used to convert a non-data descriptor to a data descriptor that disallows writes. This function can be used as a decorator to make functions defined in proxy classes take precedence over functions defined in proxied objects. >>> class MyProxy(ProxyBase): ... __slots__ = () ... ... @zope.proxy.non_overridable ... def foo(self): ... return 'MyProxy foo' >>> class MyOb: ... def foo(self): ... return 'MyOb foo' >>> o = MyOb() >>> p = MyProxy(o) >>> p.foo() 'MyProxy foo' """ def test_setProxiedObject(): """ >>> from zope.proxy import ProxyBase >>> from zope.proxy import setProxiedObject, getProxiedObject >>> class C(object): ... pass >>> c1 = C() >>> c2 = C() >>> p = ProxyBase(c1) `setProxiedObject()` allows us to change the object a proxy refers to, returning the previous referent: >>> old = setProxiedObject(p, c2) >>> old is c1 True >>> getProxiedObject(p) is c2 True The first argument to `setProxiedObject()` must be a proxy; other objects cause it to raise an exception: >>> try: ... setProxiedObject(c1, None) ... except TypeError: ... print "TypeError raised" ... else: ... print "Expected TypeError not raised" TypeError raised """ def test_suite(): suite = unittest.makeSuite(ProxyTestCase) suite.addTest(DocTestSuite()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner(sys.stdout) result = runner.run(test_suite()) newerrs = len(result.errors) + len(result.failures) sys.exit(newerrs and 1 or 0) zope.security-3.8.3/LICENSE.txt0000644000076600000240000000402611637266447016101 0ustar macstaff00000000000000Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.security-3.8.3/PKG-INFO0000644000076600000240000011162511637266524015353 0ustar macstaff00000000000000Metadata-Version: 1.0 Name: zope.security Version: 3.8.3 Summary: Zope Security Framework Home-page: http://pypi.python.org/pypi/zope.security Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: The Security framework provides a generic mechanism to implement security policies on Python objects. .. contents:: ============== Zope3 Security ============== Introduction ------------ The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions ----------- Principal ~~~~~~~~~ A generalization of a concept of a user. Permission ~~~~~~~~~~ A kind of access, i.e. permission to READ vs. permission to WRITE. Fundamentally the whole security framework is organized around checking permissions on objects. Purpose ------- The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and implicit security checks on attribute access for objects. Attribute names are mapped onto permission names when checking access and the implementation of the security check is defined by the security policy, which receives the object, the permission name, and an interaction. Interactions are objects that represent the use of the system by one or more principals. An interaction contains a list of participations, which represents the way a single principal participates in the interaction. An HTTP request is one example of a participation. Its important to keep in mind that the policy provided is just a default, and it can be substituted with one which doesn't care about principals or interactions at all. Framework Components -------------------- Low Level Components ~~~~~~~~~~~~~~~~~~~~ These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. Checkers ~~~~~~~~ A checker is associated with an object kind, and provides the hooks that map attribute checks onto permissions deferring to the security manager (which in turn defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. There are several implementation variants of checkers, such as checkers that grant access based on attribute names. Proxies ~~~~~~~ Wrappers around Python objects that implicitly guard access to their wrapped contents by delegating to their associated checker. Proxies are also viral in nature, in that values returned by proxies are also proxied. High Level Components --------------------- Security Management ~~~~~~~~~~~~~~~~~~~ Provides accessors for setting up interactions and the global security policy. Interaction ~~~~~~~~~~~ Stores transient information on the list of participations. Participation ~~~~~~~~~~~~~ Stores information about a principal participating in the interaction. Security Policy ~~~~~~~~~~~~~~~ Provides a single method that accepts the object, the permission, and the interaction of the access being checked and is used to implement the application logic for the security framework. Narrative (agent sandbox) ------------------------- As an example we take a look at constructing a multi-agent distributed system, and then adding a security layer using the Zope security model onto it. Scenario ~~~~~~~~ Our agent simulation consists of autonomous agents that live in various agent homes/sandboxes and perform actions that access services available at their current home. Agents carry around authentication tokens which signify their level of access within any given home. Additionally agents attempt to migrate from home to home randomly. The agent simulation was constructed separately from any security aspects. Now we want to define and integrate a security model into the simulation. The full code for the simulation and the security model is available separately; we present only relevant code snippets here for illustration as we go through the implementation process. For the agent simulation we want to add a security model such that we group agents into two authentication groups, "norse legends", including the principals thor, odin, and loki, and "greek men", including prometheus, archimedes, and thucydides. We associate permissions with access to services and homes. We differentiate the homes such that certain authentication groups only have access to services or the home itself based on the local settings of the home in which they reside. We define the homes/sandboxes - origin - all agents start here, and have access to all services here. - valhalla - only agents in the authentication group 'norse legend' can reside here. - jail - all agents can come here, but only 'norse legend's can leave or access services. Process ~~~~~~~ Loosely we define a process for implementing this security model - mapping permissions onto actions - mapping authentication tokens onto permissions - implementing checkers and security policies that use our authentication tokens and permissions. - binding checkers to our simulation classes - inserting the hooks into the original simulation code to add proxy wrappers to automatically check security. - inserting hooks into the original simulation to register the agents as the active principal in an interaction. Defining a Permission Model ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We define the following permissions:: NotAllowed = 'Not Allowed' Public = Checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' and create a dictionary database mapping homes to authentication groups which are linked to associated permissions. Defining and Binding Checkers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Checkers are the foundational unit for the security framework. They define what attributes can be accessed or set on a given instance. They can be used implicitly via Proxy objects, to guard all attribute access automatically or explicitly to check a given access for an operation. Checker construction expects two functions or dictionaries, one is used to map attribute names to permissions for attribute access and another to do the same for setting attributes. We use the following checker factory function:: def PermissionMapChecker(permissions_map={}, setattr_permission_func=NoSetAttr): res = {} for k,v in permissions_map.items(): for iv in v: res[iv]=k return checker.Checker(res.get, setattr_permission_func) time_service_checker = PermissionMapChecker( # permission : [methods] {'AccessTimeService':['getTime']} ) with the NoSetAttr function defined as a lambda which always return the permission `NotAllowed`. To bind the checkers to the simulation classes we register our checkers with the security model's global checker registry:: import sandbox_simulation from zope.security.checker import defineChecker defineChecker(sandbox_simulation.TimeService, time_service_checker) Defining a Security Policy ~~~~~~~~~~~~~~~~~~~~~~~~~~ We implement our security policy such that it checks the current agent's authentication token against the given permission in the home of the object being accessed:: class SimulationSecurityPolicy: implements(ISecurityPolicy) createInteraction = staticmethod(simpleinteraction.createInteraction) def checkPermission(self, permission, object, interaction): home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if interaction is None: return False if not interaction.participations: return False for participation in interaction.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True There are no specific requirements for the interaction class, so we can just use `zope.security.simpleinteraction.Interaction`. Since an interaction can have more than one principal, we check that *all* of them are given the necessary permission. This is not really necessary since we only create interactions with a single active principal. There is some additional code present to allow for shortcuts in defining the permission database when defining permissions for all auth groups and all permissions. Integration ~~~~~~~~~~~ At this point we have implemented our security model, and we need to integrate it with our simulation model. We do so in three separate steps. First we make it such that agents only access homes that are wrapped in a security proxy. By doing this all access to homes and services (proxies have proxied return values for their methods) is implicitly guarded by our security policy. The second step is that we want to associate the active agent with the security context so the security policy will know which agent's authentication token to validate against. The third step is to set our security policy as the default policy for the Zope security framework. It is possible to create custom security policies at a finer grained than global, but such is left as an exercise for the reader. Interaction Access ~~~~~~~~~~~~~~~~~~ The *default* implementation of the interaction management interfaces defines interactions on a per thread basis with a function for an accessor. This model is not appropriate for all systems, as it restricts one to a single active interaction per thread at any given moment. Reimplementing the interaction access methods though is easily doable and is noted here for completeness. Perspectives ~~~~~~~~~~~~ It's important to keep in mind that there is a lot more that is possible using the security framework than what's been presented here. All of the interactions are interface based, such that if you need to re-implement the semantics to suite your application a new implementation of the interface will be sufficient. Additional possibilities range from restricted interpreters and dynamic loading of untrusted code to non Zope web application security systems. Insert imagination here ;-). Zope Perspective ~~~~~~~~~~~~~~~~ A Zope3 programmer will never commonly need to interact with the low level security framework. Zope3 defines a second security package over top the low level framework and authentication sources and checkers are handled via zcml registration. Still those developing Zope3 will hopefully find this useful as an introduction into the underpinnings of the security framework. Code ~~~~ The complete code for this example is available. - sandbox.py - the agent framework - sandbox_security.py - the security implementation and binding to the agent framework. Authors ~~~~~~~ - Kapil Thangavelu - Guido Wesdorp - Marius Gedminas ====================== Untrusted interpreters ====================== Untrusted programs are executed by untrusted interpreters. Untrusted interpreters make use of security proxies to prevent un-mediated access to assets. An untrusted interpreter defines an environment for running untrusted programs. All objects within the environment are either: - "safe" objects created internally by the environment or created in the course of executing the untrusted program, or - "basic" objects - security-proxied non-basic objects The environment includes proxied functions for accessing objects outside of the environment. These proxied functions provide the only way to access information outside the environment. Because these functions are proxied, as described below, any access to objects outside the environment is mediated by the target security functions. Safe objects are objects whose operations, except for attribute retrieval, and methods access only information stored within the objects or passed as arguments. Safe objects contained within the interpreter environment can contain only information that is already in the environment or computed directly from information that is included in the environment. For this reason, safe objects created within the environment cannot be used to directly access information outside the environment. Safe objects have some attributes that could (very) indirectly be used to access assets. For this reason, an untrusted interpreter always proxies the results of attribute accesses on a safe objects. Basic objects are safe objects that are used to represent elemental data values such as strings and numbers. Basic objects require a lower level of protection than non-basic objects, as will be described detail in a later section. Security proxies mediate all object operations. Any operation access is checked to see whether a subject is authorized to perform the operation. All operation results other than basic objects are, in turn, security proxied. Security proxies will be described in greater detail in a later section. Any operation on a security proxy that results in a non-basic object is also security proxied. All external resources needed to perform an operation are security proxied. Let's consider the trusted interpreter for evaluating URLs. In operation 1 of the example, the interpreter uses a proxied method for getting the system root object. Because the method is proxied, the result of calling the method and the operation is also proxied. The interpreter has a function for traversing objects. This function is proxied. When traversing an object, the function is passed an object and a name. In operation 2, the function is passed the result of operation 1, which is the proxied root object and the name 'A'. We may traverse an object by invoking an operation on it. For example, we may use an operation to get a sub-object. Because any operation on a proxied object returns a proxied object or a basic object, the result is either a proxied object or a basic object. Traversal may also look up a component. For example, in operation 1, we might look up a presentation component named "A" for the root object. In this case, the external object is not proxied, but, when it is returned from the traversal function, it is proxied (unless it is a a basic object) because the traversal function is proxied, and the result of calling a proxied function is proxied (unless the result is a basic object). Operation 3 proceeds in the same way. When we get to operation 4, we use a function for computing the default presentation of the result of operation 3. As with traversal, the result of getting the default presentation is either a proxied object or a basic object because the function for getting the default presentation is proxied. When we get to the last operation, we have either a proxied object or a basic object. If the result of operation 4 is a basic object, we simply convert it to a string and return it as the result page. If the result of operation 4 is a non-basic object, we invoke a render operation on it and return the result as a string. Note that an untrusted interpreter may or may not provide protection against excessive resource usage. Different interpreters will provide different levels of service with respect to limitations on resource usage. If an untrusted interpreter performs an attribute access, the trusted interpreter must proxy the result unless the result is a basic object. In summary, an untrusted interpreter assures that any access to assets is mediated through security proxies by creating an environment to run untrusted code and making sure that: - The only way to access anything from outside of the environment is to call functions that are proxied in the environment. - Results of any attribute access in the environment are proxied unless the results are basic objects. Security proxies ---------------- Security proxies are objects that wrap and mediate access to objects. The Python programming language used by Zope defines a set of specific named low-level operations. In addition to operations, Python objects can have attributes, used to represent data and methods. Attributes are accessed using a dot notation. Applications can, and usually do, define methods to provide extended object behaviors. Methods are accessed as attributes through the low-level operation named "__getattribute__". The Python code:: a.b() invokes 2 operations: 1. Use the low-level `__getattribute__` operation with the name "b". 2. Use the low-level '__call__' operation on the result of the first operation. For all operations except the `__getattribute__` and `__setattribute__` operations, security proxies have a permission value defined by the permission-declaration subsystem. Two special permission values indicate that access is either forbidden (never allowed) or public (always allowed). For all other permission values, the authorization subsystem is used to decide whether the subject has the permission for the proxied object. If the subject has the permission, then access to the operation is allowed. Otherwise, access is denied. For getting or setting attributes, a proxy has permissions for getting and a permission for setting attribute values for a given attribute name. As described above, these permissions may be one of the two special permission values indicating forbidden or public access, or another permission value that must be checked with the authorization system. For all objects, Zope defines the following operations to be always public: comparison "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" hash "__hash__" boolean value "__nonzero__" class introspection "__class__" interface introspection "__providedBy__", "__implements__" adaptation "__conform__" low-level string representation "__repr__" The result of an operation on a proxied object is a security proxy unless the result is a basic value. Basic objects ------------- Basic objects are safe immutable objects that contain only immutable subobjects. Examples of basic objects include: - Strings, - Integers (long and normal), - Floating-point objects, - Date-time objects, - Boolean objects (True and False), and - The special (nil) object, None. Basic objects are safe, so, as described earlier, operations on basic objects, other than attribute access, use only information contained within the objects or information passed to them. For this reason, basic objects cannot be used to access information outside of the untrusted interpreter environment. The decision not to proxy basic objects is largely an optimization. It allows low-level safe computation to be performed without unnecessary overhead, Note that a basic object could contain sensitive information, but such a basic object would need to be obtained by making a call on a proxied object. Therefore, the access to the basic object in the first place is mediated by the security functions. Rationale for mutable safe objects ---------------------------------- Some safe objects are not basic. For these objects, we proxy the objects if they originate from outside of the environment. We do this for two reasons: 1. Non-basic objects from outside the environment need to be proxied to prevent unauthorized access to information. 2. We need to prevent un-mediated change of information from outside of the environment. We don't proxy safe objects created within the environment. This is safe to do because such safe objects can contain and provide access to information already in the environment. Sometimes the interpreter or the interpreted program needs to be able to create simple data containers to hold information computed in the course of the program execution. Several safe container types are provided for this purpose. ======= CHANGES ======= 3.8.3 (2011-09-24) ------------------ - Fixed a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ------------------ - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ------------------ - Fixed circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ------------------ - Added tests for our own ``configure.zcml``. - Added ``zcml`` extra dependencies, run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ------------------ - Prefer the standard libraries doctest module to the one from zope.testing. - Fixed directlyProvides IVocabularyFactory for PermissionIdsVocabulary in Python code, even if it's unnecessary because IVocabularyFactory is provided in zcml. - Removed the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ------------------ - Added compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ------------------ - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ------------------ - Made ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ------------------ - Ensure that simple zope.schema's VocabularyRegistry is used for PermissionVocabulary tests, because it's replaced implicitly in environments with zope.app.schema installed that makes that tests fail. - Fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ------------------ - Add zope.i18nmessageid.Message to non-proxied basic types. It's okay, because messages are immutable. It was done by zope.app.security before. - Add "__name__" and "__parent__" attributes to list of available by default. This was also done by zope.app.security package before. - Added PermissionsVocabulary and PermissionIdsVocabulary vocabularies to the ``zope.security.permission`` module. They were moved from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like "zope.View" and "zope.ManageContent", as well as for the special "zope.Public" permission. They are placed in a separate "permissions.zcml" file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many zope.* packages. - Add `addCheckerPublic` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ------------------ - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise NoInteraction when zope.security.checkPermission is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and `set` and `frozenset` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ------------------ - Install decorated security checker support on LocationProxy from the outside. - Added support to bootstrap on Jython. - Moved the `protectclass` module from `zope.app.security` to this package to reduce the number of dependencies on `zope.app.security`. - Moved the directive implementation from `zope.app.security` to this package. - Moved the directive implementation from `zope.app.component` to this package. 3.5.2 (2008-07-27) ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ------------------ - Add `frozenset`, `set`, `reversed`, and `sorted` to the list of safe builtins. 3.5.0 (2008-03-05) ------------------ - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) -------------------- - Backported a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) -------------------- - Added dependency 'zope.thread' to setup.py, without the tests were failing. - Backported a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ------------------ - Updated meta-data. 3.4.0b5 (2007-08-15) -------------------- - Bug: Fixed a circular import in the C implementation. 3.4.0b4 (2007-08-14) -------------------- - Bug: ``zope.security.management.system_user`` had an ugly/brittle id. 3.4.0b3 (2007-08-14) -------------------- - ``zope.security`` now works on Python 2.5 - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fixed inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) -------------------- - Bug: Removed stack extraction in newInteraction. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ------- - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.2.0 release. - Removed deprecated helper functions, 'proxy.trustedRemoveSecurityProxy' and 'proxy.getProxiedObject'. - Made handling of 'management.{end,restore}Interaction' more careful w.r.t. edge cases. - Made behavior of 'canWrite' consistent with 'canAccess': if 'canAccess' does not raise 'ForbiddenAttribute', then neither will 'canWrite'. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ------------------ - Added support for use of the new Python 2.4 datatypes, 'set' and 'frozenset', within checked code. - C security proxy acquired a dependency on the 'proxy.h' header from the 'zope.proxy' package. - XXX: the spelling of the '#include' is bizarre! It seems to be related to 'zpkg'-based builds, and should likely be revisited. For the moment, I have linked in the 'zope.proxy' package into our own 'include' directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Updated checker to avoid re-proxying objects which have and explicit '__Security_checker__' assigned. - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.1.0 release. - Clarified contract of 'IChecker' to indicate that its 'check*' methods may raise only 'Forbidden' or 'Unauthorized' exceptions. - Added interfaces, ('IPrincipal', 'IGroupAwarePrincipal', 'IGroup', and 'IPermission') specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ------------------ - Corresponds to the version of the zope.security package shipped as part of the Zope X3.0.0 release. Keywords: zope security policy principal permission Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.security-3.8.3/README.txt0000644000076600000240000000014611637266447015753 0ustar macstaff00000000000000The Security framework provides a generic mechanism to implement security policies on Python objects. zope.security-3.8.3/setup.cfg0000644000076600000240000000007311637266524016071 0ustar macstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.security-3.8.3/setup.py0000644000076600000240000000651711637266447015777 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.security package """ import os from setuptools import setup, find_packages, Extension def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() setup(name='zope.security', version='3.8.3', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Security Framework', long_description=( read('README.txt') + '\n.. contents::\n\n' + read('src', 'zope', 'security', 'README.txt') + '\n\n' + read('src', 'zope', 'security', 'untrustedinterpreter.txt') + '\n\n' + read('CHANGES.txt') ), keywords = "zope security policy principal permission", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope3'], url='http://pypi.python.org/pypi/zope.security', license='ZPL 2.1', packages=find_packages('src'), package_dir = {'': 'src'}, namespace_packages=['zope'], ext_modules=[Extension("zope.security._proxy", [os.path.join('src', 'zope', 'security', "_proxy.c") ], include_dirs=['include']), Extension("zope.security._zope_security_checker", [os.path.join('src', 'zope', 'security', "_zope_security_checker.c") ]), ], install_requires=['setuptools', 'zope.component', 'zope.i18nmessageid', 'zope.interface', 'zope.location', 'zope.proxy >= 3.4.2', 'zope.schema', ], extras_require = dict( untrustedpython=["RestrictedPython"], test=['zope.testing'], pytz=["pytz"], zcml=['zope.configuration'], ), include_package_data = True, zip_safe = False, ) zope.security-3.8.3/src/0000755000076600000240000000000011637266524015037 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/0000755000076600000240000000000011637266524016014 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/__init__.py0000644000076600000240000000007011637266447020126 0ustar macstaff00000000000000__import__('pkg_resources').declare_namespace(__name__) zope.security-3.8.3/src/zope/security/0000755000076600000240000000000011637266524017663 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/security/__init__.py0000644000076600000240000000140411637266447021777 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Base security system """ from zope.security.management import checkPermission from zope.security.checker import canWrite, canAccess zope.security-3.8.3/src/zope/security/_definitions.py0000644000076600000240000000171711637266447022721 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Common definitions to avoid circular imports """ import threading import zope.interface from zope.security import interfaces thread_local = threading.local() class system_user(object): zope.interface.classProvides(interfaces.IPrincipal) id = u'zope.security.management.system_user' title = u'System' description = u'' zope.security-3.8.3/src/zope/security/_proxy.c0000644000076600000240000006333411637266447021364 0ustar macstaff00000000000000/***************************************************************************** * * Copyright (c) 2003, 2004 Zope Foundation and Contributors. * All Rights Reserved. * * This software is subject to the provisions of the Zope Public License, * Version 2.1 (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. * ****************************************************************************** Security Proxy Implementation */ #include #include "zope.proxy/proxy.h" static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0; #define DECLARE_STRING(N) static PyObject *str_##N DECLARE_STRING(__3pow__); DECLARE_STRING(__call__); DECLARE_STRING(check); DECLARE_STRING(check_getattr); DECLARE_STRING(check_setattr); DECLARE_STRING(__cmp__); DECLARE_STRING(__coerce__); DECLARE_STRING(__contains__); DECLARE_STRING(__delitem__); DECLARE_STRING(__getitem__); DECLARE_STRING(__getslice__); DECLARE_STRING(__hash__); DECLARE_STRING(__iter__); DECLARE_STRING(__len__); DECLARE_STRING(next); DECLARE_STRING(__nonzero__); DECLARE_STRING(op_abs); DECLARE_STRING(op_add); DECLARE_STRING(op_and); DECLARE_STRING(op_div); DECLARE_STRING(op_divmod); DECLARE_STRING(op_float); DECLARE_STRING(op_floordiv); DECLARE_STRING(op_hex); DECLARE_STRING(op_iadd); DECLARE_STRING(op_iand); DECLARE_STRING(op_idiv); DECLARE_STRING(op_ifloordiv); DECLARE_STRING(op_ilshift); DECLARE_STRING(op_imod); DECLARE_STRING(op_imul); DECLARE_STRING(op_int); DECLARE_STRING(op_invert); DECLARE_STRING(op_ior); DECLARE_STRING(op_ipow); DECLARE_STRING(op_irshift); DECLARE_STRING(op_isub); DECLARE_STRING(op_itruediv); DECLARE_STRING(op_ixor); DECLARE_STRING(op_long); DECLARE_STRING(op_lshift); DECLARE_STRING(op_mod); DECLARE_STRING(op_mul); DECLARE_STRING(op_neg); DECLARE_STRING(op_oct); DECLARE_STRING(op_or); DECLARE_STRING(op_pos); DECLARE_STRING(op_radd); DECLARE_STRING(op_rand); DECLARE_STRING(op_rdiv); DECLARE_STRING(op_rdivmod); DECLARE_STRING(op_rfloordiv); DECLARE_STRING(op_rlshift); DECLARE_STRING(op_rmod); DECLARE_STRING(op_rmul); DECLARE_STRING(op_ror); DECLARE_STRING(op_rrshift); DECLARE_STRING(op_rshift); DECLARE_STRING(op_rsub); DECLARE_STRING(op_rtruediv); DECLARE_STRING(op_rxor); DECLARE_STRING(op_sub); DECLARE_STRING(op_truediv); DECLARE_STRING(op_xor); DECLARE_STRING(__pow__); DECLARE_STRING(proxy); DECLARE_STRING(__repr__); DECLARE_STRING(__rpow__); DECLARE_STRING(__setitem__); DECLARE_STRING(__setslice__); DECLARE_STRING(__str__); typedef struct { ProxyObject proxy; PyObject *proxy_checker; } SecurityProxy; #define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); } #undef Proxy_Check #define Proxy_Check(proxy) \ PyObject_TypeCheck(proxy, &SecurityProxyType) static PyTypeObject SecurityProxyType; /* * Machinery to call the checker. */ static int check(SecurityProxy *self, PyObject *meth, PyObject *name) { PyObject *r; /* If the checker has __setitem__, we call it's slot rather than calling check or check_getattr. 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. */ if (self->proxy_checker->ob_type->tp_as_mapping != NULL && self->proxy_checker->ob_type->tp_as_mapping->mp_ass_subscript != NULL && meth != str_check_setattr) return self->proxy_checker->ob_type->tp_as_mapping-> mp_ass_subscript(self->proxy_checker, self->proxy.proxy_object, name); r = PyObject_CallMethodObjArgs(self->proxy_checker, meth, self->proxy.proxy_object, name, NULL); if (r == NULL) return -1; Py_DECREF(r); return 0; } /* If the checker has __getitem__, we call it's slot rather than calling 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. */ #define PROXY_RESULT(self, result) \ if (result != NULL) { \ PyObject *tmp; \ if (self->proxy_checker->ob_type->tp_as_mapping != NULL \ && self->proxy_checker->ob_type->tp_as_mapping->mp_subscript != NULL) \ tmp = self->proxy_checker->ob_type->tp_as_mapping-> \ mp_subscript(self->proxy_checker, result); \ else \ tmp = PyObject_CallMethodObjArgs(self->proxy_checker, str_proxy, \ result, NULL); \ Py_DECREF(result); \ result = tmp; \ } typedef PyObject *(*function1)(PyObject *); static PyObject * check1(SecurityProxy *self, PyObject *opname, function1 operation) { PyObject *result = NULL; if (check(self, str_check, opname) >= 0) { result = operation(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * check2(PyObject *self, PyObject *other, PyObject *opname, PyObject *ropname, binaryfunc operation) { PyObject *result = NULL; if (Proxy_Check(self)) { if (check((SecurityProxy*)self, str_check, opname) >= 0) { result = operation(((SecurityProxy*)self)->proxy.proxy_object, other); PROXY_RESULT(((SecurityProxy*)self), result); } } else if (Proxy_Check(other)) { if (check((SecurityProxy*)other, str_check, ropname) >= 0) { result = operation(self, ((SecurityProxy*)other)->proxy.proxy_object); PROXY_RESULT(((SecurityProxy*)other), result); } } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return result; } static PyObject * check2i(SecurityProxy *self, PyObject *other, PyObject *opname, binaryfunc operation) { PyObject *result = NULL; if (check(self, str_check, opname) >= 0) { result = operation(self->proxy.proxy_object, other); if (result == self->proxy.proxy_object) { /* If the operation was really carried out inplace, don't create a new proxy, but use the old one. */ Py_DECREF(result); Py_INCREF((PyObject *)self); result = (PyObject *)self; } else PROXY_RESULT(self, result); } return result; } #define UNOP(NAME, CALL) \ static PyObject *proxy_##NAME(PyObject *self) \ { return check1((SecurityProxy *)self, str_op_##NAME, CALL); } #define BINOP(NAME, CALL) \ static PyObject *proxy_##NAME(PyObject *self, PyObject *other) \ { return check2(self, other, str_op_##NAME, str_op_r##NAME, CALL); } #define INPLACE(NAME, CALL) \ static PyObject *proxy_i##NAME(PyObject *self, PyObject *other) \ { return check2i((SecurityProxy *)self, other, str_op_i##NAME, CALL); } /* * Slot methods. */ static PyObject * proxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"object", "checker", 0}; SecurityProxy *self; PyObject *object; PyObject *checker; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:_Proxy.__new__", kwlist, &object, &checker)) return NULL; if (checker == Py_None) { PyErr_SetString(PyExc_ValueError, "None passed as proxy checker"); return NULL; } self = (SecurityProxy *)type->tp_alloc(type, 0); if (self == NULL) return NULL; Py_INCREF(object); Py_INCREF(checker); self->proxy.proxy_object = object; self->proxy_checker = checker; return (PyObject *)self; } /* This is needed to avoid calling the base class tp_init, which we don't need. */ static int proxy_init(PyObject *self, PyObject *args, PyObject *kw) { return 0; } static int proxy_clear(SecurityProxy *self) { CLEAR(self->proxy_checker); SecurityProxyType.tp_base->tp_clear((PyObject*)self); return 0; } static void proxy_dealloc(SecurityProxy *self) { proxy_clear(self); SecurityProxyType.tp_base->tp_dealloc((PyObject*)self); } static int proxy_traverse(SecurityProxy *self, visitproc visit, void *arg) { Py_VISIT(self->proxy.proxy_object); Py_VISIT(self->proxy_checker); return 0; } static PyObject * proxy_richcompare(SecurityProxy* self, PyObject* other, int op) { PyObject *result = NULL; result = PyObject_RichCompare(self->proxy.proxy_object, other, op); if (result == Py_True || result == Py_False) return result; PROXY_RESULT(self, result); return result; } static PyObject * proxy_iter(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___iter__) >= 0) { result = PyObject_GetIter(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * proxy_iternext(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check_getattr, str_next) >= 0) { result = PyIter_Next(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * proxy_getattro(SecurityProxy *self, PyObject *name) { PyObject *result = NULL; if (check(self, str_check_getattr, name) >= 0) { result = PyObject_GetAttr(self->proxy.proxy_object, name); PROXY_RESULT(self, result); } return result; } static int proxy_setattro(SecurityProxy *self, PyObject *name, PyObject *value) { if (check(self, str_check_setattr, name) >= 0) return PyObject_SetAttr(self->proxy.proxy_object, name, value); return -1; } static PyObject * default_repr(PyObject *object) { PyObject *klass, *name = 0, *module = 0, *result = 0; char *sname, *smodule; klass = PyObject_GetAttr(object, __class__str); if (klass == NULL) return NULL; name = PyObject_GetAttr(klass, __name__str); if (name == NULL) goto err; sname = PyString_AsString(name); if (sname == NULL) goto err; module = PyObject_GetAttr(klass, __module__str); if (module != NULL) { smodule = PyString_AsString(module); if (smodule == NULL) goto err; result = PyString_FromFormat("", smodule, sname, object); } else { PyErr_Clear(); result = PyString_FromFormat("", sname, object); } err: Py_DECREF(klass); Py_XDECREF(name); Py_XDECREF(module); return result; } static PyObject * proxy_str(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___str__) >= 0) { result = PyObject_Str(self->proxy.proxy_object); } else { PyErr_Clear(); result = default_repr(self->proxy.proxy_object); } return result; } static PyObject * proxy_repr(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___repr__) >= 0) { result = PyObject_Repr(self->proxy.proxy_object); } else { PyErr_Clear(); result = default_repr(self->proxy.proxy_object); } return result; } static int proxy_compare(SecurityProxy *self, PyObject *other) { return PyObject_Compare(self->proxy.proxy_object, other); } static long proxy_hash(SecurityProxy *self) { return PyObject_Hash(self->proxy.proxy_object); } static PyObject * proxy_call(SecurityProxy *self, PyObject *args, PyObject *kwds) { PyObject *result = NULL; if (check(self, str_check, str___call__) >= 0) { result = PyObject_Call(self->proxy.proxy_object, args, kwds); PROXY_RESULT(self, result); } return result; } /* * Number methods. */ #define NUMBER_METHOD(M) \ static PyObject * \ call_##M(PyObject *self) \ { \ PyNumberMethods *nb = self->ob_type->tp_as_number; \ if (nb == NULL || nb->nb_##M == NULL) { \ PyErr_SetString(PyExc_TypeError, \ "object can't be converted to " #M); \ return NULL; \ } \ return nb->nb_##M(self); \ } NUMBER_METHOD(int) NUMBER_METHOD(long) NUMBER_METHOD(float) NUMBER_METHOD(oct) NUMBER_METHOD(hex) static PyObject * call_ipow(PyObject *self, PyObject *other) { /* PyNumber_InPlacePower has three args. How silly. :-) */ return PyNumber_InPlacePower(self, other, Py_None); } BINOP(add, PyNumber_Add) BINOP(sub, PyNumber_Subtract) BINOP(mul, PyNumber_Multiply) BINOP(div, PyNumber_Divide) BINOP(mod, PyNumber_Remainder) BINOP(divmod, PyNumber_Divmod) static PyObject * proxy_pow(PyObject *self, PyObject *other, PyObject *modulus) { PyObject *result = NULL; if (Proxy_Check(self)) { if (check((SecurityProxy*)self, str_check, str___pow__) >= 0) { result = PyNumber_Power(((SecurityProxy*)self)->proxy.proxy_object, other, modulus); PROXY_RESULT(((SecurityProxy*)self), result); } } else if (Proxy_Check(other)) { if (check((SecurityProxy*)other, str_check, str___rpow__) >= 0) { result = PyNumber_Power(self, ((SecurityProxy*)other)->proxy.proxy_object, modulus); PROXY_RESULT(((SecurityProxy*)other), result); } } else if (modulus != NULL && Proxy_Check(modulus)) { if (check((SecurityProxy*)modulus, str_check, str___3pow__) >= 0) { result = PyNumber_Power(self, other, ((SecurityProxy*)modulus)->proxy.proxy_object); PROXY_RESULT(((SecurityProxy*)modulus), result); } } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return result; } BINOP(lshift, PyNumber_Lshift) BINOP(rshift, PyNumber_Rshift) BINOP(and, PyNumber_And) BINOP(xor, PyNumber_Xor) BINOP(or, PyNumber_Or) static int proxy_coerce(PyObject **p_self, PyObject **p_other) { PyObject *self = *p_self; PyObject *other = *p_other; assert(Proxy_Check(self)); if (check((SecurityProxy*)self, str_check, str___coerce__) >= 0) { PyObject *left = ((SecurityProxy*)self)->proxy.proxy_object; PyObject *right = other; int r; r = PyNumber_CoerceEx(&left, &right); if (r != 0) return r; /* Now left and right have been INCREF'ed. Any new value that comes out is proxied; any unchanged value is left unchanged. */ if (left == ((SecurityProxy*)self)->proxy.proxy_object) { /* Keep the old proxy */ Py_DECREF(left); Py_INCREF(self); left = self; } else { PROXY_RESULT(((SecurityProxy*)self), left); if (left == NULL) { Py_DECREF(right); return -1; } } if (right != other) { PROXY_RESULT(((SecurityProxy*)self), right); if (right == NULL) { Py_DECREF(left); return -1; } } *p_self = left; *p_other = right; return 0; } return -1; } UNOP(neg, PyNumber_Negative) UNOP(pos, PyNumber_Positive) UNOP(abs, PyNumber_Absolute) static int proxy_nonzero(PyObject *self) { return PyObject_IsTrue(((SecurityProxy*)self)->proxy.proxy_object); } UNOP(invert, PyNumber_Invert) UNOP(int, call_int) UNOP(long, call_long) UNOP(float, call_float) UNOP(oct, call_oct) UNOP(hex, call_hex) INPLACE(add, PyNumber_InPlaceAdd) INPLACE(sub, PyNumber_InPlaceSubtract) INPLACE(mul, PyNumber_InPlaceMultiply) INPLACE(div, PyNumber_InPlaceDivide) INPLACE(mod, PyNumber_InPlaceRemainder) INPLACE(pow, call_ipow) INPLACE(lshift, PyNumber_InPlaceLshift) INPLACE(rshift, PyNumber_InPlaceRshift) INPLACE(and, PyNumber_InPlaceAnd) INPLACE(xor, PyNumber_InPlaceXor) INPLACE(or, PyNumber_InPlaceOr) BINOP(floordiv, PyNumber_FloorDivide) BINOP(truediv, PyNumber_TrueDivide) INPLACE(floordiv, PyNumber_InPlaceFloorDivide) INPLACE(truediv, PyNumber_InPlaceTrueDivide) /* * Sequence methods. */ static Py_ssize_t proxy_length(SecurityProxy *self) { if (check(self, str_check, str___len__) >= 0) return PyObject_Length(self->proxy.proxy_object); return -1; } /* sq_item and sq_ass_item may be called by PySequece_{Get,Set}Item(). */ static PyObject *proxy_getitem(SecurityProxy *, PyObject *); static int proxy_setitem(SecurityProxy *, PyObject *, PyObject *); static PyObject * proxy_igetitem(SecurityProxy *self, Py_ssize_t i) { PyObject *key = PyInt_FromLong(i); PyObject *res = NULL; if (key != NULL) { res = proxy_getitem(self, key); Py_DECREF(key); } return res; } static int proxy_isetitem(SecurityProxy *self, Py_ssize_t i, PyObject *value) { PyObject *key = PyInt_FromLong(i); int res = -1; if (key != NULL) { res = proxy_setitem(self, key, value); Py_DECREF(key); } return res; } static PyObject * proxy_slice(SecurityProxy *self, Py_ssize_t start, Py_ssize_t end) { PyObject *result = NULL; if (check(self, str_check, str___getslice__) >= 0) { result = PySequence_GetSlice(self->proxy.proxy_object, start, end); PROXY_RESULT(self, result); } return result; } static int proxy_ass_slice(SecurityProxy *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) { if (check(self, str_check, str___setslice__) >= 0) return PySequence_SetSlice(self->proxy.proxy_object, i, j, value); return -1; } static int proxy_contains(SecurityProxy *self, PyObject *value) { if (check(self, str_check, str___contains__) >= 0) return PySequence_Contains(self->proxy.proxy_object, value); return -1; } /* * Mapping methods. */ static PyObject * proxy_getitem(SecurityProxy *self, PyObject *key) { PyObject *result = NULL; if (check(self, str_check, str___getitem__) >= 0) { result = PyObject_GetItem(self->proxy.proxy_object, key); PROXY_RESULT(self, result); } return result; } static int proxy_setitem(SecurityProxy *self, PyObject *key, PyObject *value) { if (value == NULL) { if (check(self, str_check, str___delitem__) >= 0) return PyObject_DelItem(self->proxy.proxy_object, key); } else { if (check(self, str_check, str___setitem__) >= 0) return PyObject_SetItem(self->proxy.proxy_object, key, value); } return -1; } /* * Normal methods. */ static PyNumberMethods proxy_as_number = { proxy_add, /* nb_add */ proxy_sub, /* nb_subtract */ proxy_mul, /* nb_multiply */ proxy_div, /* nb_divide */ proxy_mod, /* nb_remainder */ proxy_divmod, /* nb_divmod */ proxy_pow, /* nb_power */ proxy_neg, /* nb_negative */ proxy_pos, /* nb_positive */ proxy_abs, /* nb_absolute */ proxy_nonzero, /* nb_nonzero */ proxy_invert, /* nb_invert */ proxy_lshift, /* nb_lshift */ proxy_rshift, /* nb_rshift */ proxy_and, /* nb_and */ proxy_xor, /* nb_xor */ proxy_or, /* nb_or */ proxy_coerce, /* nb_coerce */ proxy_int, /* nb_int */ proxy_long, /* nb_long */ proxy_float, /* nb_float */ proxy_oct, /* nb_oct */ proxy_hex, /* nb_hex */ /* Added in release 2.0 */ /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ proxy_iadd, /* nb_inplace_add */ proxy_isub, /* nb_inplace_subtract */ proxy_imul, /* nb_inplace_multiply */ proxy_idiv, /* nb_inplace_divide */ proxy_imod, /* nb_inplace_remainder */ (ternaryfunc)proxy_ipow, /* nb_inplace_power */ proxy_ilshift, /* nb_inplace_lshift */ proxy_irshift, /* nb_inplace_rshift */ proxy_iand, /* nb_inplace_and */ proxy_ixor, /* nb_inplace_xor */ proxy_ior, /* nb_inplace_or */ /* Added in release 2.2 */ /* These require the Py_TPFLAGS_HAVE_CLASS flag */ proxy_floordiv, /* nb_floor_divide */ proxy_truediv, /* nb_true_divide */ proxy_ifloordiv, /* nb_inplace_floor_divide */ proxy_itruediv, /* nb_inplace_true_divide */ }; static PySequenceMethods proxy_as_sequence = { (lenfunc)proxy_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)proxy_igetitem, /* sq_item */ (ssizessizeargfunc)proxy_slice, /* sq_slice */ (ssizeobjargproc)proxy_isetitem, /* sq_ass_item */ (ssizessizeobjargproc)proxy_ass_slice, /* sq_ass_slice */ (objobjproc)proxy_contains, /* sq_contains */ }; static PyMappingMethods proxy_as_mapping = { (lenfunc)proxy_length, /* mp_length */ (binaryfunc)proxy_getitem, /* mp_subscript */ (objobjargproc)proxy_setitem, /* mp_ass_subscript */ }; static char proxy_doc[] = "\ Security proxy class. Constructor: _Proxy(object, checker)\n\ where 'object' is an arbitrary object, and 'checker' is an object\n\ whose signature is described by the IChecker interface.\n\ A checker should have the following methods:\n\ check(object, operation) # operation is e.g. '__add__' or '__hash__'\n\ check_getattr(object, name)\n\ check_setattr(object, name)\n\ proxy(object)\n\ The check methods should raise an exception if the operation is\n\ disallowed. The proxy method should return a proxy for the object\n\ if one is needed, otherwise the object itself.\n\ "; statichere PyTypeObject SecurityProxyType = { PyObject_HEAD_INIT(NULL) 0, "zope.security._proxy._Proxy", sizeof(SecurityProxy), 0, (destructor)proxy_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)proxy_compare, /* tp_compare */ (reprfunc)proxy_repr, /* tp_repr */ &proxy_as_number, /* tp_as_number */ &proxy_as_sequence, /* tp_as_sequence */ &proxy_as_mapping, /* tp_as_mapping */ (hashfunc)proxy_hash, /* tp_hash */ (ternaryfunc)proxy_call, /* tp_call */ (reprfunc)proxy_str, /* tp_str */ (getattrofunc)proxy_getattro, /* tp_getattro */ (setattrofunc)proxy_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_GC, /* tp_flags */ proxy_doc, /* tp_doc */ (traverseproc)proxy_traverse, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)proxy_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)proxy_iter, /* tp_iter */ (iternextfunc)proxy_iternext, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ proxy_init, /* tp_init */ 0, /*PyType_GenericAlloc,*/ /* tp_alloc */ proxy_new, /* tp_new */ 0, /*_PyObject_GC_Del,*/ /* tp_free */ }; static PyObject * module_getChecker(PyObject *self, PyObject *arg) { PyObject *result; if (!Proxy_Check(arg)) { PyErr_SetString(PyExc_TypeError, "getChecker argument must be a _Proxy"); return NULL; } result = ((SecurityProxy*)arg)->proxy_checker; Py_INCREF(result); return result; } static PyObject * module_getObject(PyObject *self, PyObject *arg) { PyObject *result; if (!Proxy_Check(arg)) result = arg; else result = ((SecurityProxy*)arg)->proxy.proxy_object; Py_INCREF(result); return result; } static PyMethodDef module_functions[] = { {"getChecker", module_getChecker, METH_O, "get checker from proxy"}, {"getObject", module_getObject, METH_O, "Get the proxied object\n\nReturn the original object if not proxied."}, {NULL} }; static char module___doc__[] = "Security proxy implementation."; void init_proxy(void) { PyObject *m; if (Proxy_Import() < 0) return; #define INIT_STRING(S) \ if((str_##S = PyString_InternFromString(#S)) == NULL) return #define INIT_STRING_OP(S) \ if((str_op_##S = PyString_InternFromString("__" #S "__")) == NULL) return INIT_STRING(__3pow__); INIT_STRING(__call__); INIT_STRING(check); INIT_STRING(check_getattr); INIT_STRING(check_setattr); INIT_STRING(__cmp__); INIT_STRING(__coerce__); INIT_STRING(__contains__); INIT_STRING(__delitem__); INIT_STRING(__getitem__); INIT_STRING(__getslice__); INIT_STRING(__hash__); INIT_STRING(__iter__); INIT_STRING(__len__); INIT_STRING(next); INIT_STRING(__nonzero__); INIT_STRING_OP(abs); INIT_STRING_OP(add); INIT_STRING_OP(and); INIT_STRING_OP(div); INIT_STRING_OP(divmod); INIT_STRING_OP(float); INIT_STRING_OP(floordiv); INIT_STRING_OP(hex); INIT_STRING_OP(iadd); INIT_STRING_OP(iand); INIT_STRING_OP(idiv); INIT_STRING_OP(ifloordiv); INIT_STRING_OP(ilshift); INIT_STRING_OP(imod); INIT_STRING_OP(imul); INIT_STRING_OP(int); INIT_STRING_OP(invert); INIT_STRING_OP(ior); INIT_STRING_OP(ipow); INIT_STRING_OP(irshift); INIT_STRING_OP(isub); INIT_STRING_OP(itruediv); INIT_STRING_OP(ixor); INIT_STRING_OP(long); INIT_STRING_OP(lshift); INIT_STRING_OP(mod); INIT_STRING_OP(mul); INIT_STRING_OP(neg); INIT_STRING_OP(oct); INIT_STRING_OP(or); INIT_STRING_OP(pos); INIT_STRING_OP(radd); INIT_STRING_OP(rand); INIT_STRING_OP(rdiv); INIT_STRING_OP(rdivmod); INIT_STRING_OP(rfloordiv); INIT_STRING_OP(rlshift); INIT_STRING_OP(rmod); INIT_STRING_OP(rmul); INIT_STRING_OP(ror); INIT_STRING_OP(rrshift); INIT_STRING_OP(rshift); INIT_STRING_OP(rsub); INIT_STRING_OP(rtruediv); INIT_STRING_OP(rxor); INIT_STRING_OP(sub); INIT_STRING_OP(truediv); INIT_STRING_OP(xor); INIT_STRING(__pow__); INIT_STRING(proxy); INIT_STRING(__repr__); INIT_STRING(__rpow__); INIT_STRING(__setitem__); INIT_STRING(__setslice__); INIT_STRING(__str__); __class__str = PyString_FromString("__class__"); if (! __class__str) return; __name__str = PyString_FromString("__name__"); if (! __name__str) return; __module__str = PyString_FromString("__module__"); if (! __module__str) return; SecurityProxyType.ob_type = &PyType_Type; SecurityProxyType.tp_alloc = PyType_GenericAlloc; SecurityProxyType.tp_free = _PyObject_GC_Del; SecurityProxyType.tp_base = &ProxyType; if (PyType_Ready(&SecurityProxyType) < 0) return; m = Py_InitModule3("_proxy", module_functions, module___doc__); if (m == NULL) return; Py_INCREF(&SecurityProxyType); PyModule_AddObject(m, "_Proxy", (PyObject *)&SecurityProxyType); } zope.security-3.8.3/src/zope/security/_zope_security_checker.c0000644000076600000240000004056011637266447024567 0ustar macstaff00000000000000/* Copyright (c) 2004 Zope Foundation and Contributors. All Rights Reserved. This software is subject to the provisions of the Zope Public License, Version 2.1 (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 static PyObject *_checkers, *_defaultChecker, *_available_by_default, *NoProxy; static PyObject *Proxy, *thread_local, *CheckerPublic; static PyObject *ForbiddenAttribute, *Unauthorized; #define DECLARE_STRING(N) static PyObject *str_##N DECLARE_STRING(checkPermission); DECLARE_STRING(__Security_checker__); DECLARE_STRING(interaction); #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 *interaction, *r; int i; /* if thread_local.interaction.checkPermission(permission, object): */ /* return */ interaction = PyObject_GetAttr(thread_local, str_interaction); if (interaction == NULL) return -1; r = PyObject_CallMethodObjArgs(interaction, str_checkPermission, permission, object, NULL); Py_DECREF(interaction); if (r == NULL) return -1; i = PyObject_IsTrue(r); Py_DECREF(r); if (i < 0) return -1; if (i) return 0; /* else: */ /* __traceback_supplement__ = (TracebackSupplement, object) */ /* raise Unauthorized(object, name, permission) */ r = Py_BuildValue("OOO", object, name, permission); if (r == NULL) return -1; PyErr_SetObject(Unauthorized, r); Py_DECREF(r); return -1; } /* def check(self, object, name): */ /* Note that we have an int version here because we will use it for __setitem__, as described 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 _available_by_default: */ /* return */ int ic = PySequence_Contains(_available_by_default, 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; /* 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: */ 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; } } else if (checker == Py_None) { PyObject *errv = Py_BuildValue("sO", "Invalid value, None. " "for security checker", value); if (errv != NULL) { PyErr_SetObject(PyExc_ValueError, errv); Py_DECREF(errv); } return NULL; } 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 */ 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__); INIT_STRING(interaction); 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._definitions")) == NULL) return; thread_local = PyObject_GetAttrString(m, "thread_local"); if (thread_local == NULL) return; Py_DECREF(m); if ((m = PyImport_ImportModule("zope.security.interfaces")) == 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 ((_available_by_default = 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(_available_by_default); Py_INCREF(&CheckerType); PyModule_AddObject(m, "Checker", (PyObject *)&CheckerType); } zope.security-3.8.3/src/zope/security/adapter.py0000644000076600000240000001130211637266447021656 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Support for taking security into account in adaptation """ from zope.security.checker import ProxyFactory from zope.security.proxy import removeSecurityProxy from zope.location import ILocation, LocationProxy def assertLocation(adapter, parent): """Assert locatable adapters. This function asserts that the adapter get location-proxied if it doesn't provide ILocation itself. Further more the returned locatable adapter get its parent set if its __parent__ attribute is currently None. see adapter.txt """ # handle none-locatable adapters (A) if not ILocation.providedBy(adapter): locatable = LocationProxy(adapter) locatable.__parent__ = parent return locatable # handle locatable, parentless adapters (B) if adapter.__parent__ is None: adapter.__parent__ = parent return adapter # handle locatable, parentful adapters (C) else: return adapter class LocatingTrustedAdapterFactory(object): """Adapt an adapter factory to provide trusted and (locatable) adapters. Trusted adapters always adapt unproxied objects. If asked to adapt any proxied objects, it will unproxy them and then security-proxy the resulting adapter (S) unless the objects where not security-proxied before (N). Further locating trusted adapters provide a location for protected adapters only (S). If such a protected adapter itself does not provide ILocation it is wrapped within a location proxy and it parent will be set. If the adapter does provide ILocation and it's __parent__ is None, we set the __parent__ to the adapter's context: see adapter.txt """ def __init__(self, factory): self.factory = factory self.__name__ = factory.__name__ self.__module__ = factory.__module__ # protected methods def _customizeProtected(self, adapter, context): return assertLocation(adapter, context) def _customizeUnprotected(self, adapter, context): if (ILocation.providedBy(adapter) and adapter.__parent__ is None): adapter.__parent__ = context return adapter def __call__(self, *args): for arg in args: if removeSecurityProxy(arg) is not arg: args = map(removeSecurityProxy, args) adapter = self.factory(*args) adapter = self._customizeProtected(adapter, args[0]) return ProxyFactory(adapter) adapter = self.factory(*args) adapter = self._customizeUnprotected(adapter, args[0]) return adapter # BBB, entire class gone in 3.2 class TrustedAdapterFactory(LocatingTrustedAdapterFactory): """Adapt an adapter factory to provide trusted adapters. Trusted adapters always adapt unproxied objects. If asked to adapt any proxied objects, it will unproxy them and then security-proxy the resulting adapter unless the objects where not security-proxied before. If the adapter does provide ILocation and it's __parent__ is None, we set the __parent__ to the adapter's context. """ # do not location-proxy the adapter def _customizeProtected(self, adapter, context): return self._customizeUnprotected(adapter, context) class LocatingUntrustedAdapterFactory(object): """Adapt an adapter factory to provide locatable untrusted adapters Untrusted adapters always adapt proxied objects. If any permission other than zope.Public is required, untrusted adapters need a location in order that the local authentication mechanism can be inovked correctly. If the adapter does not provide ILocation, we location proxy it and set the parent. If the adapter does provide ILocation and it's __parent__ is None, we set the __parent__ to the adapter's context only: see adapter.txt """ def __init__(self, factory): self.factory = factory self.__name__ = factory.__name__ self.__module__ = factory.__module__ def __call__(self, *args): adapter = self.factory(*args) return assertLocation(adapter, args[0]) zope.security-3.8.3/src/zope/security/checker.py0000644000076600000240000006276611637266447021666 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Security Checkers You can set the environment variable ZOPE_WATCH_CHECKERS to get additional security checker debugging output on the standard error. Setting ZOPE_WATCH_CHECKERS to 1 will display messages about unauthorized or forbidden attribute access. Setting it to a larger number will also display messages about granted attribute access. Note that the ZOPE_WATCH_CHECKERS mechanism will eventually be replaced with a more general security auditing mechanism. """ import os import sys import types import datetime import decimal import weakref import zope.interface.interface import zope.interface.interfaces import zope.interface.declarations from zope.i18nmessageid import Message from zope.interface import directlyProvides, Interface, implements from zope.interface.interfaces import IInterface, IDeclaration from zope.security.interfaces import IChecker, INameBasedChecker from zope.security.interfaces import ISecurityProxyFactory from zope.security.interfaces import Unauthorized, ForbiddenAttribute from zope.security._definitions import thread_local from zope.security._proxy import _Proxy as Proxy, getChecker try: from zope.exceptions import DuplicationError except ImportError: class DuplicationError(Exception): """A duplicate registration was attempted""" if os.environ.get('ZOPE_WATCH_CHECKERS'): try: WATCH_CHECKERS = int(os.environ.get('ZOPE_WATCH_CHECKERS')) except ValueError: WATCH_CHECKERS = 1 else: WATCH_CHECKERS = 0 def ProxyFactory(object, checker=None): """Factory function that creates a proxy for an object The proxy checker is looked up if not provided. """ if type(object) is Proxy: if checker is None or checker is getChecker(object): return object else: # We have a proxy, but someone asked us to change its checker. # Let's raise an exception. # # Other reasonable actions would be to either keep the existing # proxy, or to create a new one with the given checker. # The latter might be a security hole though, if untrusted code # can call ProxyFactory. raise TypeError("Tried to use ProxyFactory to change a Proxy's" " checker.") if checker is None: checker = getattr(object, '__Security_checker__', None) if checker is None: checker = selectChecker(object) if checker is None: return object return Proxy(object, checker) 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 except ForbiddenAttribute: # we are going to be a bit DWIM-y here: see # http://www.zope.org/Collectors/Zope3-dev/506 # generally, if the check is ForbiddenAttribute we want it to be # raised: it probably indicates a programming or configuration error. # However, we special case a write ForbiddenAttribute when one can # actually read the attribute: this represents a reasonable # configuration of a readonly attribute, and returning False (meaning # "no, you can't write it") is arguably more useful than raising the # exception. try: checker.check_getattr(obj, name) # we'll let *this* ForbiddenAttribute fall through, if any. It # means that both read and write are forbidden. except Unauthorized: pass return False # all other exceptions, other than Unauthorized and ForbiddenAttribute, # should be passed through uncaught, as they indicate programmer 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) def __init__(self, get_permissions, set_permissions=None): """Create a checker A dictionary must be provided for computing permissions for 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 granted without checking a permission. An optional setattr dictionary may be provided for checking set attribute access. """ assert isinstance(get_permissions, dict) self.get_permissions = get_permissions if set_permissions is not None: assert isinstance(set_permissions, dict) self.set_permissions = set_permissions def permission_id(self, name): 'See INameBasedChecker' return self.get_permissions.get(name) def setattr_permission_id(self, name): 'See INameBasedChecker' if self.set_permissions: return self.set_permissions.get(name) def check_getattr(self, object, name): 'See IChecker' self.check(object, name) def check_setattr(self, object, name): 'See IChecker' if self.set_permissions: permission = self.set_permissions.get(name) else: permission = None if permission is not None: if permission is CheckerPublic: return # Public if thread_local.interaction.checkPermission(permission, object): return # allowed else: __traceback_supplement__ = (TracebackSupplement, object) raise Unauthorized(object, name, permission) __traceback_supplement__ = (TracebackSupplement, object) raise ForbiddenAttribute(name, object) def check(self, object, name): 'See IChecker' permission = self.get_permissions.get(name) if permission is not None: if permission is CheckerPublic: return # Public if thread_local.interaction.checkPermission(permission, object): return else: __traceback_supplement__ = (TracebackSupplement, object) raise Unauthorized(object, name, permission) elif name in _available_by_default: return if name != '__iter__' or hasattr(object, name): __traceback_supplement__ = (TracebackSupplement, object) raise ForbiddenAttribute(name, object) 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) if checker is None: return value return Proxy(value, checker) # Helper class for __traceback_supplement__ class TracebackSupplement(object): def __init__(self, obj): self.obj = obj def getInfo(self): result = [] try: cls = self.obj.__class__ if hasattr(cls, "__module__"): s = "%s.%s" % (cls.__module__, cls.__name__) else: s = str(cls.__name__) result.append(" - class: " + s) except: pass try: cls = type(self.obj) if hasattr(cls, "__module__"): s = "%s.%s" % (cls.__module__, cls.__name__) else: s = str(cls.__name__) result.append(" - type: " + s) except: pass return "\n".join(result) class Global(object): """A global object that behaves like a string. We want this to behave as a global, meaning it's pickled by name, rather than value. We need to arrange that it has a suitable __reduce__. """ def __init__(self, name, module=None): if module is None: module = sys._getframe(1).f_locals['__name__'] self.__name__ = name self.__module__ = module def __reduce__(self): return self.__name__ def __repr__(self): return "%s(%s,%s)" % (self.__class__.__name__, self.__name__, self.__module__) # Marker for public attributes CheckerPublic = Global('CheckerPublic') # Now we wrap it in a security proxy so that it retains it's # identity when it needs to be security proxied. d={} CheckerPublic = Proxy(CheckerPublic, Checker(d)) d['__reduce__'] = CheckerPublic del d # TODO: It's a bit scary above that we can pickle a proxy if access is # granted to __reduce__. We might want to bother to prevent this in # general and only allow it in this specific case. def NamesChecker(names=(), permission_id=CheckerPublic, **__kw__): """Return a checker that grants access to a set of names. A sequence of names is given as the first argument. If a second argument, permission_id, is given, it is the permission required to access the names. Additional names and permission ids can be supplied as keyword arguments. """ data = {} data.update(__kw__) for name in names: if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id return Checker(data) def InterfaceChecker(interface, permission_id=CheckerPublic, **__kw__): return NamesChecker(interface.names(all=True), permission_id, **__kw__) def MultiChecker(specs): """Create a checker from a sequence of specifications A specification is: - A two-tuple with: o a sequence of names or an interface o a permission id All the names in the sequence of names or the interface are protected by the permission. - A dictionoid (having an items method), with items that are name/permission-id pairs. """ data = {} for spec in specs: if type(spec) is tuple: names, permission_id = spec if IInterface.providedBy(names): names = names.names(all=True) for name in names: if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id else: for name, permission_id in spec.items(): if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id return Checker(data) 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. """ # We need to be careful here. We might have a proxy, in which case # we can't use the type. OTOH, we might not be able to use the # __class__ either, since not everything has one. # TODO: we really need formal proxy introspection #if type(object) is Proxy: # # Is this already a security proxy? # return None checker = _getChecker(type(object), _defaultChecker) #checker = _getChecker(getattr(object, '__class__', type(object)), # _defaultChecker) if checker is NoProxy: return None while not isinstance(checker, Checker): checker = checker(object) if checker is NoProxy or checker is None: return None return checker def getCheckerForInstancesOf(class_): return _checkers.get(class_) def defineChecker(type_, checker): """Define a checker for a given type of object The checker can be a Checker, or a function that, when called with an object, returns a Checker. """ if not isinstance(type_, (type, types.ClassType, types.ModuleType)): raise TypeError( 'type_ must be a type, class or module, not a %s' % type_) if type_ in _checkers: raise DuplicationError(type_) _checkers[type_] = checker def undefineChecker(type_): del _checkers[type_] NoProxy = object() # _checkers is a mapping. # # - Keys are types # # - Values are # # o None => rock # o a Checker # o a function returning None or a Checker # _checkers = {} _defaultChecker = Checker({}) _available_by_default = [] # Get optimized versions try: import zope.security._zope_security_checker except ImportError: pass else: from zope.security._zope_security_checker import _checkers, selectChecker from zope.security._zope_security_checker import NoProxy, Checker from zope.security._zope_security_checker import _defaultChecker from zope.security._zope_security_checker import _available_by_default zope.interface.classImplements(Checker, INameBasedChecker) _getChecker = _checkers.get 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 check_getattr = __setitem__ = check 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(object): """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 _available_by_default: 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 _available_by_default: 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): return _checkers.get(inst.__class__, _defaultChecker) def moduleChecker(module): return _checkers.get(module) _available_by_default[:] = ['__lt__', '__le__', '__eq__', '__gt__', '__ge__', '__ne__', '__hash__', '__nonzero__', '__class__', '__providedBy__', '__implements__', '__repr__', '__conform__', '__name__', '__parent__', ] _callableChecker = NamesChecker(['__str__', '__name__', '__call__']) _typeChecker = NamesChecker( ['__str__', '__name__', '__module__', '__bases__', '__mro__', '__implemented__']) _namedChecker = NamesChecker(['__name__']) _iteratorChecker = NamesChecker(['next', '__iter__']) _setChecker = NamesChecker(['__iter__', '__len__', '__str__', '__contains__', 'copy', 'difference', 'intersection', 'issubset', 'issuperset', 'symmetric_difference', 'union', '__and__', '__or__', '__sub__', '__xor__', '__rand__', '__ror__', '__rsub__', '__rxor__', '__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__']) class BasicTypes(dict): """Basic Types Dictionary Make sure that checkers are really updated, when a new type is added. """ def __setitem__(self, name, value): super(BasicTypes.__class__, self).__setitem__(name, value) _checkers[name] = value def __delitem__(self, name): super(BasicTypes.__class__, self).__delitem__(name) del _checkers[name] def clear(self): # Make sure you cannot clear the values raise NotImplementedError def update(self, d): super(BasicTypes.__class__, self).update(d) _checkers.update(d) _basic_types = { object: NoProxy, int: NoProxy, float: NoProxy, long: NoProxy, complex: NoProxy, types.NoneType: NoProxy, str: NoProxy, unicode: NoProxy, Message: NoProxy, # Messages are immutable, so it's okay bool: NoProxy, datetime.timedelta: NoProxy, datetime.datetime: NoProxy, datetime.date: NoProxy, datetime.time: NoProxy, datetime.tzinfo: NoProxy, } try: import pytz except ImportError: pass else: _basic_types[type(pytz.UTC)] = NoProxy BasicTypes = BasicTypes(_basic_types) del _basic_types # Available for tests. Located here so it can be kept in sync with BasicTypes. BasicTypes_examples = { object: object(), int: 65536, float: -1.4142, long: 65536l, complex: -1.4142j, types.NoneType: None, str: 'abc', unicode: u'uabc', bool: True, datetime.timedelta: datetime.timedelta(3), datetime.datetime: datetime.datetime(2003, 1, 1), datetime.date: datetime.date(2003, 1, 1), datetime.time: datetime.time(23, 58), Message: Message('message', domain='hello') } class _Sequence(object): def __len__(self): return 0 def __getitem__(self, i): raise IndexError _Declaration_checker = InterfaceChecker( IDeclaration, _implied=CheckerPublic, subscribe=CheckerPublic, unsubscribe=CheckerPublic, __call__=CheckerPublic, ) def f(): yield f _default_checkers = { dict: NamesChecker(['__getitem__', '__len__', '__iter__', 'get', 'has_key', 'copy', '__str__', 'keys', 'values', 'items', 'iterkeys', 'iteritems', 'itervalues', '__contains__']), list: NamesChecker(['__getitem__', '__getslice__', '__len__', '__iter__', '__contains__', 'index', 'count', '__str__', '__add__', '__radd__', ]), set: _setChecker, frozenset: _setChecker, decimal.Decimal: NamesChecker(['__nonzero__', '__cmp__', '__eq__', '__ne__', 'compare', '__hash__', 'as_tuple', '__str__', 'to_eng_string', '__neg__', '__pos__', '__abs__', '__add__', '__radd__', '__sub__', '__rsub__', '__mul__', '__rmul__', '__div__', '__rdiv__', '__rtruediv__', '__divmod__', '__rdivmod__', '__mod__', '__rmod__', 'remainder_near', '__floordiv__', '__rfloordiv__', '__float__', '__int__', '__long__', '__pow__', '__rpow__', 'normalize', 'quantize', 'same_quantum', 'to_integral', 'sqrt', 'max', 'min', 'adjusted']), # YAGNI: () a rock tuple: NamesChecker(['__getitem__', '__getslice__', '__add__', '__radd__', '__contains__', '__len__', '__iter__', '__str__']), types.InstanceType: _instanceChecker, Proxy: NoProxy, type(weakref.ref(_Sequence())): NamesChecker(['__call__']), types.ClassType: _typeChecker, types.FunctionType: _callableChecker, types.MethodType: _callableChecker, types.BuiltinFunctionType: _callableChecker, types.BuiltinMethodType: _callableChecker, type(().__getslice__): _callableChecker, # slot description type: _typeChecker, types.ModuleType: lambda module: _checkers.get(module, _namedChecker), type(iter([])): _iteratorChecker, # Same types in Python 2.2.1, type(iter(())): _iteratorChecker, # different in Python 2.3. type(iter({})): _iteratorChecker, type(iter(set())): _iteratorChecker, type({}.iteritems()): _iteratorChecker, type({}.iterkeys()): _iteratorChecker, type({}.itervalues()): _iteratorChecker, type(iter(_Sequence())): _iteratorChecker, type(f()): _iteratorChecker, type(Interface): InterfaceChecker( IInterface, __str__=CheckerPublic, _implied=CheckerPublic, subscribe=CheckerPublic, ), zope.interface.interface.Method: InterfaceChecker( zope.interface.interfaces.IMethod), zope.interface.declarations.ProvidesClass: _Declaration_checker, zope.interface.declarations.ClassProvides: _Declaration_checker, zope.interface.declarations.Implements: _Declaration_checker, zope.interface.declarations.Declaration: _Declaration_checker, } if sys.version_info < (2, 6): import sets _default_checkers[sets.Set] = _setChecker _default_checkers[sets.ImmutableSet] = _setChecker if sys.version_info >= (2, 6): import abc _default_checkers[abc.ABCMeta] = _typeChecker def _clear(): _checkers.clear() _checkers.update(_default_checkers) _checkers.update(BasicTypes) _clear() try: from zope.testing.cleanup import addCleanUp except ImportError: pass else: addCleanUp(_clear) zope.security-3.8.3/src/zope/security/configure.zcml0000644000076600000240000000117711637266447022545 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope/security/decorator.py0000644000076600000240000001677511637266447022243 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Decorator support Decorators are proxies that are mostly transparent but that may provide additional features. """ __docformat__ = "reStructuredText" from zope.interface.declarations import ObjectSpecification from zope.proxy import getProxiedObject, ProxyBase from zope.proxy.decorator import SpecificationDecoratorBase from zope.security.checker import selectChecker, CombinedChecker from zope.security.proxy import Proxy from zope.security.proxy import getChecker class DecoratedSecurityCheckerDescriptor(object): """Descriptor for a Decorator that provides a decorated security checker. To illustrate, we'll create a class that will be proxied: >>> class Foo(object): ... a = 'a' and a class to proxy it that uses a decorated security checker: >>> class Wrapper(ProxyBase): ... b = 'b' ... __Security_checker__ = DecoratedSecurityCheckerDescriptor() Next we'll create and register a checker for `Foo`: >>> from zope.security.checker import NamesChecker, defineChecker >>> fooChecker = NamesChecker(['a']) >>> defineChecker(Foo, fooChecker) along with a checker for `Wrapper`: >>> wrapperChecker = NamesChecker(['b']) >>> defineChecker(Wrapper, wrapperChecker) Using `selectChecker()`, we can confirm that a `Foo` object uses `fooChecker`: >>> foo = Foo() >>> selectChecker(foo) is fooChecker True >>> fooChecker.check(foo, 'a') >>> fooChecker.check(foo, 'b') # doctest: +ELLIPSIS Traceback (most recent call last): ForbiddenAttribute: ('b', ) and that a `Wrapper` object uses `wrappeChecker`: >>> wrapper = Wrapper(foo) >>> selectChecker(wrapper) is wrapperChecker True >>> wrapperChecker.check(wrapper, 'b') >>> wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS Traceback (most recent call last): ForbiddenAttribute: ('a', ) (Note that the object description says `Foo` because the object is a proxy and generally looks and acts like the object it's proxying.) When we access wrapper's ``__Security_checker__`` attribute, we invoke the decorated security checker descriptor. The decorator's job is to make sure checkers from both objects are used when available. In this case, because both objects have checkers, we get a combined checker: >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker will work even with security proxied objects. To illustrate, we'll proxify `foo`: >>> from zope.security.proxy import ProxyFactory >>> secure_foo = ProxyFactory(foo) >>> secure_foo.a 'a' >>> secure_foo.b # doctest: +ELLIPSIS Traceback (most recent call last): ForbiddenAttribute: ('b', ) when we wrap the secured `foo`: >>> wrapper = Wrapper(secure_foo) we still get a combined checker: >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker has three other scenarios: - the wrapper has a checker but the proxied object doesn't - the proxied object has a checker but the wrapper doesn't - neither the wrapper nor the proxied object have checkers When the wrapper has a checker but the proxied object doesn't: >>> from zope.security.checker import NoProxy, _checkers >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is wrapperChecker True the decorator uses only the wrapper checker: >>> wrapper = Wrapper(foo) >>> wrapper.__Security_checker__ is wrapperChecker True When the proxied object has a checker but the wrapper doesn't: >>> del _checkers[Wrapper] >>> defineChecker(Wrapper, NoProxy) >>> selectChecker(wrapper) is None True >>> del _checkers[Foo] >>> defineChecker(Foo, fooChecker) >>> selectChecker(foo) is fooChecker True the decorator uses only the proxied object checker: >>> wrapper.__Security_checker__ is fooChecker True Finally, if neither the wrapper not the proxied have checkers: >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is None True the decorator doesn't have a checker: >>> wrapper.__Security_checker__ Traceback (most recent call last): ... AttributeError: 'Foo' has no attribute '__Security_checker__' __Security_checker__ cannot be None, otherwise Checker.proxy blows up: >>> checker.proxy(wrapper) is wrapper True """ def __get__(self, inst, cls=None): if inst is None: return self else: proxied_object = getProxiedObject(inst) if type(proxied_object) is Proxy: checker = getChecker(proxied_object) else: checker = getattr(proxied_object, '__Security_checker__', None) if checker is None: checker = selectChecker(proxied_object) wrapper_checker = selectChecker(inst) if wrapper_checker is None and checker is None: raise AttributeError("%r has no attribute %r" % (proxied_object.__class__.__name__, '__Security_checker__')) elif wrapper_checker is None: return checker elif checker is None: return wrapper_checker else: return CombinedChecker(wrapper_checker, checker) def __set__(self, inst, value): raise TypeError("Can't set __Security_checker__ on a decorated object") class SecurityCheckerDecoratorBase(ProxyBase): """Base class for a proxy that provides additional security declarations.""" __Security_checker__ = DecoratedSecurityCheckerDescriptor() class DecoratorBase(SpecificationDecoratorBase, SecurityCheckerDecoratorBase): """Base class for a proxy that provides both additional interfaces and security declarations.""" # zope.location was made independent of security. To work together with # security, we re-inject the DecoratedSecurityCheckerDescriptor onto the # location proxy from here. # This is the only sane place we found for doing it: it kicks in as soon # as someone starts using security proxies. import zope.location.location zope.location.location.LocationProxy.__Security_checker__ = ( DecoratedSecurityCheckerDescriptor()) zope.security-3.8.3/src/zope/security/examples/0000755000076600000240000000000011637266524021501 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/security/examples/sandbox.py0000644000076600000240000002105311637266447023516 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """A small sandbox application. """ import time, random from zope.interface import Interface, implements class IAgent(Interface): """A player/agent in the world. The agent represents an autonomous unit, that lives in various homes/sandboxes and accesses services present at the sandboxes. Agents are imbued with a sense of wanderlust and attempt to find new homes after a few turns of the time generator (think turn based games). """ def action(): """Perform agent's action.""" def setHome(home): """Move to a different home.""" def getHome(): """Return the place where the agent currently lives.""" def getAuthenticationToken(): """Return the authority by which the agent perform actions.""" class IService(Interface): """Marker to designate some form of functionality. Services are available from sandboxes, examples include time service, agent discovery, and sandbox discovery. """ class ISandbox(Interface): """A container for agents to live in and services to be available.""" def getService(service_id): """Get the service having the provided id in this sandbox.""" def getAgents(): """Return a list of agents living in this sandbox.""" def addAgent(agent): """Add a new agent to the sandbox.""" def transportAgent(agent, destination): """Move the specified agent to the destination sandbox.""" class SandboxError(Exception): """A sandbox error is thrown, if any action could not be performed.""" pass class Identity(object): """Mixin for pretty printing and identity method""" def __init__(self, id, *args, **kw): self.id = id def getId(self): return self.id def __str__ (self): return "<%s> %s"%(str(self.__class__.__name__), str(self.id)) __repr__ = __str__ class Agent(Identity): implements(IAgent) def __init__(self, id, home, auth_token, action): """Initialize agent.""" self.id = id self.auth_token = auth_token self.home = home self._action = action def action(self): """See IAgent.""" self._action(self, self.getHome()) def setHome(self, home): """See IAgent.""" self.home = home def getHome(self): """See IAgent.""" return self.home def getAuthenticationToken(self): """See IAgent.""" return self.auth_token class Sandbox(Identity): """ see ISandbox doc """ implements(ISandbox) def __init__(self, id, service_factories): self.id = id self._services = {} self._agents = {} for sf in service_factories: self.addService(sf()) def getAgentIds(self): return self._agents.keys() def getAgents(self): return self._agents.values() def getServiceIds(self): return self._services.keys() def getService(self, sid): return self._services.get(sid) def getHome(self): return self def addAgent(self, agent): if not self._agents.has_key(agent.getId()) \ and IAgent.providedBy(agent): self._agents[agent.getId()]=agent agent.setHome(self) else: raise SandboxError("couldn't add agent %s"%agent) def addService(self, service): if not self._services.has_key(service.getId()) \ and IService.providedBy(service): self._services[service.getId()]=service service.setHome(self) else: raise SandboxError("couldn't add service %s"%service) def transportAgent(self, agent, destination): if self._agents.has_key(agent.getId()) \ and destination is not self \ and ISandbox.providedBy(destination): destination.addAgent(agent) del self._agents[agent.getId()] else: raise SandboxError("couldn't transport agent %s to %s"%( agent, destination) ) class Service(object): implements(IService) def getId(self): return self.__class__.__name__ def setHome(self, home): self._home = home def getHome(self): return getattr(self, '_home') class HomeDiscoveryService(Service): """ returns the ids of available agent homes """ def getAvailableHomes(self): return _homes.keys() class AgentDiscoveryService(Service): """ returns the agents available at a given home """ def getLocalAgents(self, home): return home.getAgents() class TimeService(Service): """ returns the local time """ def getTime(self): return time.time() default_service_factories = ( HomeDiscoveryService, AgentDiscoveryService, TimeService ) def action_find_homes(agent, home): home_service = home.getService('HomeDiscoveryService') return home_service.getAvailableHomes() def action_find_neighbors(agent, home): agent_service = home.getService('AgentDiscoveryService') return agent_service.getLocalAgents(home) def action_find_time(agent, home): time_service = home.getService('TimeService') return time_service.getTime() class TimeGenerator(object): """Represents the passage of time in the agent simulation. each turn represents some discrete unit of time, during which all agents attempt to perform their action. Additionally, all agents are checked to see if they have a desire to move, and if so are transported to a new random home. """ def setupAgent(self, agent): pass def teardownAgent(self, agent): pass def turn(self): global _homes for h in _homes.values(): agents = h.getAgents() for a in agents: self.setupAgent(a) try: a.action() except Exception, e: print '-- Exception --' print '"%s" in "%s" not allow to "%s"' %(a, h, a._action.__name__) print e print self.teardownAgent(a) agents = filter(WanderLust, agents) for a in agents: self.setupAgent(a) try: home = a.getHome() new_home = GreenerPastures(a) home.transportAgent(a, new_home) except Exception, e: print '-- Exception --' print 'moving "%s" from "%s" to "%s"' %(a, h,` new_home`) print e print self.teardownAgent(a) def WanderLust(agent): """ is agent ready to move """ if int(random.random()*100) <= 30: return 1 def GreenerPastures(agent): """ where do they want to go today """ global _homes possible_homes = _homes.keys() possible_homes.remove(agent.getHome().getId()) return _homes.get(random.choice(possible_homes)) # boot strap initial setup. # global list of homes _homes = {} all_homes = ( Sandbox('jail', default_service_factories), Sandbox('origin', default_service_factories), Sandbox('valhalla', default_service_factories) ) origin = all_homes[1] for h in all_homes: _homes[h.getId()]=h agents = [ Agent('odin', None, 'norse legend', action_find_time), Agent('loki', None, 'norse legend', action_find_neighbors), Agent('thor', None, 'norse legend', action_find_homes), Agent('thucydides', None, 'greek men', action_find_time), Agent('archimedes', None, 'greek men', action_find_neighbors), Agent('prometheus', None, 'greek men', action_find_homes), ] for a in agents: origin.addAgent(a) def main(): world = TimeGenerator() for x in range(5): print 'world turning' world.turn() for h in _homes.values(): print h.getId(), h.getAgentIds() if __name__ == '__main__': main() zope.security-3.8.3/src/zope/security/examples/sandbox_security.py0000644000076600000240000001424011637266447025445 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """A small, secure sandbox application. This module is responsible of securing the sandbox application and run it in a secure mode. There are several steps that are taken to set up the security 1. map permissions to actions 2. map authentication tokens/principals onto permissions 3. implement checker and security policies that affect 1,2 4. bind checkers to classes/instances 5. proxy wrap as necessary """ import sandbox from zope.security.interfaces import IParticipation from zope.security import checker, management, simplepolicies from zope.interface import implements # Define all permissions that will be available NotAllowed = 'Not Allowed' Public = checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' AddAgent = 'Add Agent' ALL='All' NoSetAttr = lambda name: NotAllowed class SimulationSecurityDatabase(object): """Security Database In the database, locations are mapped to authentication tokens to permissions. """ origin = { 'any' : [ALL] } jail = { 'norse legend' : [TransportAgent, AccessServices, AccessAgentService, AccessHomeService, TransportAgent, AccessAgents], 'any' : [AccessTimeService, AddAgent] } valhalla = { 'norse legend' : [AddAgent], 'any' : [AccessServices, AccessTimeService, AccessAgentService, AccessHomeService, TransportAgent, AccessAgents] } class SimulationSecurityPolicy(simplepolicies.ParanoidSecurityPolicy): """Security Policy during the Simulation. A very simple security policy that is specific to the simulations. """ def checkPermission(self, permission, object): """See zope.security.interfaces.ISecurityPolicy""" home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if not self.participations: return False for participation in self.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True class AgentParticipation(object): """Agent Participation during the Simulation. A very simple participation that is specific to the simulations. """ implements(IParticipation) def __init__(self, agent): self.principal = agent self.interaction = None def PermissionMapChecker(permissions_map=None, set_permissions=None): """Create a checker from using the 'permission_map.'""" if permissions_map is None: permissions_map = {} if set_permissions is None: set_permissions = {} res = {} for key, value in permissions_map.items(): for method in value: res[method] = key return checker.Checker(res, set_permissions) ################################# # sandbox security settings sandbox_security = { AccessServices : ['getService', 'addService', 'getServiceIds'], AccessAgents : ['getAgentsIds', 'getAgents'], AddAgent : ['addAgent'], TransportAgent : ['transportAgent'], Public : ['getId','getHome'] } sandbox_checker = PermissionMapChecker(sandbox_security) ################################# # service security settings # time service tservice_security = { AccessTimeService:['getTime'] } time_service_checker = PermissionMapChecker(tservice_security) # home service hservice_security = { AccessHomeService:['getAvailableHomes'] } home_service_checker = PermissionMapChecker(hservice_security) # agent service aservice_security = { AccessAgentService:['getLocalAgents'] } agent_service_checker = PermissionMapChecker(aservice_security) def wire_security(): management.setSecurityPolicy(SimulationSecurityPolicy) checker.defineChecker(sandbox.Sandbox, sandbox_checker) checker.defineChecker(sandbox.TimeService, time_service_checker) checker.defineChecker(sandbox.AgentDiscoveryService, agent_service_checker) checker.defineChecker(sandbox.HomeDiscoveryService, home_service_checker) def addAgent(self, agent): if not self._agents.has_key(agent.getId()) \ and sandbox.IAgent.providedBy(agent): self._agents[agent.getId()]=agent agentChecker = checker.selectChecker(self) wrapped_home = agentChecker.proxy(self) agent.setHome(wrapped_home) else: raise sandbox.SandboxError("couldn't add agent %s" %agent) sandbox.Sandbox.addAgent = addAgent def setupAgent(self, agent): management.newInteraction(AgentParticipation(agent)) sandbox.TimeGenerator.setupAgent = setupAgent def teardownAgent(self, agent): management.endInteraction() sandbox.TimeGenerator.teardownAgent = teardownAgent def GreenerPastures(agent): """ where do they want to go today """ import random _homes = sandbox._homes possible_homes = _homes.keys() possible_homes.remove(agent.getHome().getId()) new_home = _homes.get(random.choice(possible_homes)) return checker.selectChecker(new_home).proxy(new_home) sandbox.GreenerPastures = GreenerPastures if __name__ == '__main__': wire_security() sandbox.main() zope.security-3.8.3/src/zope/security/i18n.py0000644000076600000240000000157211637266447021025 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Customization of zope.i18n for the Zope application server """ __docformat__ = 'restructuredtext' # import this as _ to create i18n messages in the zope domain from zope.i18nmessageid import MessageFactory ZopeMessageFactory = MessageFactory('zope') zope.security-3.8.3/src/zope/security/interfaces.py0000644000076600000240000002266511637266447022377 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Interfaces for security machinery. """ from zope.interface import Interface, Attribute, implements from zope.interface.common.interfaces import IException, IAttributeError from zope.schema import Text, TextLine from zope.security.i18n import ZopeMessageFactory as _ class IUnauthorized(IException): pass class Unauthorized(Exception): """Some user wasn't allowed to access a resource""" implements(IUnauthorized) class IForbidden(IException): pass class Forbidden(Exception): """A resource cannot be accessed under any circumstances """ implements(IForbidden) class IForbiddenAttribute(IForbidden, IAttributeError): pass class ForbiddenAttribute(Forbidden, AttributeError): """An attribute is unavailable because it is forbidden (private) """ implements(IForbiddenAttribute) class ISecurityManagement(Interface): """Public security management API.""" def getSecurityPolicy(): """Get the system default security policy.""" def setSecurityPolicy(aSecurityPolicy): """Set the system default security policy. This method should only be called by system startup code. It should never, for example, be called during a web request. """ class ISecurityChecking(Interface): """Public security API.""" def checkPermission(permission, object, interaction=None): """Return whether security policy allows permission on object. Arguments: permission -- A permission name object -- The object being accessed according to the permission interaction -- An interaction, which provides access to information such as authenticated principals. If it is None, the current interaction is used. """ class ISecurityProxyFactory(Interface): def __call__(object, checker=None): """Create a security proxy If a checker is given, then use it, otherwise, try to figure out a checker. If the object is already a security proxy, then it will be returned. """ class IChecker(Interface): """Security-proxy plugin objects that implement low-level checks The checker is responsible for creating proxies for operation return values, via the proxy method. There are check_getattr() and check_setattr() methods for checking getattr and setattr, and a check() method for all other operations. The check methods may raise errors. They return no value. Example (for __getitem__): checker.check(ob, \"__getitem__\") return checker.proxy(ob[key]) """ def check_getattr(ob, name): """Check whether attribute access is allowed. May raise Unauthorized or Forbidden. Returns no value. If a checker implements __setitem__, then __setitem__ will be called rather than check_getattr to check whether an attribute access is allowed. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. """ def check_setattr(ob, name): """Check whether attribute assignment is allowed. May raise Unauthorized or Forbidden. Returns no value. """ def check(ob, operation): """Check whether operation is allowed. The operation name is the Python special method name, e.g. "__getitem__". May raise Unauthorized or Forbidden. Returns no value. If a checker implements __setitem__, then __setitem__ will be called rather than check to chack whether an operation is allowed. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. """ def proxy(value): """Return a security proxy for the value. If a checker implements __getitem__, then __getitem__ will be called rather than proxy to proxy the value. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. """ class INameBasedChecker(IChecker): """Security checker that uses permissions to check attribute access.""" def permission_id(name): """Return the permission used to check attribute access on name. This permission is used by both check and check_getattr. """ def setattr_permission_id(name): """Return the permission used to check attribute assignment on name. This permission is used by check_setattr. """ class ISecurityPolicy(Interface): def __call__(participation=None): """Creates a new interaction for a given request. If participation is not None, it is added to the new interaction. """ class IInteraction(Interface): """A representation of an interaction between some actors and the system. """ participations = Attribute("""An iterable of participations.""") def add(participation): """Add a participation.""" def remove(participation): """Remove a participation.""" def checkPermission(permission, object): """Return whether security context allows permission on object. Arguments: permission -- A permission name object -- The object being accessed according to the permission """ class IParticipation(Interface): interaction = Attribute("The interaction") principal = Attribute("The authenticated principal") class NoInteraction(Exception): """No interaction started """ class IInteractionManagement(Interface): """Interaction management API. Every thread has at most one active interaction at a time. """ def newInteraction(participation=None): """Start a new interaction. If participation is not None, it is added to the new interaction. Raises an error if the calling thread already has an interaction. """ def queryInteraction(): """Return the current interaction. Return None if there is no interaction. """ def getInteraction(): """Return the current interaction. Raise NoInteraction if there isn't a current interaction. """ def endInteraction(): """End the current interaction. Does nothing if there is no interaction. """ class IPrincipal(Interface): """Principals are security artifacts that execute actions in a security environment. The most common examples of principals include user and group objects. It is likely that IPrincipal objects will have associated views used to list principals in management interfaces. For example, a system in which other meta-data are provided for principals might extend IPrincipal and register a view for the extended interface that displays the extended information. We'll probably want to define a standard view name (e.g. 'inline_summary') for this purpose. """ id = TextLine( title=_("Id"), description=_("The unique identification of the principal."), required=True, readonly=True) title = TextLine( title=_("Title"), description=_("The title of the principal. " "This is usually used in the UI."), required=False) description = Text( title=_("Description"), description=_("A detailed description of the principal."), required=False) class IGroupAwarePrincipal(IPrincipal): """Group aware principal interface Extends IPrincipal to contain group information. """ groups = Attribute( 'An iterable of groups to which the principal directly belongs') class IGroupClosureAwarePrincipal(IGroupAwarePrincipal): allGroups = Attribute( "An iterable of the full closure of the principal's groups.") class IGroup(IPrincipal): """Group of principals """ class IMemberGetterGroup(IGroup): """a group that can get its members""" def getMembers(): """return an iterable of the members of the group""" class IMemberAwareGroup(IMemberGetterGroup): """a group that can both set and get its members.""" def setMembers(value): """set members of group to the principal ids in the iterable value""" class IPermission(Interface): """A permission object.""" id = TextLine( title=_("Id"), description=_("Id as which this permission will be known and used."), readonly=True, required=True) title = TextLine( title=_("Title"), description=_("Provides a title for the permission."), required=True) description = Text( title=_("Description"), description=_("Provides a description for the permission."), required=False) zope.security-3.8.3/src/zope/security/management.py0000644000076600000240000001045411637266447022361 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Default 'ISecurityManagement' and 'IInteractionManagement' implementation """ import zope.interface from zope.security import interfaces from zope.security.checker import CheckerPublic from zope.security._definitions import thread_local, system_user from zope.security.simplepolicies import ParanoidSecurityPolicy _defaultPolicy = ParanoidSecurityPolicy zope.interface.moduleProvides( interfaces.ISecurityManagement, interfaces.IInteractionManagement) def _clear(): global _defaultPolicy _defaultPolicy = ParanoidSecurityPolicy # XXX This code is used to support automated testing. However, it shouldn't be # here and needs to be refactored. The empty addCleanUp-method is a temporary # workaround to fix packages that depend on zope.security but don't have a # need for zope.testing. try: from zope.testing.cleanup import addCleanUp except ImportError: def addCleanUp(arg): pass addCleanUp(_clear) # # ISecurityManagement implementation # def getSecurityPolicy(): """Get the system default security policy.""" return _defaultPolicy def setSecurityPolicy(aSecurityPolicy): """Set the system default security policy, and return the previous value. This method should only be called by system startup code. It should never, for example, be called during a web request. """ global _defaultPolicy last, _defaultPolicy = _defaultPolicy, aSecurityPolicy return last # # IInteractionManagement implementation # def queryInteraction(): return getattr(thread_local, 'interaction', None) def getInteraction(): """Get the current interaction.""" try: return thread_local.interaction except AttributeError: raise interfaces.NoInteraction def newInteraction(*participations): """Start a new interaction.""" if queryInteraction() is not None: raise AssertionError("newInteraction called" " while another interaction is active.") interaction = getSecurityPolicy()(*participations) thread_local.interaction = interaction def endInteraction(): """End the current interaction.""" try: thread_local.previous_interaction = thread_local.interaction except AttributeError: # if someone does a restore later, it should be restored to not having # an interaction. If there was a previous interaction from a previous # call to endInteraction, it should be removed. try: del thread_local.previous_interaction except AttributeError: pass else: del thread_local.interaction def restoreInteraction(): try: previous = thread_local.previous_interaction except AttributeError: try: del thread_local.interaction except AttributeError: pass else: thread_local.interaction = previous def checkPermission(permission, object, interaction=None): """Return whether security policy allows permission on object. Arguments: permission -- A permission name object -- The object being accessed according to the permission interaction -- An interaction, which provides access to information such as authenticated principals. If it is None, the current interaction is used. checkPermission is guaranteed to return True if permission is CheckerPublic or None. """ if permission is CheckerPublic or permission is None: return True if interaction is None: try: interaction = thread_local.interaction except AttributeError: raise interfaces.NoInteraction return interaction.checkPermission(permission, object) addCleanUp(endInteraction) zope.security-3.8.3/src/zope/security/meta.zcml0000644000076600000240000000365411637266447021514 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope/security/metaconfigure.py0000644000076600000240000001777011637266447023105 0ustar macstaff00000000000000############################################################################# # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """ Register class directive. """ __docformat__ = 'restructuredtext' from types import ModuleType from zope.component.interfaces import IFactory from zope.component.factory import Factory from zope.component.interface import provideInterface from zope.component.zcml import utility from zope.interface import classImplements from zope.schema.interfaces import IField from zope.configuration.exceptions import ConfigurationError from zope.security.checker import moduleChecker, Checker, defineChecker from zope.security.checker import CheckerPublic from zope.security.protectclass import protectLikeUnto, protectName from zope.security.protectclass import protectSetAttribute PublicPermission = 'zope.Public' def dottedName(klass): if klass is None: return 'None' return klass.__module__ + '.' + klass.__name__ class ProtectionDeclarationException(Exception): """Security-protection-specific exceptions.""" pass class ClassDirective(object): def __init__(self, _context, class_): self.__id = dottedName(class_) self.__class = class_ if isinstance(self.__class, ModuleType): raise ConfigurationError('Content class attribute must be a class') self.__context = _context def implements(self, _context, interface): for interface in interface: _context.action( discriminator = ( 'ContentDirective', self.__class, object()), callable = classImplements, args = (self.__class, interface), ) _context.action( discriminator = None, callable = provideInterface, args = (interface.__module__ + '.' + interface.getName(), interface) ) def require(self, _context, permission=None, attributes=None, interface=None, like_class=None, set_attributes=None, set_schema=None): """Require a permission to access a specific aspect""" if like_class: self.__mimic(_context, like_class) if not (interface or attributes or set_attributes or set_schema): if like_class: return raise ConfigurationError("Nothing required") if not permission: raise ConfigurationError("No permission specified") if interface: for i in interface: if i: self.__protectByInterface(i, permission) if attributes: self.__protectNames(attributes, permission) if set_attributes: self.__protectSetAttributes(set_attributes, permission) if set_schema: for s in set_schema: self.__protectSetSchema(s, permission) def __mimic(self, _context, class_): """Base security requirements on those of the given class""" _context.action( discriminator=('mimic', self.__class, object()), callable=protectLikeUnto, args=(self.__class, class_), ) def allow(self, _context, attributes=None, interface=None): """Like require, but with permission_id zope.Public""" return self.require(_context, PublicPermission, attributes, interface) def __protectByInterface(self, interface, permission_id): "Set a permission on names in an interface." for n, d in interface.namesAndDescriptions(1): self.__protectName(n, permission_id) self.__context.action( discriminator = None, callable = provideInterface, args = (interface.__module__ + '.' + interface.getName(), interface) ) def __protectName(self, name, permission_id): "Set a permission on a particular name." self.__context.action( discriminator = ('protectName', self.__class, name), callable = protectName, args = (self.__class, name, permission_id) ) def __protectNames(self, names, permission_id): "Set a permission on a bunch of names." for name in names: self.__protectName(name, permission_id) def __protectSetAttributes(self, names, permission_id): "Set a permission on a bunch of names." for name in names: self.__context.action( discriminator = ('protectSetAttribute', self.__class, name), callable = protectSetAttribute, args = (self.__class, name, permission_id) ) def __protectSetSchema(self, schema, permission_id): "Set a permission on a bunch of names." _context = self.__context for name in schema: field = schema[name] if IField.providedBy(field) and not field.readonly: _context.action( discriminator = ('protectSetAttribute', self.__class, name), callable = protectSetAttribute, args = (self.__class, name, permission_id) ) _context.action( discriminator = None, callable = provideInterface, args = (schema.__module__ + '.' + schema.getName(), schema) ) def __call__(self): "Handle empty/simple declaration." return () def factory(self, _context, id=None, title="", description=''): """Register a zmi factory for this class""" id = id or self.__id factoryObj = Factory(self.__class, title, description) # note factories are all in one pile, utilities and content, # so addable names must also act as if they were all in the # same namespace, despite the utilities/content division utility(_context, IFactory, factoryObj, permission=PublicPermission, name=id) def protectModule(module, name, permission): """Set up a module checker to require a permission to access a name If there isn't a checker for the module, create one. """ checker = moduleChecker(module) if checker is None: checker = Checker({}, {}) defineChecker(module, checker) if permission == 'zope.Public': # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary get method was used because we set it protections = checker.get_permissions protections[name] = permission def _names(attributes, interfaces): seen = {} for name in attributes: if not name in seen: seen[name] = 1 yield name for interface in interfaces: for name in interface: if not name in seen: seen[name] = 1 yield name def allow(context, attributes=(), interface=()): for name in _names(attributes, interface): context.action( discriminator=('http://namespaces.zope.org/zope:module', context.module, name), callable = protectModule, args = (context.module, name, 'zope.Public'), ) def require(context, permission, attributes=(), interface=()): for name in _names(attributes, interface): context.action( discriminator=('http://namespaces.zope.org/zope:module', context.module, name), callable = protectModule, args = (context.module, name, permission), ) zope.security-3.8.3/src/zope/security/metadirectives.py0000644000076600000240000001530211637266447023252 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Component architecture related 'zope' ZCML namespace directive interfaces """ __docformat__ = 'restructuredtext' import zope.configuration.fields from zope.configuration.fields import GlobalObject, GlobalInterface from zope.configuration.fields import Tokens, PythonIdentifier import zope.interface import zope.schema from zope.interface import Interface import zope.security.zcml from zope.security.i18n import ZopeMessageFactory as _ from zope.security.zcml import Permission class IClassDirective(zope.interface.Interface): """Make statements about a class""" class_ = zope.configuration.fields.GlobalObject( title=_("Class"), required=True ) class IImplementsSubdirective(zope.interface.Interface): """Declare that the class given by the content directive's class attribute implements a given interface """ interface = zope.configuration.fields.Tokens( title=_("One or more interfaces"), required=True, value_type=zope.configuration.fields.GlobalInterface() ) class IRequireSubdirective(zope.interface.Interface): """Indicate that the a specified list of names or the names in a given Interface require a given permission for access. """ permission = zope.security.zcml.Permission( title=_("Permission"), description=_(""" Specifies the permission by id that will be required to access or mutate the attributes and methods specified."""), required=False, ) attributes = zope.configuration.fields.Tokens( title=_("Attributes and methods"), description=_("This is a list of attributes and methods" " that can be accessed."), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) set_attributes = zope.configuration.fields.Tokens( title=_("Attributes that can be set"), description=_("This is a list of attributes that can be" " modified/mutated."), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) interface = zope.configuration.fields.Tokens( title=_("Interfaces"), description=_("The listed interfaces' methods and attributes" " can be accessed."), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) set_schema = zope.configuration.fields.Tokens( title=_("The attributes specified by the schema can be set"), description=_("The listed schemas' properties can be" " modified/mutated."), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) like_class = zope.configuration.fields.GlobalObject( title=_("Configure like this class"), description=_(""" This argument says that this content class should be configured in the same way the specified class' security is. If this argument is specified, no other argument can be used."""), required=False, ) class IAllowSubdirective(zope.interface.Interface): """ Declare a part of the class to be publicly viewable (that is, requires the zope.Public permission). Only one of the following two attributes may be used. """ attributes = zope.configuration.fields.Tokens( title=_("Attributes"), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) interface = zope.configuration.fields.Tokens( title=_("Interface"), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) class IFactorySubdirective(zope.interface.Interface): """Specify the factory used to create this content object""" id = zope.schema.Id( title=_("ID"), description=_(""" the identifier for this factory in the ZMI factory identification scheme. If not given, defaults to the literal string given as the content directive's 'class' attribute."""), required=False, ) title = zope.configuration.fields.MessageID( title=_("Title"), description=_("Text suitable for use in the 'add content' menu" " of a management interface"), required=False, ) description = zope.configuration.fields.MessageID( title=_("Description"), description=_("Longer narrative description of what this" " factory does"), required=False, ) class IModule(Interface): """Group security declarations about a module""" module = GlobalObject( title=u"Module", description=u"Pointer to the module object.", required=True) class IAllow(Interface): """Allow access to selected module attributes Access is unconditionally allowed to any names provided directly in the attributes attribute or to any names defined by interfaces listed in the interface attribute. """ attributes = Tokens( title=u"Attributes", description=u"The attributes to provide access to.", value_type = PythonIdentifier(), required=False) interface = Tokens( title=u"Interface", description=u"Interfaces whos names to provide access to. Access " u"will be provided to all of the names defined by the " u"interface(s). Multiple interfaces can be supplied.", value_type = GlobalInterface(), required=False) class IRequire(Interface): """Require a permission to access selected module attributes The given permission is required to access any names provided directly in the attributes attribute or any names defined by interfaces listed in the interface attribute. """ attributes = Tokens( title=u"Attributes", description=u"The attributes to require permission for.", value_type = PythonIdentifier(), required=False) permission = Permission( title=u"Permission ID", description=u"The id of the permission to require.") zope.security-3.8.3/src/zope/security/permission.py0000644000076600000240000001362511637266447022440 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Permissions """ __docformat__ = "reStructuredText" from zope.interface import implements, directlyProvides from zope.component import queryUtility, getUtilitiesFor from zope.schema.interfaces import IVocabularyFactory from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm from zope.security.checker import CheckerPublic from zope.security.interfaces import IPermission class Permission(object): implements(IPermission) def __init__(self, id, title="", description=""): self.id = id self.title = title self.description = description def checkPermission(context, permission_id): """Check whether a given permission exists in the provided context. >>> from zope.component import provideUtility >>> provideUtility(Permission('x'), IPermission, 'x') >>> checkPermission(None, 'x') >>> checkPermission(None, 'y') Traceback (most recent call last): ... ValueError: ('Undefined permission id', 'y') The CheckerPublic always exists: >>> checkPermission(None, CheckerPublic) """ if permission_id is CheckerPublic: return if not queryUtility(IPermission, permission_id, context=context): raise ValueError("Undefined permission id", permission_id) def allPermissions(context=None): """Get the ids of all defined permissions >>> from zope.component import provideUtility >>> provideUtility(Permission('x'), IPermission, 'x') >>> provideUtility(Permission('y'), IPermission, 'y') >>> ids = list(allPermissions(None)) >>> ids.sort() >>> ids [u'x', u'y'] """ for id, permission in getUtilitiesFor(IPermission, context): if id != u'zope.Public': yield id def PermissionsVocabulary(context=None): """A vocabulary of permission IDs. Term values are permissions, while term tokens are permission IDs. To illustrate, we need to register the permission IDs vocabulary: >>> from zope.schema.vocabulary import _clear >>> _clear() >>> from zope.schema.vocabulary import getVocabularyRegistry >>> registry = getVocabularyRegistry() >>> registry.register('Permissions', PermissionsVocabulary) We also need to register some sample permission utilities: >>> from zope.security.interfaces import IPermission >>> from zope.security.permission import Permission >>> from zope.component import provideUtility >>> a = Permission('a') >>> b = Permission('b') >>> provideUtility(a, IPermission, 'a') >>> provideUtility(b, IPermission, 'b') We can now lookup these permissions using the vocabulary: >>> vocab = registry.get(None, 'Permissions') >>> vocab.getTermByToken('a').value is a True >>> vocab.getTermByToken('b').value is b True """ terms = [] for id, permission in getUtilitiesFor(IPermission, context): terms.append(SimpleTerm(permission, id)) return SimpleVocabulary(terms) directlyProvides(PermissionsVocabulary, IVocabularyFactory) def PermissionIdsVocabulary(context=None): """A vocabulary of permission IDs. Term values are the permission ID strings except for 'zope.Public', which is the global permission CheckerPublic. Term titles are the permission ID strings except for 'zope.Public', which is shortened to 'Public'. Terms are sorted by title except for 'Public', which always appears as the first term. To illustrate, we need to register the permission IDs vocabulary: >>> from zope.schema.vocabulary import _clear >>> _clear() >>> from zope.schema.vocabulary import getVocabularyRegistry >>> registry = getVocabularyRegistry() >>> registry.register('Permission Ids', PermissionIdsVocabulary) We also need to register some sample permission utilities, including the special permission 'zope.Public': >>> from zope.security.interfaces import IPermission >>> from zope.security.permission import Permission >>> from zope.component import provideUtility >>> provideUtility(Permission('zope.Public'), IPermission, 'zope.Public') >>> provideUtility(Permission('b'), IPermission, 'b') >>> provideUtility(Permission('a'), IPermission, 'a') We can now lookup these permissions using the vocabulary: >>> vocab = registry.get(None, 'Permission Ids') The non-public permissions 'a' and 'b' are string values: >>> vocab.getTermByToken('a').value u'a' >>> vocab.getTermByToken('b').value u'b' However, the public permission value is CheckerPublic: >>> vocab.getTermByToken('zope.Public').value is CheckerPublic True and its title is shortened: >>> vocab.getTermByToken('zope.Public').title u'Public' The terms are sorted by title except for the public permission, which is listed first: >>> [term.title for term in vocab] [u'Public', u'a', u'b'] """ terms = [] for name, permission in getUtilitiesFor(IPermission, context): if name == 'zope.Public': terms.append(SimpleTerm(CheckerPublic, 'zope.Public', u'Public')) else: terms.append(SimpleTerm(name, name, name)) terms.sort(cmp=lambda lhs, rhs: \ (lhs.token == 'zope.Public' and -1) or cmp(lhs.title, rhs.title)) return SimpleVocabulary(terms) directlyProvides(PermissionIdsVocabulary, IVocabularyFactory) zope.security-3.8.3/src/zope/security/permissions.zcml0000644000076600000240000000177311637266447023141 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope/security/protectclass.py0000644000076600000240000000644211637266447022755 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Make assertions about permissions needed to access class instances attributes """ from zope.security.checker import defineChecker, getCheckerForInstancesOf from zope.security.checker import Checker, CheckerPublic def protectName(class_, name, permission): """Set a permission on a particular name.""" checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) if permission == 'zope.Public': # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary was used because we set it protections = checker.get_permissions protections[name] = permission def protectSetAttribute(class_, name, permission): """Set a permission on a particular name.""" checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) if permission == 'zope.Public': # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary was used because we set it # Note however, that if a checker was created manually # and the caller used say NamesChecker or MultiChecker, # then set_permissions may be None here as Checker # defaults a missing set_permissions parameter to None. # Jim says this doensn't happens with the C version of the # checkers because they use a 'shared dummy dict'. protections = checker.set_permissions protections[name] = permission def protectLikeUnto(class_, like_unto): """Use the protections from like_unto for class_""" unto_checker = getCheckerForInstancesOf(like_unto) if unto_checker is None: return # We know a dictionary was used because we set it # Note however, that if a checker was created manually # and the caller used say NamesChecker or MultiChecker, # then set_permissions may be None here as Checker # defaults a missing set_permissions parameter to None. # Jim says this doensn't happens with the C version of the # checkers because they use a 'shared dummy dict'. unto_get_protections = unto_checker.get_permissions unto_set_protections = unto_checker.set_permissions checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) get_protections = checker.get_permissions for name in unto_get_protections: get_protections[name] = unto_get_protections[name] set_protections = checker.set_permissions for name in unto_set_protections: set_protections[name] = unto_set_protections[name] zope.security-3.8.3/src/zope/security/proxy.py0000644000076600000240000000431111637266447021421 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Helper functions for Proxies. """ __docformat__ = 'restructuredtext' from zope.security._proxy import getChecker, getObject from zope.security._proxy import _Proxy as Proxy # We need the injection of DecoratedSecurityCheckerDescriptor into # zope.location's LocationProxy as soon someone uses security proxies by # importing zope.security.proxy: import zope.security.decorator removeSecurityProxy = getObject # This import represents part of the API for this module from zope.security.checker import ProxyFactory def getTestProxyItems(proxy): """Try to get checker names and permissions for testing If this succeeds, a sorted sequence of items is returned, otherwise, None is returned. """ checker = getChecker(proxy) items = checker.get_permissions.items() items.sort() return items builtin_isinstance = isinstance def isinstance(object, cls): """Test whether an object is an instance of a type. This works even if the object is security proxied: >>> class C1(object): ... pass >>> c = C1() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True >>> class C2(C1): ... pass >>> c = C2() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True """ # The removeSecurityProxy call is OK here because it is *only* # being used for isinstance return builtin_isinstance(removeSecurityProxy(object), cls) zope.security-3.8.3/src/zope/security/README.txt0000644000076600000240000002614311637266447021373 0ustar macstaff00000000000000============== Zope3 Security ============== Introduction ------------ The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions ----------- Principal ~~~~~~~~~ A generalization of a concept of a user. Permission ~~~~~~~~~~ A kind of access, i.e. permission to READ vs. permission to WRITE. Fundamentally the whole security framework is organized around checking permissions on objects. Purpose ------- The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and implicit security checks on attribute access for objects. Attribute names are mapped onto permission names when checking access and the implementation of the security check is defined by the security policy, which receives the object, the permission name, and an interaction. Interactions are objects that represent the use of the system by one or more principals. An interaction contains a list of participations, which represents the way a single principal participates in the interaction. An HTTP request is one example of a participation. Its important to keep in mind that the policy provided is just a default, and it can be substituted with one which doesn't care about principals or interactions at all. Framework Components -------------------- Low Level Components ~~~~~~~~~~~~~~~~~~~~ These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. Checkers ~~~~~~~~ A checker is associated with an object kind, and provides the hooks that map attribute checks onto permissions deferring to the security manager (which in turn defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. There are several implementation variants of checkers, such as checkers that grant access based on attribute names. Proxies ~~~~~~~ Wrappers around Python objects that implicitly guard access to their wrapped contents by delegating to their associated checker. Proxies are also viral in nature, in that values returned by proxies are also proxied. High Level Components --------------------- Security Management ~~~~~~~~~~~~~~~~~~~ Provides accessors for setting up interactions and the global security policy. Interaction ~~~~~~~~~~~ Stores transient information on the list of participations. Participation ~~~~~~~~~~~~~ Stores information about a principal participating in the interaction. Security Policy ~~~~~~~~~~~~~~~ Provides a single method that accepts the object, the permission, and the interaction of the access being checked and is used to implement the application logic for the security framework. Narrative (agent sandbox) ------------------------- As an example we take a look at constructing a multi-agent distributed system, and then adding a security layer using the Zope security model onto it. Scenario ~~~~~~~~ Our agent simulation consists of autonomous agents that live in various agent homes/sandboxes and perform actions that access services available at their current home. Agents carry around authentication tokens which signify their level of access within any given home. Additionally agents attempt to migrate from home to home randomly. The agent simulation was constructed separately from any security aspects. Now we want to define and integrate a security model into the simulation. The full code for the simulation and the security model is available separately; we present only relevant code snippets here for illustration as we go through the implementation process. For the agent simulation we want to add a security model such that we group agents into two authentication groups, "norse legends", including the principals thor, odin, and loki, and "greek men", including prometheus, archimedes, and thucydides. We associate permissions with access to services and homes. We differentiate the homes such that certain authentication groups only have access to services or the home itself based on the local settings of the home in which they reside. We define the homes/sandboxes - origin - all agents start here, and have access to all services here. - valhalla - only agents in the authentication group 'norse legend' can reside here. - jail - all agents can come here, but only 'norse legend's can leave or access services. Process ~~~~~~~ Loosely we define a process for implementing this security model - mapping permissions onto actions - mapping authentication tokens onto permissions - implementing checkers and security policies that use our authentication tokens and permissions. - binding checkers to our simulation classes - inserting the hooks into the original simulation code to add proxy wrappers to automatically check security. - inserting hooks into the original simulation to register the agents as the active principal in an interaction. Defining a Permission Model ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We define the following permissions:: NotAllowed = 'Not Allowed' Public = Checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' and create a dictionary database mapping homes to authentication groups which are linked to associated permissions. Defining and Binding Checkers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Checkers are the foundational unit for the security framework. They define what attributes can be accessed or set on a given instance. They can be used implicitly via Proxy objects, to guard all attribute access automatically or explicitly to check a given access for an operation. Checker construction expects two functions or dictionaries, one is used to map attribute names to permissions for attribute access and another to do the same for setting attributes. We use the following checker factory function:: def PermissionMapChecker(permissions_map={}, setattr_permission_func=NoSetAttr): res = {} for k,v in permissions_map.items(): for iv in v: res[iv]=k return checker.Checker(res.get, setattr_permission_func) time_service_checker = PermissionMapChecker( # permission : [methods] {'AccessTimeService':['getTime']} ) with the NoSetAttr function defined as a lambda which always return the permission `NotAllowed`. To bind the checkers to the simulation classes we register our checkers with the security model's global checker registry:: import sandbox_simulation from zope.security.checker import defineChecker defineChecker(sandbox_simulation.TimeService, time_service_checker) Defining a Security Policy ~~~~~~~~~~~~~~~~~~~~~~~~~~ We implement our security policy such that it checks the current agent's authentication token against the given permission in the home of the object being accessed:: class SimulationSecurityPolicy: implements(ISecurityPolicy) createInteraction = staticmethod(simpleinteraction.createInteraction) def checkPermission(self, permission, object, interaction): home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if interaction is None: return False if not interaction.participations: return False for participation in interaction.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True There are no specific requirements for the interaction class, so we can just use `zope.security.simpleinteraction.Interaction`. Since an interaction can have more than one principal, we check that *all* of them are given the necessary permission. This is not really necessary since we only create interactions with a single active principal. There is some additional code present to allow for shortcuts in defining the permission database when defining permissions for all auth groups and all permissions. Integration ~~~~~~~~~~~ At this point we have implemented our security model, and we need to integrate it with our simulation model. We do so in three separate steps. First we make it such that agents only access homes that are wrapped in a security proxy. By doing this all access to homes and services (proxies have proxied return values for their methods) is implicitly guarded by our security policy. The second step is that we want to associate the active agent with the security context so the security policy will know which agent's authentication token to validate against. The third step is to set our security policy as the default policy for the Zope security framework. It is possible to create custom security policies at a finer grained than global, but such is left as an exercise for the reader. Interaction Access ~~~~~~~~~~~~~~~~~~ The *default* implementation of the interaction management interfaces defines interactions on a per thread basis with a function for an accessor. This model is not appropriate for all systems, as it restricts one to a single active interaction per thread at any given moment. Reimplementing the interaction access methods though is easily doable and is noted here for completeness. Perspectives ~~~~~~~~~~~~ It's important to keep in mind that there is a lot more that is possible using the security framework than what's been presented here. All of the interactions are interface based, such that if you need to re-implement the semantics to suite your application a new implementation of the interface will be sufficient. Additional possibilities range from restricted interpreters and dynamic loading of untrusted code to non Zope web application security systems. Insert imagination here ;-). Zope Perspective ~~~~~~~~~~~~~~~~ A Zope3 programmer will never commonly need to interact with the low level security framework. Zope3 defines a second security package over top the low level framework and authentication sources and checkers are handled via zcml registration. Still those developing Zope3 will hopefully find this useful as an introduction into the underpinnings of the security framework. Code ~~~~ The complete code for this example is available. - sandbox.py - the agent framework - sandbox_security.py - the security implementation and binding to the agent framework. Authors ~~~~~~~ - Kapil Thangavelu - Guido Wesdorp - Marius Gedminas zope.security-3.8.3/src/zope/security/setup.py0000644000076600000240000000147111637266447021404 0ustar macstaff00000000000000#! /usr/bin/env python ############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Security setup """ from distutils.core import setup, Extension setup(name="_Proxy", version = "0.1", ext_modules=[Extension("_Proxy", ["_Proxy.c"])]) zope.security-3.8.3/src/zope/security/simplepolicies.py0000644000076600000240000000426611637266447023272 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Simple 'ISecurityPolicy' implementations. """ import zope.interface from zope.security.checker import CheckerPublic from zope.security.interfaces import IInteraction, ISecurityPolicy from zope.security._definitions import system_user class ParanoidSecurityPolicy(object): zope.interface.implements(IInteraction) zope.interface.classProvides(ISecurityPolicy) def __init__(self, *participations): self.participations = [] for participation in participations: self.add(participation) def add(self, participation): if participation.interaction is not None: raise ValueError("%r already belongs to an interaction" % participation) participation.interaction = self self.participations.append(participation) def remove(self, participation): if participation.interaction is not self: raise ValueError("%r does not belong to this interaction" % participation) self.participations.remove(participation) participation.interaction = None def checkPermission(self, permission, object): if permission is CheckerPublic: return True users = [p.principal for p in self.participations if p.principal is not system_user] return not users class PermissiveSecurityPolicy(ParanoidSecurityPolicy): """Allow all access.""" zope.interface.classProvides(ISecurityPolicy) def checkPermission(self, permission, object): return True zope.security-3.8.3/src/zope/security/testing.py0000644000076600000240000000354311637266447021723 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Testing support code This module provides some helper/stub objects for setting up interactions. """ from zope import interface, component from zope.security import interfaces from zope.security.permission import Permission class Principal: interface.implements(interfaces.IPrincipal) def __init__(self, id, title=None, description='', groups=None): self.id = id self.title = title or id self.description = description if groups is not None: self.groups = groups interface.directlyProvides(self, interfaces.IGroupAwarePrincipal) class Participation: interface.implements(interfaces.IParticipation) def __init__(self, principal): self.principal = principal self.interaction = None def addCheckerPublic(): """Add the CheckerPublic permission as 'zope.Public'""" perm = Permission('zope.Public', 'Public', """Special permission used for resources that are always public The public permission is effectively an optimization, sine it allows security computation to be bypassed. """ ) gsm = component.getGlobalSiteManager() gsm.registerUtility(perm, interfaces.IPermission, perm.id) zope.security-3.8.3/src/zope/security/tests/0000755000076600000240000000000011637266524021025 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/security/tests/__init__.py0000644000076600000240000000007511637266447023144 0ustar macstaff00000000000000# # This file is necessary to make this directory a package. zope.security-3.8.3/src/zope/security/tests/adapter.py0000644000076600000240000000270711637266447023031 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Sample adapter class for testing """ import zope.interface import zope.component import components class I1(zope.interface.Interface): pass class I2(zope.interface.Interface): pass class I3(zope.interface.Interface): def f1(): pass def f2(): pass def f3(): pass class IS(zope.interface.Interface): pass class Adapter(object): def __init__(self, *args): self.context = args class A1(Adapter): zope.interface.implements(I1) class A2(Adapter): zope.interface.implements(I2) class A3(Adapter): zope.component.adapts(components.IContent, I1, I2) zope.interface.implements(I3) class A4: pass a4 = A4() class A5: zope.interface.implements(I1, I2) a5 = A5() def Handler(content, *args): # uninteresting handler content.args = getattr(content, 'args', ()) + (args, ) zope.security-3.8.3/src/zope/security/tests/components.py0000644000076600000240000000220611637266447023570 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Components for testing """ from zope.interface import Interface, Attribute, implements from zope.component import adapts class IAppb(Interface): a = Attribute('test attribute') def f(): "test func" class IApp(IAppb): pass class IContent(Interface): pass class Content(object): implements(IContent) class Comp(object): adapts(IContent) implements(IApp) def __init__(self, *args): # Ignore arguments passed to constructor pass a = 1 def f(): pass comp = Comp() zope.security-3.8.3/src/zope/security/tests/configure.txt0000644000076600000240000000116311637266447023554 0ustar macstaff00000000000000Package configuration ===================== The ``zope.security`` package provides a ZCML file that configures some utilities and a couple of permissions: >>> from zope.configuration.xmlconfig import XMLConfig >>> import zope.security >>> XMLConfig('permissions.zcml', zope.security)() >>> len(list(zope.component.getGlobalSiteManager().registeredUtilities())) 7 Clear the current state: >>> from zope.component.testing import setUp, tearDown >>> tearDown() >>> setUp() >>> XMLConfig('configure.zcml', zope.security)() >>> len(list(zope.component.getGlobalSiteManager().registeredUtilities())) 10 zope.security-3.8.3/src/zope/security/tests/emptymodule.py0000644000076600000240000000154111637266447023750 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """This empty module is for containing objects used in the course of tests. (There is a problem with the way the unit tests interact with the modules being tests, so the objects can't be expected to show up in place.) """ zope.security-3.8.3/src/zope/security/tests/exampleclass.py0000644000076600000240000000153411637266447024067 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Example test classes """ from zope.interface import Interface class ExampleClass(object): pass class IExample(Interface): pass class IExample2(Interface): pass class IExampleContainer(Interface): pass zope.security-3.8.3/src/zope/security/tests/module.py0000644000076600000240000000272111637266447022672 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Preliminaries to hookup a test suite with the external TestModule. This is necessary because the test framework interferes with seeing changes in the running modules via the module namespace. This enables having some subject classes, instances, permissions, etc, that don't live in the test modules, themselves. """ from zope.interface import Interface from zope.schema import Text class I(Interface): def m1(): pass def m2(): pass class I2(I): def m4(): pass class I3(Interface): def m3(): pass class I4(Interface): def m2(): pass class S(Interface): foo = Text() bar = Text() baro = Text(readonly=True) class S2(Interface): foo2 = Text() bar2 = Text() template_bracket = """ %s """ zope.security-3.8.3/src/zope/security/tests/modulehookup.py0000644000076600000240000000215711637266447024123 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Preliminaries to hookup a test suite with the external TestModule. This is necessary because the test framework interferes with seeing changes in the running modules via the module namespace. This enables having some subject classes, instances, permissions, etc, that don't live in the test modules, themselves. """ from zope.interface import Interface from zope.security.tests import emptymodule as TestModule class I(Interface): def m1(): pass def m2(): pass zope.security-3.8.3/src/zope/security/tests/redefineperms.zcml0000644000076600000240000000131311637266447024546 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope/security/tests/test_adapter.py0000644000076600000240000000143311637266447024063 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## import unittest from doctest import DocTestSuite def test_suite(): return unittest.TestSuite(( DocTestSuite('zope.security.adapter'), )) zope.security-3.8.3/src/zope/security/tests/test_checker.py0000644000076600000240000005705711637266447024064 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Security Checker tests """ from unittest import TestCase, TestSuite, main, makeSuite from zope.interface import implements from zope.interface.verify import verifyObject from zope.testing.cleanup import CleanUp from zope.proxy import getProxiedObject from zope.security.interfaces import ISecurityPolicy, Unauthorized from zope.security.interfaces import Forbidden, ForbiddenAttribute from zope.security.management import setSecurityPolicy, newInteraction from zope.security.management import endInteraction, getInteraction from zope.security.proxy import removeSecurityProxy, getChecker, Proxy from zope.security.checker import defineChecker, undefineChecker, ProxyFactory from zope.security.checker import canWrite, canAccess from zope.security.checker import Checker, NamesChecker, CheckerPublic from zope.security.checker import BasicTypes, _checkers, NoProxy, _clear import types, pickle class SecurityPolicy(object): implements(ISecurityPolicy) def checkPermission(self, permission, object): 'See ISecurityPolicy' return permission == 'test_allowed' class RecordedSecurityPolicy(object): implements(ISecurityPolicy) def __init__(self): self._checked = [] self.permissions = {} def checkPermission(self, permission, object): 'See ISecurityPolicy' self._checked.append(permission) return self.permissions.get(permission, True) def checkChecked(self, checked): res = self._checked == checked self._checked = [] return res class TransparentProxy(object): def __init__(self, ob): self._ob = ob def __getattribute__(self, name): ob = object.__getattribute__(self, '_ob') return getattr(ob, name) class OldInst: __metaclass__ = types.ClassType a = 1 def b(self): pass c = 2 def gete(self): return 3 e = property(gete) def __getitem__(self, x): return 5, x def __setitem__(self, x, v): pass class NewInst(object, OldInst): # This is not needed, but left in to show the change of metaclass # __metaclass__ = type def gete(self): return 3 def sete(self, v): pass e = property(gete, sete) class Test(TestCase, CleanUp): def setUp(self): CleanUp.setUp(self) self.__oldpolicy = setSecurityPolicy(SecurityPolicy) newInteraction() def tearDown(self): endInteraction() setSecurityPolicy(self.__oldpolicy) CleanUp.tearDown(self) def test_typesAcceptedByDefineChecker(self): class ClassicClass: __metaclass__ = types.ClassType class NewStyleClass: __metaclass__ = type import zope.security not_a_type = object() defineChecker(ClassicClass, NamesChecker()) defineChecker(NewStyleClass, NamesChecker()) defineChecker(zope.security, NamesChecker()) self.assertRaises(TypeError, defineChecker, not_a_type, NamesChecker()) # check_getattr cases: # # - no attribute there # - method # - allow and disallow by permission def test_check_getattr(self): oldinst = OldInst() oldinst.d = OldInst() newinst = NewInst() newinst.d = NewInst() for inst in oldinst, newinst: checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'perm') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'a') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'b') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'c') self.assertRaises(Unauthorized, checker.check, inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'test_allowed') checker.check_getattr(inst, 'a') checker.check_getattr(inst, 'b') checker.check_getattr(inst, 'c') checker.check(inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') checker = NamesChecker(['a', 'b', 'c', '__getitem__'], CheckerPublic) checker.check_getattr(inst, 'a') checker.check_getattr(inst, 'b') checker.check_getattr(inst, 'c') checker.check(inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') def test_check_setattr(self): oldinst = OldInst() oldinst.d = OldInst() newinst = NewInst() newinst.d = NewInst() for inst in oldinst, newinst: checker = Checker({}, {'a': 'perm', 'z': 'perm'}) self.assertRaises(Unauthorized, checker.check_setattr, inst, 'a') self.assertRaises(Unauthorized, checker.check_setattr, inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'c') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': 'test_allowed', 'z': 'test_allowed'}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': CheckerPublic, 'z': CheckerPublic}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') def test_proxy(self): checker = NamesChecker(()) from zope.security.checker import BasicTypes_examples rocks = tuple(BasicTypes_examples.values()) for rock in rocks: proxy = checker.proxy(rock) self.failUnless(proxy is rock, (rock, type(proxy))) for class_ in OldInst, NewInst: inst = class_() for ob in inst, class_: proxy = checker.proxy(ob) self.failUnless(removeSecurityProxy(proxy) is ob) checker = getChecker(proxy) if ob is inst: self.assertEqual(checker.permission_id('__str__'), None) else: self.assertEqual(checker.permission_id('__str__'), CheckerPublic) #No longer doing anything special for transparent proxies. #A proxy needs to provide its own security checker. # #special = NamesChecker(['a', 'b'], 'test_allowed') #defineChecker(class_, special) # #for ob in inst, TransparentProxy(inst): # proxy = checker.proxy(ob) # self.failUnless(removeSecurityProxy(proxy) is ob) # # checker = getChecker(proxy) # self.failUnless(checker is special, # checker.get_permissions) # # 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 class I1(Interface): def f1(): '' def f2(): '' class I2(I1): def f3(): '' def f4(): '' class I3(Interface): def g(): '' from zope.exceptions import DuplicationError from zope.security.checker import MultiChecker self.assertRaises(DuplicationError, MultiChecker, [(I1, 'p1'), (I2, 'p2')]) self.assertRaises(DuplicationError, MultiChecker, [(I1, 'p1'), {'f2': 'p2'}]) MultiChecker([(I1, 'p1'), (I2, 'p1')]) checker = MultiChecker([ (I2, 'p1'), {'a': 'p3'}, (I3, 'p2'), (('x','y','z'), 'p4'), ]) self.assertEqual(checker.permission_id('f1'), 'p1') self.assertEqual(checker.permission_id('f2'), 'p1') self.assertEqual(checker.permission_id('f3'), 'p1') self.assertEqual(checker.permission_id('f4'), 'p1') self.assertEqual(checker.permission_id('g'), 'p2') self.assertEqual(checker.permission_id('a'), 'p3') self.assertEqual(checker.permission_id('x'), 'p4') self.assertEqual(checker.permission_id('y'), 'p4') self.assertEqual(checker.permission_id('z'), 'p4') self.assertEqual(checker.permission_id('zzz'), None) def testAlwaysAvailable(self): from zope.security.checker import NamesChecker checker = NamesChecker(()) class C(object): pass self.assertEqual(checker.check(C, '__hash__'), None) self.assertEqual(checker.check(C, '__nonzero__'), None) self.assertEqual(checker.check(C, '__class__'), None) self.assertEqual(checker.check(C, '__implements__'), None) self.assertEqual(checker.check(C, '__lt__'), None) self.assertEqual(checker.check(C, '__le__'), None) self.assertEqual(checker.check(C, '__gt__'), None) self.assertEqual(checker.check(C, '__ge__'), None) self.assertEqual(checker.check(C, '__eq__'), None) self.assertEqual(checker.check(C, '__ne__'), None) self.assertEqual(checker.check(C, '__name__'), None) self.assertEqual(checker.check(C, '__parent__'), None) def test_setattr(self): checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'test_allowed') for inst in NewInst(), OldInst(): self.assertRaises(Forbidden, checker.check_setattr, inst, 'a') self.assertRaises(Forbidden, checker.check_setattr, inst, 'z') # TODO: write a test to see that # Checker.check/check_setattr handle permission # values that evaluate to False def test_ProxyFactory(self): class SomeClass(object): pass import zope.security checker = NamesChecker() specific_checker = NamesChecker() checker_as_magic_attr = NamesChecker() obj = SomeClass() proxy = ProxyFactory(obj) self.assert_(type(proxy) is Proxy) from zope.security.checker import _defaultChecker self.assert_(getChecker(proxy) is _defaultChecker) defineChecker(SomeClass, checker) proxy = ProxyFactory(obj) self.assert_(type(proxy) is Proxy) self.assert_(getChecker(proxy) is checker) obj.__Security_checker__ = checker_as_magic_attr proxy = ProxyFactory(obj) self.assert_(type(proxy) is Proxy) self.assert_(getChecker(proxy) is checker_as_magic_attr) proxy = ProxyFactory(obj, specific_checker) self.assert_(type(proxy) is Proxy) self.assert_(getChecker(proxy) is specific_checker) def test_define_and_undefineChecker(self): class SomeClass(object): pass obj = SomeClass() checker = NamesChecker() from zope.security.checker import _defaultChecker, selectChecker self.assert_(selectChecker(obj) is _defaultChecker) defineChecker(SomeClass, checker) self.assert_(selectChecker(obj) is checker) undefineChecker(SomeClass) self.assert_(selectChecker(obj) is _defaultChecker) def test_ProxyFactory_using_proxy(self): class SomeClass(object): pass obj = SomeClass() checker = NamesChecker() proxy1 = ProxyFactory(obj) proxy2 = ProxyFactory(proxy1) self.assert_(proxy1 is proxy2) # Trying to change the checker on a proxy. self.assertRaises(TypeError, ProxyFactory, proxy1, checker) # Setting exactly the same checker as the proxy already has. 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', # these are the read settings 'bar':'test_allowed', 'baz':'you_will_not_have_this_permission'}, {'foo':'test_allowed', # these are the write settings 'bar':'you_will_not_have_this_permission', 'bing':'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 or reading shazam, so # checking these actually raises Forbidden. The rationale behind # exposing the Forbidden exception is primarily that it is usually # indicative of programming or configuration errors. self.assertRaises(Forbidden, canAccess, obj, 'shazam') self.assertRaises(Forbidden, canWrite, obj, 'shazam') # However, we special-case canWrite when an attribute has a Read # setting but no Write setting. Consider the 'baz' attribute from the # checker above: it is readonly. All users are forbidden to write # it. This is a very reasonable configuration. Therefore, canWrite # will hide the Forbidden exception if and only if there is a # setting for accessing the attribute. self.assert_(not canWrite(obj, 'baz')) # The reverse is not true at the moment: an unusal case like the # write-only 'bing' attribute will return a boolean for canWrite, # but canRead will simply raise a Forbidden exception, without checking # write settings. self.assert_(not canWrite(obj, 'bing')) self.assertRaises(Forbidden, canAccess, obj, 'bing') class TestCheckerPublic(TestCase): def test_that_pickling_CheckerPublic_retains_identity(self): self.assert_(pickle.loads(pickle.dumps(CheckerPublic)) is CheckerPublic) def test_that_CheckerPublic_identity_works_even_when_proxied(self): self.assert_(ProxyFactory(CheckerPublic) is CheckerPublic) class TestMixinDecoratedChecker(TestCase): def decoratedSetUp(self): self.policy = RecordedSecurityPolicy self._oldpolicy = setSecurityPolicy(self.policy) newInteraction() self.interaction = getInteraction() self.obj = object() def decoratedTearDown(self): endInteraction() setSecurityPolicy(self._oldpolicy) def check_checking_impl(self, checker): o = self.obj checker.check_getattr(o, 'both_get_set') self.assert_(self.interaction.checkChecked(['dc_get_permission'])) checker.check_getattr(o, 'c_only') self.assert_(self.interaction.checkChecked(['get_permission'])) checker.check_getattr(o, 'd_only') self.assert_(self.interaction.checkChecked(['dc_get_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_getattr, o, 'completely_different_attr') self.assert_(self.interaction.checkChecked([])) checker.check(o, '__str__') self.assert_(self.interaction.checkChecked(['get_permission'])) checker.check_setattr(o, 'both_get_set') self.assert_(self.interaction.checkChecked(['dc_set_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'c_only') self.assert_(self.interaction.checkChecked([])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'd_only') self.assert_(self.interaction.checkChecked([])) originalChecker = NamesChecker(['both_get_set', 'c_only', '__str__'], 'get_permission') decorationSetMap = {'both_get_set': 'dc_set_permission'} decorationGetMap = {'both_get_set': 'dc_get_permission', 'd_only': 'dc_get_permission'} overridingChecker = Checker(decorationGetMap, decorationSetMap) class TestCombinedChecker(TestMixinDecoratedChecker, TestCase): def setUp(self): TestCase.setUp(self) self.decoratedSetUp() def tearDown(self): self.decoratedTearDown() TestCase.tearDown(self) def test_checking(self): from zope.security.checker import CombinedChecker cc = CombinedChecker(self.overridingChecker, self.originalChecker) self.check_checking_impl(cc) # When a permission is not authorized by the security policy, # the policy is queried twice per check_getattr -- once for each # checker. self.interaction.permissions['dc_get_permission'] = False cc.check_getattr(self.obj, 'both_get_set') self.assert_( self.interaction.checkChecked(['dc_get_permission', 'get_permission']) ) # This should raise Unauthorized instead of ForbiddenAttribute, since # access can be granted if you e.g. login with different credentials. self.assertRaises(Unauthorized, cc.check_getattr, self.obj, 'd_only') self.assertRaises(Unauthorized, cc.check, self.obj, 'd_only') def test_interface(self): from zope.security.checker import CombinedChecker from zope.security.interfaces import IChecker dc = CombinedChecker(self.overridingChecker, self.originalChecker) verifyObject(IChecker, dc) class TestBasicTypes(TestCase): def test(self): class MyType(object): pass class MyType2(object): pass # When an item is added to the basic types, it should also be added to # the list of checkers. BasicTypes[MyType] = NoProxy self.assert_(MyType in _checkers) # If we clear the checkers, the type should still be there _clear() self.assert_(MyType in BasicTypes) self.assert_(MyType in _checkers) # Now delete the type from the dictionary, will also delete it from # the checkers del BasicTypes[MyType] self.assert_(MyType not in BasicTypes) self.assert_(MyType not in _checkers) # The quick way of adding new types is using update BasicTypes.update({MyType: NoProxy, MyType2: NoProxy}) self.assert_(MyType in BasicTypes) self.assert_(MyType2 in BasicTypes) self.assert_(MyType in _checkers) self.assert_(MyType2 in _checkers) # Let's remove the two new types del BasicTypes[MyType] del BasicTypes[MyType2] # Of course, BasicTypes is a full dictionary. This dictionary is by # default filled with several entries: keys = BasicTypes.keys() keys.sort() self.assert_(bool in keys) self.assert_(int in keys) self.assert_(float in keys) self.assert_(str in keys) self.assert_(unicode in keys) self.assert_(object in keys) # ... # Finally, the ``clear()`` method has been deactivated to avoid # unwanted deletions. self.assertRaises(NotImplementedError, BasicTypes.clear) def test_suite(): return TestSuite(( makeSuite(Test), makeSuite(TestCheckerPublic), makeSuite(TestCombinedChecker), makeSuite(TestBasicTypes), )) if __name__=='__main__': main(defaultTest='test_suite') zope.security-3.8.3/src/zope/security/tests/test_configure.py0000644000076600000240000000055711637266447024432 0ustar macstaff00000000000000import doctest import unittest def test_suite(): suite = unittest.TestSuite() try: import zope.configuration except ImportError: pass else: from zope.component.testing import setUp, tearDown suite.addTest(doctest.DocFileSuite('configure.txt', setUp=setUp, tearDown=tearDown)) return suite zope.security-3.8.3/src/zope/security/tests/test_contentdirective.py0000644000076600000240000001465211637266447026023 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test 'zope:class' directive. """ import unittest from StringIO import StringIO from zope.component.interfaces import IFactory from zope.component.interfaces import ComponentLookupError from zope.component.interface import queryInterface try: from zope.configuration.xmlconfig import xmlconfig, XMLConfig except ImportError: HAVE_ZCML = False else: HAVE_ZCML = True import zope.component import zope.security from zope.component.testing import PlacelessSetup # explicitly import ExampleClass and IExample using full paths # so that they are the same objects as resolve will get. from zope.security.tests.exampleclass import ExampleClass from zope.security.tests.exampleclass import IExample, IExample2 class ParticipationStub(object): def __init__(self, principal): self.principal = principal self.interaction = None def configfile(s): return StringIO(""" %s """ % s) class TestClassDirective(PlacelessSetup, unittest.TestCase): def setUp(self): super(TestClassDirective, self).setUp() XMLConfig('meta.zcml', zope.security)() try: del ExampleClass.__implements__ except AttributeError: pass def testEmptyDirective(self): f = configfile(""" """) xmlconfig(f) def testImplements(self): self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), None) f = configfile(""" """) xmlconfig(f) self.failUnless(IExample.implementedBy(ExampleClass)) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), IExample) def testMulImplements(self): self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), None) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample2"), None) f = configfile(""" """) xmlconfig(f) self.failUnless(IExample.implementedBy(ExampleClass)) self.failUnless(IExample2.implementedBy(ExampleClass)) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), IExample) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample2"), IExample2) def testRequire(self): f = configfile(""" """) xmlconfig(f) def testAllow(self): f = configfile(""" """) xmlconfig(f) def testMimic(self): f = configfile(""" """) xmlconfig(f) class TestFactorySubdirective(PlacelessSetup, unittest.TestCase): def setUp(self): super(TestFactorySubdirective, self).setUp() XMLConfig('meta.zcml', zope.security)() def testFactory(self): f = configfile(""" """) xmlconfig(f) factory = zope.component.getUtility(IFactory, 'test.Example') self.assertEquals(factory.title, "Example content") self.assertEquals(factory.description, "Example description") def testFactoryNoId(self): f = configfile(""" """) xmlconfig(f) self.assertRaises(ComponentLookupError, zope.component.getUtility, IFactory, 'Example') factory = zope.component.getUtility( IFactory, 'zope.security.tests.exampleclass.ExampleClass') self.assertEquals(factory.title, "Example content") self.assertEquals(factory.description, "Example description") def testFactoryPublicPermission(self): f = configfile(""" """) xmlconfig(f) factory = zope.component.getUtility(IFactory, 'test.Example') self.assert_(hasattr(factory, '__Security_checker__')) def test_suite(): if not HAVE_ZCML: return unittest.TestSuite() suite = unittest.TestSuite() loader = unittest.TestLoader() suite.addTest(loader.loadTestsFromTestCase(TestClassDirective)) suite.addTest(loader.loadTestsFromTestCase(TestFactorySubdirective)) return suite zope.security-3.8.3/src/zope/security/tests/test_decorator.py0000644000076600000240000000145411637266447024430 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Context Tests """ import doctest def test_suite(): suite = doctest.DocTestSuite() suite.addTest(doctest.DocTestSuite('zope.security.decorator')) return suite zope.security-3.8.3/src/zope/security/tests/test_directives.py0000644000076600000240000003275611637266447024620 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Component Directives Tests """ import re import unittest import pprint from cStringIO import StringIO from doctest import DocTestSuite import zope.component from zope.interface import implements from zope.component.interface import queryInterface try: from zope.configuration.xmlconfig import xmlconfig, XMLConfig from zope.configuration.xmlconfig import ZopeXMLConfigurationError except ImportError: HAVE_ZCML = False else: HAVE_ZCML = True from zope.security.checker import selectChecker from zope.security import proxy import zope.security from zope.component.testing import PlacelessSetup from zope.security.tests import module, exampleclass # TODO: tests for other directives needed atre = re.compile(' at [0-9a-fA-Fx]+') class Context(object): actions = () def action(self, discriminator, callable, args): self.actions += ((discriminator, callable, args), ) def __repr__(self): stream = StringIO() pprinter = pprint.PrettyPrinter(stream=stream, width=60) pprinter.pprint(self.actions) r = stream.getvalue() return (''.join(atre.split(r))).strip() template = """ %s """ def definePermissions(): XMLConfig('meta.zcml', zope.security)() class ParticipationStub(object): def __init__(self, principal): self.principal = principal self.interaction = None def configfile(s): return StringIO(""" %s """ % s) class TestFactoryDirective(PlacelessSetup, unittest.TestCase): def setUp(self): super(TestFactoryDirective, self).setUp() XMLConfig('meta.zcml', zope.security)() def testFactory(self): f = configfile(''' ''') xmlconfig(f) obj = zope.component.createObject('test.Example') self.failUnless(proxy.isinstance(obj, exampleclass.ExampleClass)) PREFIX = module.__name__ + '.' def defineDirectives(): XMLConfig('meta.zcml', zope.security)() xmlconfig(StringIO(""" """)) NOTSET = [] P1 = "zope.Extravagant" P2 = "zope.Paltry" class TestRequireDirective(PlacelessSetup, unittest.TestCase): def setUp(self): super(TestRequireDirective, self).setUp() defineDirectives() class B(object): def m1(self): return "m1" def m2(self): return "m2" class C(B): implements(module.I) def m3(self): return "m3" def m4(self): return "m4" module.test_base = B module.test_class = C module.test_instance = C() self.assertState() def tearDown(self): PlacelessSetup.tearDown(self) module.test_class = None def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): "Verify that class, instance, and methods have expected permissions." checker = selectChecker(module.test_instance) self.assertEqual(checker.permission_id('m1'), (m1P or None)) self.assertEqual(checker.permission_id('m2'), (m2P or None)) self.assertEqual(checker.permission_id('m3'), (m3P or None)) def assertDeclaration(self, declaration, **state): apply_declaration(module.template_bracket % declaration) self.assertState(**state) # "testSimple*" exercises tags that do NOT have children. This mode # inherently sets the instances as well as the class attributes. def testSimpleMethodsPlural(self): declaration = (''' ''' % (PREFIX+"test_class", P1)) self.assertDeclaration(declaration, m1P=P1, m3P=P1) def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): "Verify that class, instance, and methods have expected permissions." from zope.security.checker import selectChecker checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) self.assertEqual(checker.setattr_permission_id('m3'), (m3P or None)) def assertSetattrDeclaration(self, declaration, **state): self.assertSetattrState(**state) def test_set_attributes(self): declaration = (''' ''' % (PREFIX+"test_class", P1)) apply_declaration(module.template_bracket % declaration) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), P1) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), P1) def test_set_schema(self): self.assertEqual(queryInterface(PREFIX+"S"), None) declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"S")) apply_declaration(module.template_bracket % declaration) self.assertEqual(queryInterface(PREFIX+"S"), module.S) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), None) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), None) self.assertEqual(checker.setattr_permission_id('foo'), P1) self.assertEqual(checker.setattr_permission_id('bar'), P1) self.assertEqual(checker.setattr_permission_id('baro'), None) def test_multiple_set_schema(self): self.assertEqual(queryInterface(PREFIX+"S"), None) self.assertEqual(queryInterface(PREFIX+"S2"), None) declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"S", PREFIX+"S2")) apply_declaration(module.template_bracket % declaration) self.assertEqual(queryInterface(PREFIX+"S"), module.S) self.assertEqual(queryInterface(PREFIX+"S2"), module.S2) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), None) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), None) self.assertEqual(checker.setattr_permission_id('foo'), P1) self.assertEqual(checker.setattr_permission_id('bar'), P1) self.assertEqual(checker.setattr_permission_id('foo2'), P1) self.assertEqual(checker.setattr_permission_id('bar2'), P1) self.assertEqual(checker.setattr_permission_id('baro'), None) def testSimpleInterface(self): self.assertEqual(queryInterface(PREFIX+"I"), None) declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"I")) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) # Make sure we know about the interfaces self.assertEqual(queryInterface(PREFIX+"I"), module.I) def testMultipleInterface(self): self.assertEqual(queryInterface(PREFIX+"I3"), None) self.assertEqual(queryInterface(PREFIX+"I4"), None) declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"I3", PREFIX+"I4")) self.assertDeclaration(declaration, m3P=P1, m2P=P1) # Make sure we know about the interfaces self.assertEqual(queryInterface(PREFIX+"I3"), module.I3) self.assertEqual(queryInterface(PREFIX+"I4"), module.I4) # "testComposite*" exercises tags that DO have children. # "testComposite*TopPerm" exercises tags with permission in containing tag. # "testComposite*ElementPerm" exercises tags w/permission in children. def testCompositeNoPerm(self): # Establish rejection of declarations lacking a permission spec. declaration = (''' ''' % (PREFIX+"test_class")) self.assertRaises(ZopeXMLConfigurationError, self.assertDeclaration, declaration) def testCompositeMethodsPluralElementPerm(self): declaration = (''' ''' % (PREFIX+"test_class", P1)) self.assertDeclaration(declaration, m1P=P1, m3P=P1) def testCompositeInterfaceTopPerm(self): declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"I")) self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testSubInterfaces(self): declaration = (''' ''' % (PREFIX+"test_class", P1, PREFIX+"I2")) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testMimicOnly(self): declaration = (''' ''' % (PREFIX+"test_base", P1, PREFIX+"test_class", PREFIX+"test_base")) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testMimicAsDefault(self): declaration = (''' ''' % (PREFIX+"test_base", P1, PREFIX+"test_class", PREFIX+"test_base", P2)) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P2, m3P=P2) def apply_declaration(declaration): '''Apply the xmlconfig machinery.''' return xmlconfig(StringIO(declaration)) def test_suite(): if not HAVE_ZCML: return unittest.TestSuite() return unittest.TestSuite(( unittest.makeSuite(TestFactoryDirective), unittest.makeSuite(TestRequireDirective), DocTestSuite(), )) zope.security-3.8.3/src/zope/security/tests/test_location.py0000644000076600000240000000310211637266447024246 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Context Tests """ import doctest def test_locationproxy_security(): """We start with an unlocated class that will be wrapped by a LocationProxy: >>> class Unlocated(object): ... a = 'a' >>> unlocated = Unlocated() Now we create a location proxy around it: >>> from zope.location.location import LocationProxy >>> located = LocationProxy(unlocated) We define a checker for the unlocated object, which will also be used by the security proxy as the LocationProxy defines __Security_checker__: >>> from zope.security.checker import NamesChecker, defineChecker >>> unlocatedChecker = NamesChecker(['a']) >>> defineChecker(Unlocated, unlocatedChecker) >>> from zope.security.proxy import ProxyFactory >>> secure_located = ProxyFactory(located) >>> secure_located.a 'a' """ def test_suite(): return doctest.DocTestSuite() zope.security-3.8.3/src/zope/security/tests/test_management.py0000644000076600000240000001122211637266447024554 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """ Unit tests for SecurityManagement """ import unittest from zope.interface.verify import verifyObject from zope.testing.cleanup import CleanUp class Test(CleanUp, unittest.TestCase): def test_import(self): from zope.security import management from zope.security.interfaces import ISecurityManagement from zope.security.interfaces import IInteractionManagement verifyObject(ISecurityManagement, management) verifyObject(IInteractionManagement, management) def test_securityPolicy(self): from zope.security.management import setSecurityPolicy from zope.security.management import getSecurityPolicy from zope.security.simplepolicies import PermissiveSecurityPolicy policy = PermissiveSecurityPolicy setSecurityPolicy(policy) self.assert_(getSecurityPolicy() is policy) def test_query_new_end_restore_Interaction(self): from zope.security.management import queryInteraction self.assertEquals(queryInteraction(), None) from zope.security.management import newInteraction newInteraction() interaction = queryInteraction() self.assert_(interaction is not None) self.assertRaises(AssertionError, newInteraction) from zope.security.management import endInteraction endInteraction() self.assertEquals(queryInteraction(), None) from zope.security.management import restoreInteraction restoreInteraction() self.assert_(interaction is queryInteraction()) endInteraction() self.assertEquals(queryInteraction(), None) endInteraction() self.assertEquals(queryInteraction(), None) newInteraction() self.assert_(queryInteraction() is not None) restoreInteraction() # restore to no interaction self.assert_(queryInteraction() is None) def test_checkPermission(self): from zope.security import checkPermission from zope.security.management import setSecurityPolicy from zope.security.management import queryInteraction from zope.security.management import newInteraction, endInteraction from zope.security.interfaces import NoInteraction permission = 'zope.Test' obj = object() class PolicyStub(object): def checkPermission(s, p, o,): self.assert_(p is permission) self.assert_(o is obj) self.assert_(s is queryInteraction() or s is interaction) return s is interaction setSecurityPolicy(PolicyStub) newInteraction() interaction = queryInteraction() self.assertEquals(checkPermission(permission, obj), True) endInteraction() self.assertRaises(NoInteraction, checkPermission, permission, obj) def test_checkPublicPermission(self): from zope.security import checkPermission from zope.security.checker import CheckerPublic from zope.security.management import setSecurityPolicy from zope.security.management import newInteraction obj = object() class ForbiddenPolicyStub(object): def checkPermission(s, p, o): return False setSecurityPolicy(ForbiddenPolicyStub) newInteraction() self.assertEquals(checkPermission('zope.Test', obj), False) self.assertEquals(checkPermission(None, obj), True) self.assertEquals(checkPermission(CheckerPublic, obj), True) def test_system_user(self): from zope.security.management import system_user self.assertEquals(system_user.id, u'zope.security.management.system_user') self.assertEquals(system_user.title, u'System') for name in 'id', 'title', 'description': self.assert_(isinstance(getattr(system_user, name), unicode)) def test_suite(): loader = unittest.TestLoader() return loader.loadTestsFromTestCase(Test) if __name__=='__main__': unittest.TextTestRunner().run(test_suite()) zope.security-3.8.3/src/zope/security/tests/test_module_directives.py0000644000076600000240000001725011637266447026155 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Directives Tests """ import doctest import unittest from pprint import PrettyPrinter from zope.interface import Interface, Attribute from zope.component.testing import setUp, tearDown, PlacelessSetup try: from zope.configuration import xmlconfig except ImportError: HAVE_ZCML = False else: HAVE_ZCML = True def pprint(ob, width=70): PrettyPrinter(width=width).pprint(ob) class I1(Interface): def x(): pass y = Attribute("Y") class I2(I1): def a(): pass b = Attribute("B") test_perm = 'zope.security.metaconfigure.test' test_bad_perm = 'zope.security.metaconfigure.bad' def test_protectModule(): """ >>> from zope.security import metaconfigure >>> from zope.security.tests import test_directives >>> from zope.security.interfaces import IPermission >>> from zope.security.permission import Permission >>> from zope.component import provideUtility Initially, there's no checker defined for the module: >>> from zope.security.checker import moduleChecker >>> moduleChecker(test_directives) >>> perm = Permission(test_perm, '') >>> provideUtility(perm, IPermission, test_perm) >>> metaconfigure.protectModule(test_directives, 'foo', test_perm) Now, the checker should exist and have an access dictionary with the name and permission: >>> checker = moduleChecker(test_directives) >>> cdict = checker.get_permissions >>> pprint(cdict) {'foo': 'zope.security.metaconfigure.test'} If we define additional names, they will be added to the dict: >>> metaconfigure.protectModule(test_directives, 'bar', test_perm) >>> metaconfigure.protectModule(test_directives, 'baz', test_perm) >>> pprint(cdict) {'bar': 'zope.security.metaconfigure.test', 'baz': 'zope.security.metaconfigure.test', 'foo': 'zope.security.metaconfigure.test'} """ def test_allow(): """ The allow directive creates actions for each named defined directly, or via interface: >>> from zope.security import metaconfigure >>> class Context(object): ... def __init__(self): ... self.actions = [] ... ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is metaconfigure.protectModule), ... 'args': args}) ... ... module='testmodule' >>> context = Context() >>> metaconfigure.allow(context, attributes=['foo', 'bar'], ... interface=[I1, I2]) >>> context.actions.sort( ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] """ def test_require(): """ The allow directive creates actions for each named defined directly, or via interface: >>> from zope.security import metaconfigure >>> class Context(object): ... def __init__(self): ... self.actions = [] ... ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is metaconfigure.protectModule), ... 'args': args}) ... ... module='testmodule' >>> context = Context() >>> metaconfigure.require(context, attributes=['foo', 'bar'], ... interface=[I1, I2], permission='p') >>> context.actions.sort( ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] """ if HAVE_ZCML: import zope.security.zcml class IDummy(Interface): perm = zope.security.zcml.Permission(title=u'') perms = [] def dummy(context_, perm): global perms perms.append(perm) class DirectivesTest(PlacelessSetup, unittest.TestCase): def setUp(self): super(DirectivesTest, self).setUp() from zope.security import tests self.context = xmlconfig.file("redefineperms.zcml", tests) def tearDown(self): super(DirectivesTest, self).tearDown() perms.remove('zope.Security') def testRedefinePermission(self): self.assertEqual(perms, ['zope.Security']) def setUpAuth(test=None): setUp(test) def zcml(s): context = xmlconfig.file('meta.zcml', package=zope.security) xmlconfig.string(s, context) def reset(): tearDown() setUpAuth() def test_suite(): if not HAVE_ZCML: return unittest.TestSuite() return unittest.TestSuite(( doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), doctest.DocTestSuite('zope.security.zcml'), unittest.makeSuite(DirectivesTest), )) zope.security-3.8.3/src/zope/security/tests/test_permission.py0000644000076600000240000000170211637266447024632 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test permissions """ import unittest from doctest import DocTestSuite from zope.component.testing import setUp, tearDown __docformat__ = "reStructuredText" def test_suite(): return unittest.TestSuite([ DocTestSuite('zope.security.permission', setUp=setUp, tearDown=tearDown), ]) zope.security-3.8.3/src/zope/security/tests/test_protectclass.py0000644000076600000240000001207411637266447025154 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test handler for 'protectClass' directive """ import unittest from zope.interface import implements from zope.security.checker import selectChecker from zope.security.permission import Permission from zope import component from zope.component.testing import PlacelessSetup from zope.security.interfaces import IPermission from zope.security.protectclass import protectName, protectLikeUnto from zope.security.protectclass import protectSetAttribute from zope.security.tests.modulehookup import TestModule, I NOTSET = [] P1 = "extravagant" P2 = "paltry" class Test(PlacelessSetup, unittest.TestCase): def setUp(self): super(Test, self).setUp() component.provideUtility(Permission(P1), IPermission, P1) component.provideUtility(Permission(P2), IPermission, P2) class B(object): def m1(self): return "m1" def m2(self): return "m2" class C(B): implements(I) def m3(self): return "m3" def m4(self): return "m4" TestModule.test_base = B TestModule.test_class = C TestModule.test_instance = C() self.assertState() def tearDown(self): super(Test, self).tearDown() TestModule.test_class = None def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): "Verify that class, instance, and methods have expected permissions." checker = selectChecker(TestModule.test_instance) self.assertEqual(checker.permission_id('m1'), (m1P or None)) self.assertEqual(checker.permission_id('m2'), (m2P or None)) self.assertEqual(checker.permission_id('m3'), (m3P or None)) def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): "Verify that class, instance, and methods have expected permissions." checker = selectChecker(TestModule.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) self.assertEqual(checker.setattr_permission_id('m3'), (m3P or None)) # "testSimple*" exercises tags that do NOT have children. This mode # inherently sets the instances as well as the class attributes. def testSimpleMethodsPlural(self): protectName(TestModule.test_class, 'm1', P1) protectName(TestModule.test_class, 'm3', P1) self.assertState(m1P=P1, m3P=P1) def testLikeUntoOnly(self): protectName(TestModule.test_base, 'm1', P1) protectName(TestModule.test_base, 'm2', P1) protectSetAttribute(TestModule.test_base, 'm1', P1) protectSetAttribute(TestModule.test_base, 'm2', P1) protectLikeUnto(TestModule.test_class, TestModule.test_base) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertState(m1P=P1, m2P=P1) self.assertSetattrState(m1P=P1, m2P=P1) def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): "Verify that class, instance, and methods have expected permissions." checker = selectChecker(TestModule.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) self.assertEqual(checker.setattr_permission_id("m3"), (m3P or None)) def testSetattr(self): protectSetAttribute(TestModule.test_class, 'm1', P1) protectSetAttribute(TestModule.test_class, 'm3', P1) self.assertSetattrState(m1P=P1, m3P=P1) def testLikeUntoAsDefault(self): protectName(TestModule.test_base, 'm1', P1) protectName(TestModule.test_base, 'm2', P1) protectSetAttribute(TestModule.test_base, 'm1', P1) protectSetAttribute(TestModule.test_base, 'm2', P1) protectLikeUnto(TestModule.test_class, TestModule.test_base) protectName(TestModule.test_class, 'm2', P2) protectName(TestModule.test_class, 'm3', P2) protectSetAttribute(TestModule.test_class, 'm2', P2) protectSetAttribute(TestModule.test_class, 'm3', P2) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertState(m1P=P1, m2P=P2, m3P=P2) self.assertSetattrState(m1P=P1, m2P=P2, m3P=P2) def test_suite(): loader=unittest.TestLoader() return loader.loadTestsFromTestCase(Test) if __name__=='__main__': unittest.TextTestRunner().run(test_suite()) zope.security-3.8.3/src/zope/security/tests/test_protectsubclass.py0000644000076600000240000000367011637266447025670 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test proper protection of inherited methods """ import unittest from zope.security.checker import selectChecker from zope.security.permission import Permission from zope import component from zope.component.testing import PlacelessSetup from zope.security.interfaces import IPermission from zope.security.protectclass import protectName class Test(PlacelessSetup, unittest.TestCase): def testInherited(self): class B1(object): def g(self): return 'B1.g' class B2(object): def h(self): return 'B2.h' class S(B1, B2): pass component.provideUtility(Permission('B1', ''), IPermission, 'B1') component.provideUtility(Permission('S', ''), IPermission, 'S') protectName(B1, 'g', 'B1') protectName(S, 'g', 'S') protectName(S, 'h', 'S') self.assertEqual(selectChecker(B1()).permission_id('g'), 'B1') self.assertEqual(selectChecker(B2()).permission_id('h'), None) self.assertEqual(selectChecker(S()).permission_id('g'), 'S') self.assertEqual(selectChecker(S()).permission_id('h'), 'S') self.assertEqual(S().g(), 'B1.g') self.assertEqual(S().h(), 'B2.h') def test_suite(): return unittest.makeSuite(Test) if __name__=='__main__': unittest.main(defaultTest='test_suite') zope.security-3.8.3/src/zope/security/tests/test_proxy.py0000644000076600000240000002722611637266447023634 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Security proxy tests """ import unittest from doctest import DocTestSuite from zope.security.proxy import getChecker, ProxyFactory, removeSecurityProxy from zope.proxy import ProxyBase as proxy class Checker(object): ok = 1 unproxied_types = str, def check_getattr(self, object, name): if name not in ("foo", "next", "__class__", "__name__", "__module__"): raise RuntimeError def check_setattr(self, object, name): if name != "foo": raise RuntimeError def check(self, object, opname): if not self.ok: raise RuntimeError def proxy(self, value): if type(value) in self.unproxied_types: return value return ProxyFactory(value, self) class Something: def __init__(self): self.foo = [1,2,3] def __getitem__(self, key): return self.foo[key] def __setitem__(self, key, value): self.foo[key] = value def __delitem__(self, key): del self.foo[key] def __call__(self, arg): return 42 def __eq__(self, other): return self is other def __hash__(self): return 42 def __iter__(self): return self def next(self): return 42 # Infinite sequence def __len__(self): return 42 def __nonzero__(self): return 1 def __getslice__(self, i, j): return [42] def __setslice__(self, i, j, value): if value != [42]: raise ValueError def __contains__(self, x): return x == 42 class ProxyTests(unittest.TestCase): def setUp(self): self.x = Something() self.c = Checker() self.p = ProxyFactory(self.x, self.c) def shouldFail(self, *args): self.c.ok = 0 self.assertRaises(RuntimeError, *args) self.c.ok = 1 def testDerivation(self): self.assert_(isinstance(self.p, proxy)) def testStr(self): self.assertEqual(str(self.p), str(self.x)) x = Something() c = Checker() c.ok = 0 p = ProxyFactory(x, c) s = str(p) self.failUnless(s.startswith( ">y", "x&y", "x|y", "x^y", ] def test_binops(self): P = self.c.proxy for expr in self.binops: first = 1 for x in [1, P(1)]: for y in [2, P(2)]: if first: z = eval(expr) first = 0 else: self.assertEqual(removeSecurityProxy(eval(expr)), z, "x=%r; y=%r; expr=%r" % (x, y, expr)) self.shouldFail(lambda x, y: eval(expr), x, y) def test_inplace(self): # TODO: should test all inplace operators... P = self.c.proxy pa = P(1) pa += 2 self.assertEqual(removeSecurityProxy(pa), 3) a = [1, 2, 3] pa = qa = P(a) pa += [4, 5, 6] self.failUnless(pa is qa) self.assertEqual(a, [1, 2, 3, 4, 5, 6]) def doit(): pa = P(1) pa += 2 self.shouldFail(doit) pa = P(2) pa **= 2 self.assertEqual(removeSecurityProxy(pa), 4) def doit(): pa = P(2) pa **= 2 self.shouldFail(doit) def test_coerce(self): P = self.c.proxy # Before 2.3, coerce() of two proxies returns them unchanged import sys fixed_coerce = sys.version_info >= (2, 3, 0) x = P(1) y = P(2) a, b = coerce(x, y) self.failUnless(a is x and b is y) x = P(1) y = P(2.1) a, b = coerce(x, y) self.failUnless(removeSecurityProxy(a) == 1.0 and b is y) if fixed_coerce: self.failUnless(type(removeSecurityProxy(a)) is float and b is y) x = P(1.1) y = P(2) a, b = coerce(x, y) self.failUnless(a is x and removeSecurityProxy(b) == 2.0) if fixed_coerce: self.failUnless(a is x and type(removeSecurityProxy(b)) is float) x = P(1) y = 2 a, b = coerce(x, y) self.failUnless(a is x and b is y) x = P(1) y = 2.1 a, b = coerce(x, y) self.failUnless(type(removeSecurityProxy(a)) is float and b is y) x = P(1.1) y = 2 a, b = coerce(x, y) self.failUnless(a is x and type(removeSecurityProxy(b)) is float) x = 1 y = P(2) a, b = coerce(x, y) self.failUnless(a is x and b is y) x = 1.1 y = P(2) a, b = coerce(x, y) self.failUnless(a is x and type(removeSecurityProxy(b)) is float) x = 1 y = P(2.1) a, b = coerce(x, y) self.failUnless(type(removeSecurityProxy(a)) is float and b is y) def test_using_mapping_slots_hack(): """The security proxy will use mapping slots, on the checker to go faster If a checker implements normally, a checkers's check and check_getattr methods are used to check operator and attribute access: >>> class Checker(object): ... def check(self, object, name): ... print 'check', name ... def check_getattr(self, object, name): ... print 'check_getattr', name ... def proxy(self, object): ... return 1 >>> def f(): ... pass >>> p = ProxyFactory(f, Checker()) >>> p.__name__ check_getattr __name__ 1 >>> p() check __call__ 1 But, if the checker has a __setitem__ method: >>> def __setitem__(self, object, name): ... print '__setitem__', name >>> Checker.__setitem__ = __setitem__ It will be used rather than either check or check_getattr: >>> p.__name__ __setitem__ __name__ 1 >>> p() __setitem__ __call__ 1 If a checker has a __getitem__ method: >>> def __getitem__(self, object): ... return 2 >>> Checker.__getitem__ = __getitem__ It will be used rather than it's proxy method: >>> p.__name__ __setitem__ __name__ 2 >>> p() __setitem__ __call__ 2 """ def test_suite(): suite = unittest.makeSuite(ProxyTests) suite.addTest(DocTestSuite()) suite.addTest(DocTestSuite('zope.security.proxy')) return suite zope.security-3.8.3/src/zope/security/tests/test_set_checkers.py0000644000076600000240000001207411637266447025110 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test checkers for standard types This is a test of the assertions made in zope.security.checkers._default_checkers. """ import sys import unittest from doctest import DocTestSuite from zope.security.checker import ProxyFactory from zope.security.interfaces import ForbiddenAttribute def check_forbidden_get(object, attr): try: return getattr(object, attr) except ForbiddenAttribute, e: return 'ForbiddenAttribute: %s' % e[0] def test_set(): """Test that we can do everything we expect to be able to do with proxied sets. >>> us = set((1, 2)) >>> s = ProxyFactory(us) >>> check_forbidden_get(s, 'add') # Verify that we are protected 'ForbiddenAttribute: add' >>> check_forbidden_get(s, 'remove') # Verify that we are protected 'ForbiddenAttribute: remove' >>> check_forbidden_get(s, 'discard') # Verify that we are protected 'ForbiddenAttribute: discard' >>> check_forbidden_get(s, 'pop') # Verify that we are protected 'ForbiddenAttribute: pop' >>> check_forbidden_get(s, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> len(s) 2 >>> 1 in s True >>> 1 not in s False >>> s.issubset(set((1,2,3))) True >>> s.issuperset(set((1,2,3))) False >>> c = s.union(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | set((2, 3)) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | ProxyFactory(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) | s >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.intersection(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & set((2, 3)) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & ProxyFactory(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) & s >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.difference(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - ProxyFactory(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - set((2, 3)) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) - s >>> sorted(c) [3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.symmetric_difference(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ set((2, 3)) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ ProxyFactory(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) ^ s >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.copy() >>> sorted(c) [1, 2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> str(s) == str(us) True >>> repr(s) == repr(us) True Always available: >>> s < us False >>> s > us False >>> s <= us True >>> s >= us True >>> s == us True >>> s != us False Note that you can't compare proxied sets with other proxied sets due a limitaion in the set comparison functions which won't work with any kind of proxy. >>> bool(s) True >>> s.__class__ == set True """ def setUpFrozenSet(test): test.globs['set'] = frozenset def setUpSet(test): import sets test.globs['set'] = sets.Set def setUpImmutableSet(test): import sets test.globs['set'] = sets.ImmutableSet def test_suite(): doctests = [ DocTestSuite(), DocTestSuite(setUp=setUpFrozenSet), ] if sys.version_info[:2] < (2, 6): doctests.append(DocTestSuite(setUp=setUpSet)) doctests.append(DocTestSuite(setUp=setUpImmutableSet)) return unittest.TestSuite(doctests) zope.security-3.8.3/src/zope/security/tests/test_simpleinteraction.py0000644000076600000240000000466211637266447026203 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Unit tests for zope.security.simpleinteraction. """ import unittest from zope.interface.verify import verifyObject from zope.security.interfaces import IInteraction from zope.security.simplepolicies import ParanoidSecurityPolicy class RequestStub(object): def __init__(self, principal=None): self.principal = principal self.interaction = None class TestInteraction(unittest.TestCase): def test(self): interaction = ParanoidSecurityPolicy() verifyObject(IInteraction, interaction) def test_add(self): rq = RequestStub() interaction = ParanoidSecurityPolicy() interaction.add(rq) self.assert_(rq in interaction.participations) self.assert_(rq.interaction is interaction) # rq already added self.assertRaises(ValueError, interaction.add, rq) interaction2 = ParanoidSecurityPolicy() self.assertRaises(ValueError, interaction2.add, rq) def test_remove(self): rq = RequestStub() interaction = ParanoidSecurityPolicy() self.assertRaises(ValueError, interaction.remove, rq) interaction.add(rq) interaction.remove(rq) self.assert_(rq not in interaction.participations) self.assert_(rq.interaction is None) def testCreateInteraction(self): i1 = ParanoidSecurityPolicy() verifyObject(IInteraction, i1) self.assertEquals(list(i1.participations), []) user = object() request = RequestStub(user) i2 = ParanoidSecurityPolicy(request) verifyObject(IInteraction, i2) self.assertEquals(list(i2.participations), [request]) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestInteraction)) return suite if __name__ == '__main__': unittest.main() zope.security-3.8.3/src/zope/security/tests/test_standard_checkers.py0000644000076600000240000002510411637266447026113 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Test checkers for standard types This is a test of the assertions made in zope.security.checkers._default_checkers. """ from zope.security.checker import ProxyFactory, NamesChecker from zope.security.interfaces import ForbiddenAttribute import sys def check_forbidden_get(object, attr): try: return getattr(object, attr) except ForbiddenAttribute, e: return 'ForbiddenAttribute: %s' % e[0] def check_forbidden_setitem(object, item, value): try: object[item] = value except ForbiddenAttribute, e: return 'ForbiddenAttribute: %s' % e[0] def check_forbidden_delitem(object, item): try: del object[item] except ForbiddenAttribute, e: return 'ForbiddenAttribute: %s' % e[0] def check_forbidden_call(callable, *args): try: return callable(*args) except ForbiddenAttribute, e: return 'ForbiddenAttribute: %s' % e[0] def test_dict(): """Test that we can do everything we expect to be able to do with proxied dicts. >>> d = ProxyFactory({'a': 1, 'b': 2}) >>> check_forbidden_get(d, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected 'ForbiddenAttribute: __setitem__' >>> d['a'] 1 >>> len(d) 2 >>> list(d) ['a', 'b'] >>> d.get('a') 1 >>> int(d.has_key('a')) 1 >>> c = d.copy() >>> check_forbidden_get(c, 'clear') 'ForbiddenAttribute: clear' >>> int(str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) 1 >>> int(`c` in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) 1 >>> def sorted(x): ... x = list(x) ... x.sort() ... return x >>> sorted(d.keys()) ['a', 'b'] >>> sorted(d.values()) [1, 2] >>> sorted(d.items()) [('a', 1), ('b', 2)] >>> sorted(d.iterkeys()) ['a', 'b'] >>> sorted(d.itervalues()) [1, 2] >>> sorted(d.iteritems()) [('a', 1), ('b', 2)] Always available: >>> int(d < d) 0 >>> int(d > d) 0 >>> int(d <= d) 1 >>> int(d >= d) 1 >>> int(d == d) 1 >>> int(d != d) 0 >>> int(bool(d)) 1 >>> int(d.__class__ == dict) 1 """ def test_list(): """Test that we can do everything we expect to be able to do with proxied lists. >>> l = ProxyFactory([1, 2]) >>> check_forbidden_delitem(l, 0) 'ForbiddenAttribute: __delitem__' >>> check_forbidden_setitem(l, 0, 3) 'ForbiddenAttribute: __setitem__' >>> l[0] 1 >>> l[0:1] [1] >>> check_forbidden_setitem(l[:1], 0, 2) 'ForbiddenAttribute: __setitem__' >>> len(l) 2 >>> tuple(l) (1, 2) >>> int(1 in l) 1 >>> l.index(2) 1 >>> l.count(2) 1 >>> str(l) '[1, 2]' >>> `l` '[1, 2]' >>> l + l [1, 2, 1, 2] Always available: >>> int(l < l) 0 >>> int(l > l) 0 >>> int(l <= l) 1 >>> int(l >= l) 1 >>> int(l == l) 1 >>> int(l != l) 0 >>> int(bool(l)) 1 >>> int(l.__class__ == list) 1 """ def test_tuple(): """Test that we can do everything we expect to be able to do with proxied lists. >>> l = ProxyFactory((1, 2)) >>> l[0] 1 >>> l[0:1] (1,) >>> len(l) 2 >>> list(l) [1, 2] >>> int(1 in l) 1 >>> str(l) '(1, 2)' >>> `l` '(1, 2)' >>> l + l (1, 2, 1, 2) Always available: >>> int(l < l) 0 >>> int(l > l) 0 >>> int(l <= l) 1 >>> int(l >= l) 1 >>> int(l == l) 1 >>> int(l != l) 0 >>> int(bool(l)) 1 >>> int(l.__class__ == tuple) 1 """ def test_iter(): """ >>> list(ProxyFactory(iter([1, 2]))) [1, 2] >>> list(ProxyFactory(iter((1, 2)))) [1, 2] >>> list(ProxyFactory(iter({1:1, 2:2}))) [1, 2] >>> def f(): ... for i in 1, 2: ... yield i ... >>> list(ProxyFactory(f())) [1, 2] >>> list(ProxyFactory(f)()) [1, 2] """ def test_new_class(): """ >>> class C(object): ... x = 1 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = `C` >>> int(C.__module__ == __name__) 1 >>> len(C.__bases__) 1 >>> len(C.__mro__) 2 Always available: >>> int(C < C) 0 >>> int(C > C) 0 >>> int(C <= C) 1 >>> int(C >= C) 1 >>> int(C == C) 1 >>> int(C != C) 0 >>> int(bool(C)) 1 >>> int(C.__class__ == type) 1 """ def test_new_instance(): """ >>> class C(object): ... x, y = 1, 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> int(c.__class__ == C) 1 Always available: >>> int(c < c) 0 >>> int(c > c) 0 >>> int(c <= c) 1 >>> int(c >= c) 1 >>> int(c == c) 1 >>> int(c != c) 0 >>> int(bool(c)) 1 >>> int(c.__class__ == C) 1 """ def test_classic_class(): """ >>> class C: ... x = 1 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = `C` >>> int(C.__module__ == __name__) 1 >>> len(C.__bases__) 0 Always available: >>> int(C < C) 0 >>> int(C > C) 0 >>> int(C <= C) 1 >>> int(C >= C) 1 >>> int(C == C) 1 >>> int(C != C) 0 >>> int(bool(C)) 1 """ def test_classic_instance(): """ >>> class C(object): ... x, y = 1, 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> int(c.__class__ == C) 1 Always available: >>> int(c < c) 0 >>> int(c > c) 0 >>> int(c <= c) 1 >>> int(c >= c) 1 >>> int(c == c) 1 >>> int(c != c) 0 >>> int(bool(c)) 1 >>> int(c.__class__ == C) 1 """ def test_rocks(): """ >>> int(type(ProxyFactory( object() )) is object) 1 >>> int(type(ProxyFactory( 1 )) is int) 1 >>> int(type(ProxyFactory( 1.0 )) is float) 1 >>> int(type(ProxyFactory( 1l )) is long) 1 >>> int(type(ProxyFactory( 1j )) is complex) 1 >>> int(type(ProxyFactory( None )) is type(None)) 1 >>> int(type(ProxyFactory( 'xxx' )) is str) 1 >>> int(type(ProxyFactory( u'xxx' )) is unicode) 1 >>> int(type(ProxyFactory( True )) is type(True)) 1 >>> from datetime import timedelta, datetime, date, time, tzinfo >>> int(type(ProxyFactory( timedelta(1) )) is timedelta) 1 >>> int(type(ProxyFactory( datetime(2000, 1, 1) )) is datetime) 1 >>> int(type(ProxyFactory( date(2000, 1, 1) )) is date) 1 >>> int(type(ProxyFactory( time() )) is time) 1 >>> int(type(ProxyFactory( tzinfo() )) is tzinfo) 1 >>> try: ... from pytz import UTC ... except ImportError: # pytz checker only if pytz is present. ... UTC = None >>> int(UTC is None or type(ProxyFactory( UTC )) is type(UTC)) 1 """ def test_iter_of_sequences(): """ >>> class X(object): ... d = 1, 2, 3 ... def __getitem__(self, i): ... return self.d[i] ... >>> x = X() We can iterate over sequences >>> list(x) [1, 2, 3] >>> c = NamesChecker(['__getitem__']) >>> p = ProxyFactory(x, c) Even if they are proxied >>> list(p) [1, 2, 3] But if the class has an iter: >>> X.__iter__ = lambda self: iter(self.d) >>> list(x) [1, 2, 3] We shouldn't be able to iterate if we don't have an assertion: >>> check_forbidden_call(list, p) 'ForbiddenAttribute: __iter__' """ def test_interfaces_and_declarations(): """Test that we can still use interfaces though proxies >>> import zope.interface >>> class I(zope.interface.Interface): ... pass >>> class IN(zope.interface.Interface): ... pass >>> class II(zope.interface.Interface): ... pass >>> class N(object): ... zope.interface.implements(I) ... zope.interface.classProvides(IN) >>> n = N() >>> zope.interface.directlyProvides(n, II) >>> from zope.security.checker import ProxyFactory >>> N = ProxyFactory(N) >>> n = ProxyFactory(n) >>> I.implementedBy(N) True >>> IN.providedBy(N) True >>> I.providedBy(n) True >>> II.providedBy(n) True """ if sys.version_info >= (2, 6): def test_ABCMeta(): """ Test that we work with the ABCMeta meta class >>> import abc >>> class MyABC: ... __metaclass__ = abc.ABCMeta >>> class Foo(MyABC): pass >>> class Bar(Foo): pass >>> PBar = ProxyFactory(Bar) >>> [c.__name__ for c in PBar.__mro__] ['Bar', 'Foo', 'MyABC', 'object'] >>> check_forbidden_call(PBar) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(PBar, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(PBar) >>> s = `PBar` >>> int(PBar.__module__ == __name__) 1 >>> len(PBar.__bases__) 1 Always available: >>> int(PBar < PBar) 0 >>> int(PBar > PBar) 0 >>> int(PBar <= PBar) 1 >>> int(PBar >= PBar) 1 >>> int(PBar == PBar) 1 >>> int(PBar != PBar) 0 >>> int(bool(PBar)) 1 >>> int(PBar.__class__ == abc.ABCMeta) 1 """ def test_suite(): from doctest import DocTestSuite return DocTestSuite() zope.security-3.8.3/src/zope/security/untrustedinterpreter.txt0000644000076600000240000002137611637266447024762 0ustar macstaff00000000000000====================== Untrusted interpreters ====================== Untrusted programs are executed by untrusted interpreters. Untrusted interpreters make use of security proxies to prevent un-mediated access to assets. An untrusted interpreter defines an environment for running untrusted programs. All objects within the environment are either: - "safe" objects created internally by the environment or created in the course of executing the untrusted program, or - "basic" objects - security-proxied non-basic objects The environment includes proxied functions for accessing objects outside of the environment. These proxied functions provide the only way to access information outside the environment. Because these functions are proxied, as described below, any access to objects outside the environment is mediated by the target security functions. Safe objects are objects whose operations, except for attribute retrieval, and methods access only information stored within the objects or passed as arguments. Safe objects contained within the interpreter environment can contain only information that is already in the environment or computed directly from information that is included in the environment. For this reason, safe objects created within the environment cannot be used to directly access information outside the environment. Safe objects have some attributes that could (very) indirectly be used to access assets. For this reason, an untrusted interpreter always proxies the results of attribute accesses on a safe objects. Basic objects are safe objects that are used to represent elemental data values such as strings and numbers. Basic objects require a lower level of protection than non-basic objects, as will be described detail in a later section. Security proxies mediate all object operations. Any operation access is checked to see whether a subject is authorized to perform the operation. All operation results other than basic objects are, in turn, security proxied. Security proxies will be described in greater detail in a later section. Any operation on a security proxy that results in a non-basic object is also security proxied. All external resources needed to perform an operation are security proxied. Let's consider the trusted interpreter for evaluating URLs. In operation 1 of the example, the interpreter uses a proxied method for getting the system root object. Because the method is proxied, the result of calling the method and the operation is also proxied. The interpreter has a function for traversing objects. This function is proxied. When traversing an object, the function is passed an object and a name. In operation 2, the function is passed the result of operation 1, which is the proxied root object and the name 'A'. We may traverse an object by invoking an operation on it. For example, we may use an operation to get a sub-object. Because any operation on a proxied object returns a proxied object or a basic object, the result is either a proxied object or a basic object. Traversal may also look up a component. For example, in operation 1, we might look up a presentation component named "A" for the root object. In this case, the external object is not proxied, but, when it is returned from the traversal function, it is proxied (unless it is a a basic object) because the traversal function is proxied, and the result of calling a proxied function is proxied (unless the result is a basic object). Operation 3 proceeds in the same way. When we get to operation 4, we use a function for computing the default presentation of the result of operation 3. As with traversal, the result of getting the default presentation is either a proxied object or a basic object because the function for getting the default presentation is proxied. When we get to the last operation, we have either a proxied object or a basic object. If the result of operation 4 is a basic object, we simply convert it to a string and return it as the result page. If the result of operation 4 is a non-basic object, we invoke a render operation on it and return the result as a string. Note that an untrusted interpreter may or may not provide protection against excessive resource usage. Different interpreters will provide different levels of service with respect to limitations on resource usage. If an untrusted interpreter performs an attribute access, the trusted interpreter must proxy the result unless the result is a basic object. In summary, an untrusted interpreter assures that any access to assets is mediated through security proxies by creating an environment to run untrusted code and making sure that: - The only way to access anything from outside of the environment is to call functions that are proxied in the environment. - Results of any attribute access in the environment are proxied unless the results are basic objects. Security proxies ---------------- Security proxies are objects that wrap and mediate access to objects. The Python programming language used by Zope defines a set of specific named low-level operations. In addition to operations, Python objects can have attributes, used to represent data and methods. Attributes are accessed using a dot notation. Applications can, and usually do, define methods to provide extended object behaviors. Methods are accessed as attributes through the low-level operation named "__getattribute__". The Python code:: a.b() invokes 2 operations: 1. Use the low-level `__getattribute__` operation with the name "b". 2. Use the low-level '__call__' operation on the result of the first operation. For all operations except the `__getattribute__` and `__setattribute__` operations, security proxies have a permission value defined by the permission-declaration subsystem. Two special permission values indicate that access is either forbidden (never allowed) or public (always allowed). For all other permission values, the authorization subsystem is used to decide whether the subject has the permission for the proxied object. If the subject has the permission, then access to the operation is allowed. Otherwise, access is denied. For getting or setting attributes, a proxy has permissions for getting and a permission for setting attribute values for a given attribute name. As described above, these permissions may be one of the two special permission values indicating forbidden or public access, or another permission value that must be checked with the authorization system. For all objects, Zope defines the following operations to be always public: comparison "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" hash "__hash__" boolean value "__nonzero__" class introspection "__class__" interface introspection "__providedBy__", "__implements__" adaptation "__conform__" low-level string representation "__repr__" The result of an operation on a proxied object is a security proxy unless the result is a basic value. Basic objects ------------- Basic objects are safe immutable objects that contain only immutable subobjects. Examples of basic objects include: - Strings, - Integers (long and normal), - Floating-point objects, - Date-time objects, - Boolean objects (True and False), and - The special (nil) object, None. Basic objects are safe, so, as described earlier, operations on basic objects, other than attribute access, use only information contained within the objects or information passed to them. For this reason, basic objects cannot be used to access information outside of the untrusted interpreter environment. The decision not to proxy basic objects is largely an optimization. It allows low-level safe computation to be performed without unnecessary overhead, Note that a basic object could contain sensitive information, but such a basic object would need to be obtained by making a call on a proxied object. Therefore, the access to the basic object in the first place is mediated by the security functions. Rationale for mutable safe objects ---------------------------------- Some safe objects are not basic. For these objects, we proxy the objects if they originate from outside of the environment. We do this for two reasons: 1. Non-basic objects from outside the environment need to be proxied to prevent unauthorized access to information. 2. We need to prevent un-mediated change of information from outside of the environment. We don't proxy safe objects created within the environment. This is safe to do because such safe objects can contain and provide access to information already in the environment. Sometimes the interpreter or the interpreted program needs to be able to create simple data containers to hold information computed in the course of the program execution. Several safe container types are provided for this purpose. zope.security-3.8.3/src/zope/security/untrustedpython/0000755000076600000240000000000011637266524023162 5ustar macstaff00000000000000zope.security-3.8.3/src/zope/security/untrustedpython/__init__.py0000644000076600000240000000000211637266446025266 0ustar macstaff00000000000000# zope.security-3.8.3/src/zope/security/untrustedpython/builtins.py0000644000076600000240000001116511637266446025374 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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 # ############################################################################## """Protection of builtin objects. """ from zope.security.proxy import ProxyFactory import new def SafeBuiltins(): builtins = {} from zope.security.checker import NamesChecker import __builtin__ _builtinTypeChecker = NamesChecker( ['__str__', '__repr__', '__name__', '__module__', '__bases__', '__call__']) # It's better to say what is safe than it say what is not safe for name in [ # Names of safe objects. See untrustedinterpreter.txt for a # definition of safe objects. 'ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'FloatingPointError', 'IOError', 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__debug__', '__name__', '__doc__', 'abs', 'apply', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'divmod', 'filter', 'float', 'frozenset', 'getattr', 'hasattr', 'hash', 'hex', 'id', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'long', 'map', 'max', 'min', 'object', 'oct', 'ord', 'pow', 'property', 'quit', 'range', 'reduce', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip', 'True', 'False', # TODO: dir segfaults with a seg fault due to a bad tuple # check in merge_class_dict in object.c. The assert macro # seems to be doing the wrong think. Basically, if an object # has bases, then bases is assumed to be a tuple. #dir, ]: try: value = getattr(__builtin__, name) except AttributeError: pass else: if isinstance(value, type): value = ProxyFactory(value, _builtinTypeChecker) else: value = ProxyFactory(value) builtins[name] = value from sys import modules def _imp(name, fromlist, prefix=''): module = modules.get(prefix+name) if module is not None: if fromlist or ('.' not in name): return module return modules[prefix+name.split('.')[0]] def __import__(name, globals=None, locals=None, fromlist=()): # Waaa, we have to emulate __import__'s weird semantics. if globals: __name__ = globals.get('__name__') if __name__: # Maybe do a relative import if '__path__' not in globals: # We have an ordinary module, not a package, # so remove last name segment: __name__ = '.'.join(__name__.split('.')[:-1]) if __name__: module = _imp(name, fromlist, __name__+'.') if module is not None: return module module = _imp(name, fromlist) if module is not None: return module raise ImportError(name) builtins['__import__'] = ProxyFactory(__import__) return builtins class ImmutableModule(new.module): def __init__(self, name='__builtins__', **kw): new.module.__init__(self, name) self.__dict__.update(kw) def __setattr__(self, name, v): raise AttributeError(name) def __delattr__(self, name): raise AttributeError(name) SafeBuiltins = ImmutableModule(**SafeBuiltins()) zope.security-3.8.3/src/zope/security/untrustedpython/builtins.txt0000644000076600000240000000612211637266446025560 0ustar macstaff00000000000000Safe Builtins ============= When executing untrusted Python code, we need to make sure that we only give the code access to safe, basic or proxied objects. This included the builtin objects provided to Python code through a special __builtins__ module in globals. The `builtins` module provides a suitable module object: >>> from zope.security.untrustedpython.builtins import SafeBuiltins >>> d = {'__builtins__': SafeBuiltins} >>> exec 'x = str(1)' in d >>> d['x'] '1' The object is immutable: >>> SafeBuiltins.foo = 1 Traceback (most recent call last): ... AttributeError: foo >>> del SafeBuiltins['getattr'] Traceback (most recent call last): ... TypeError: object does not support item deletion Exception raised: ... TypeError: object does not support item deletion (Note that you can mutate it through its `__dict__` attribute, however, when combined with the untrusted code compiler, getting the `__dict__` attribute will return a proxied object that will prevent mutation.) It contains items with keys that are all strings and values that are either proxied or are basic types: >>> from zope.security.proxy import Proxy >>> for key, value in SafeBuiltins.__dict__.items(): ... if not isinstance(key, str): ... raise TypeError(key) ... if value is not None and not isinstance(value, (Proxy, int, str)): ... raise TypeError(value, key) It doesn't contain unsafe items, such as eval, globals, etc: >>> SafeBuiltins.eval Traceback (most recent call last): ... AttributeError: 'ImmutableModule' object has no attribute 'eval' >>> SafeBuiltins.globals Traceback (most recent call last): ... AttributeError: 'ImmutableModule' object has no attribute 'globals' The safe builtins also contains a custom __import__ function. >>> imp = SafeBuiltins.__import__ As with regular import, it only returns the top-level package if no fromlist is specified: >>> import zope.security >>> imp('zope.security') == zope True >>> imp('zope.security', {}, {}, ['*']) == zope.security True Note that the values returned are proxied: >>> type(imp('zope.security')) is Proxy True This means that, having imported a module, you will only be able to access attributes for which you are authorized. Unlike regular __import__, you can only import modules that have been previously imported. This is to prevent unauthorized execution of module-initialization code: >>> security = zope.security >>> import sys >>> del sys.modules['zope.security'] >>> imp('zope.security') Traceback (most recent call last): ... ImportError: zope.security >>> sys.modules['zope.security'] = security Package-relative imports are supported (for now): >>> imp('security', {'__name__': 'zope', '__path__': []}) == security True >>> imp('security', {'__name__': 'zope.foo'}) == zope.security True >>> imp('security.untrustedpython', {'__name__': 'zope.foo'}) == security True >>> from zope.security import untrustedpython >>> imp('security.untrustedpython', {'__name__': 'zope.foo'}, {}, ['*'] ... ) == untrustedpython True zope.security-3.8.3/src/zope/security/untrustedpython/interpreter.py0000644000076600000240000000504711637266446026110 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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 # ############################################################################## """Restricted interpreter. TODO: This code needs a serious security review!!! """ from zope.security.untrustedpython.builtins import SafeBuiltins from zope.security.untrustedpython.rcompile import compile import warnings class RestrictedInterpreter(object): def __init__(self): warnings.warn("RestrictedInterpreter was deprecated 2004/7/27", DeprecationWarning, 2) self.globals = {} self.locals = {} def ri_exec(self, code): """Execute Python code in a restricted environment. The value of code can be either source or binary code.""" if isinstance(code, basestring): code = compile(code, '', 'exec') self.globals['__builtins__'] = SafeBuiltins exec code in self.globals, self.locals def exec_code(code, globals, locals=None): globals['__builtins__'] = SafeBuiltins exec code in globals, locals def exec_src(source, globals, locals=None): globals['__builtins__'] = SafeBuiltins code = compile(source, '', 'exec') exec code in globals, locals class CompiledExpression(object): """A compiled expression """ def __init__(self, source, filename=''): self.source = source self.code = compile(source, filename, 'eval') def eval(self, globals, locals=None): globals['__builtins__'] = SafeBuiltins if locals is None: return eval(self.code, globals) else: return eval(self.code, globals) class CompiledProgram(object): """A compiled expression """ def __init__(self, source, filename=''): self.source = source self.code = compile(source, filename, 'exec') def exec_(self, globals, locals=None, output=None): globals['__builtins__'] = SafeBuiltins if output is not None: globals['untrusted_output'] = output exec self.code in globals, locals zope.security-3.8.3/src/zope/security/untrustedpython/interpreter.txt0000644000076600000240000000613511637266446026276 0ustar macstaff00000000000000Untrusted Python interpreter ============================ The interpreter module provides very basic Python interpreter support. It combined untrusted code compilation with safe builtins and an exec-like API. The exec_src function can be used to execute Python source: >>> from zope.security.untrustedpython.interpreter import exec_src >>> d = {} >>> exec_src("x=1", d) >>> d['x'] 1 >>> exec_src("x=getattr", d) Note that the safe builtins dictionary is inserted into the dictionary: >>> from zope.security.untrustedpython.builtins import SafeBuiltins >>> d['__builtins__'] == SafeBuiltins True All of the non-basic items in the safe builtins are proxied: >>> exec_src('str=str', d) >>> from zope.security.proxy import Proxy >>> type(d['str']) is Proxy True Note that, while you can get to the safe `__builtins__`'s dictionary, you can't use the dictionary to mutate it: >>> from zope.security.interfaces import ForbiddenAttribute >>> try: exec_src('__builtins__.__dict__["x"] = 1', d) ... except ForbiddenAttribute: print 'Forbidden!' Forbidden! >>> try: exec_src('del __builtins__.__dict__["str"]', d) ... except ForbiddenAttribute: print 'Forbidden!' Forbidden! >>> try: exec_src('__builtins__.__dict__.update({"x": 1})', d) ... except ForbiddenAttribute: print 'Forbidden!' Forbidden! Because the untrusted code compiler is used, you can't use exec, raise, or try/except statements: >>> exec_src("exec 'x=1'", d) Traceback (most recent call last): ... SyntaxError: Line 1: exec statements are not supported Any attribute-access results will be proxied: >>> exec_src("data = {}\nupdate = data.update\nupdate({'x': 'y'})", d) >>> type(d['update']) is Proxy True In this case, we were able to get to and use the update method because the data dictionary itself was created by the untrusted code and was, thus, unproxied. You can compile code yourself and call exec_code instead: >>> from zope.security.untrustedpython.rcompile import compile >>> code = compile('x=2', '', 'exec') >>> d = {} >>> from zope.security.untrustedpython.interpreter import exec_code >>> exec_code(code, d) >>> d['x'] 2 This is useful if you are going to be executing the same expression many times, as you can avoid the cost of repeated comilation. Compiled Programs ----------------- A slightly higher-level interface is provided by compiled programs. These make it easier to safetly safe the results of compilation: >>> from zope.security.untrustedpython.interpreter import CompiledProgram >>> p = CompiledProgram('x=2') >>> d = {} >>> p.exec_(d) >>> d['x'] 2 When you execute a compiled program, you can supply an object with a write method to get print output: >>> p = CompiledProgram('print "Hello world!"') >>> import cStringIO >>> f = cStringIO.StringIO() >>> p.exec_({}, output=f) >>> f.getvalue() 'Hello world!\n' Compiled Expressions -------------------- You can also precompile expressions: >>> from zope.security.untrustedpython.interpreter import CompiledExpression >>> p = CompiledExpression('x*2') >>> p.eval({'x': 2}) 4 zope.security-3.8.3/src/zope/security/untrustedpython/rcompile.py0000644000076600000240000000624211637266446025355 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """compile() equivalent that produces restricted code. Only 'eval' is supported at this time. """ import compiler.pycodegen import RestrictedPython.RCompile from RestrictedPython.SelectCompiler import ast, OP_ASSIGN, OP_DELETE, OP_APPLY def compile(text, filename, mode): if not isinstance(text, basestring): raise TypeError("Compiled source must be string") gen = RExpression(text, str(filename), mode) gen.compile() return gen.getCode() class RExpression(RestrictedPython.RCompile.RestrictedCompileMode): CodeGeneratorClass = compiler.pycodegen.ExpressionCodeGenerator def __init__(self, source, filename, mode = "eval"): self.mode = mode RestrictedPython.RCompile.RestrictedCompileMode.__init__( self, source, filename) self.rm = RestrictionMutator() # The security checks are performed by a set of six functions that # must be provided by the restricted environment. _getattr_name = ast.Name("getattr") class RestrictionMutator: def __init__(self): self.errors = [] self.warnings = [] self.used_names = {} def error(self, node, info): """Records a security error discovered during compilation.""" lineno = getattr(node, 'lineno', None) if lineno is not None and lineno > 0: self.errors.append('Line %d: %s' % (lineno, info)) else: self.errors.append(info) def visitGetattr(self, node, walker): """Converts attribute access to a function call. 'foo.bar' becomes 'getattr(foo, "bar")'. Also prevents augmented assignment of attributes, which would be difficult to support correctly. """ node = walker.defaultVisitNode(node) return ast.CallFunc(_getattr_name, [node.expr, ast.Const(node.attrname)]) def visitExec(self, node, walker): self.error(node, "exec statements are not supported") def visitPrint(self, node, walker): """Make sure prints always have a destination If we get a print without a destination, make the default destination untrusted_output. """ node = walker.defaultVisitNode(node) if node.dest is None: node.dest = ast.Name('untrusted_output') return node visitPrintnl = visitPrint def visitRaise(self, node, walker): self.error(node, "raise statements are not supported") def visitTryExcept(self, node, walker): self.error(node, "try/except statements are not supported") zope.security-3.8.3/src/zope/security/untrustedpython/rcompile.txt0000644000076600000240000000763411637266446025552 0ustar macstaff00000000000000================================== Support for Restricted Python Code ================================== This package provides a way to compile untrusted Python code so that it can be executed safely. This form of restricted Python assumes that security proxies will be used to protect assets. Given this, the only thing that actually needs to be done differently by the generated code is to: - Ensure that all attribute lookups go through a safe version of the getattr() function that's been provided in the built-in functions used in the execution environment. - Prevent exec statements. (Later, we could possibly make exec safe.) - Print statements always go to an output that is provided as a global, rather than having an implicit sys.output. - Prevent try/except and raise statements. This is mainly because they don't work properly in the presense of security proxies. Try/except statements will be made to work in the future. No other special treatment is needed to support safe expression evaluation. The implementation makes use of the `RestrictedPython` package, originally written for Zope 2. There is a new AST re-writer in `zope.security.untrustedpython.rcompile` which performs the tree-transformation, and a top-level `compile()` function in `zope.security.untrustedpython.rcompile`; the later is what client applications are expected to use. The signature of the `compile()` function is very similar to that of Python's built-in `compile()` function:: compile(source, filename, mode) Using it is equally simple:: >>> from zope.security.untrustedpython.rcompile import compile >>> code = compile("21 * 2", "", "eval") >>> eval(code) 42 What's interesting about the restricted code is that all attribute lookups go through the `getattr()` function. This is generally provided as a built-in function in the restricted environment:: >>> def mygetattr(object, name, default="Yahoo!"): ... marker = [] ... print "Looking up", name ... if getattr(object, name, marker) is marker: ... return default ... else: ... return "Yeehaw!" >>> import __builtin__ >>> builtins = __builtin__.__dict__.copy() >>> builtins["getattr"] = mygetattr >>> def reval(source): ... code = compile(source, "README.txt", "eval") ... globals = {"__builtins__": builtins} ... return eval(code, globals, {}) >>> reval("(42).__class__") Looking up __class__ 'Yeehaw!' >>> reval("(42).not_really_there") Looking up not_really_there 'Yahoo!' >>> reval("(42).foo.not_really_there") Looking up foo Looking up not_really_there 'Yahoo!' This allows a `getattr()` to be used that ensures the result of evaluation is a security proxy. To compile code with statements, use exec or single: >>> exec compile("x = 1", "", "exec") >>> x 1 Trying to compile exec, raise or try/except sattements gives syntax errors: >>> compile("exec 'x = 2'", "", "exec") Traceback (most recent call last): ... SyntaxError: Line 1: exec statements are not supported >>> compile("raise KeyError('x')", "", "exec") Traceback (most recent call last): ... SyntaxError: Line 1: raise statements are not supported >>> compile("try: pass\nexcept: pass", "", "exec") Traceback (most recent call last): ... SyntaxError: Line 1: try/except statements are not supported Printing to an explicit writable is allowed: >>> import StringIO >>> f = StringIO.StringIO() >>> code = compile("print >> f, 'hi',\nprint >> f, 'world'", '', 'exec') >>> exec code in {'f': f} >>> f.getvalue() 'hi world\n' But if no output is specified, then output will be send to `untrusted_output`: >>> code = compile("print 'hi',\nprint 'world'", '', 'exec') >>> exec code in {} Traceback (most recent call last): ... NameError: name 'untrusted_output' is not defined >>> f = StringIO.StringIO() >>> exec code in {'untrusted_output': f} zope.security-3.8.3/src/zope/security/untrustedpython/tests.py0000644000076600000240000000233411637266446024703 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Untrusted python tests """ import doctest import unittest import re from zope.testing import renormalizing def test_suite(): try: import RestrictedPython except ImportError: return unittest.TestSuite() checker = renormalizing.RENormalizing([ (re.compile(r"'ImmutableModule' object"), r'object'), ]) return unittest.TestSuite(( doctest.DocFileSuite('builtins.txt', 'rcompile.txt', 'interpreter.txt', checker=checker, ), )) zope.security-3.8.3/src/zope/security/zcml.py0000644000076600000240000001111711637266447021207 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## """Security related configuration fields. """ __docformat__ = 'restructuredtext' import zope.schema from zope.interface import Interface, implements from zope.schema.interfaces import IFromUnicode from zope.security.permission import checkPermission from zope.security.management import setSecurityPolicy from zope.configuration.fields import MessageID, GlobalObject class Permission(zope.schema.Id): r"""This field describes a permission. Let's look at an example: >>> class FauxContext(object): ... permission_mapping = {'zope.ManageCode':'zope.private'} ... _actions = [] ... def action(self, **kws): ... self._actions.append(kws) >>> context = FauxContext() >>> field = Permission().bind(context) Let's test the fromUnicode method: >>> field.fromUnicode(u'zope.foo') 'zope.foo' >>> field.fromUnicode(u'zope.ManageCode') 'zope.private' Now let's see whether validation works alright >>> field._validate('zope.ManageCode') >>> context._actions[0]['args'] (None, 'zope.foo') >>> field._validate('3 foo') Traceback (most recent call last): ... InvalidId: 3 foo zope.Public is always valid >>> field._validate('zope.Public') """ implements(IFromUnicode) def fromUnicode(self, u): u = super(Permission, self).fromUnicode(u) map = getattr(self.context, 'permission_mapping', {}) return map.get(u, u) def _validate(self, value): super(Permission, self)._validate(value) if value != 'zope.Public': self.context.action( discriminator = None, callable = checkPermission, args = (None, value), # Delay execution till end. This is an # optimization. We don't want to intersperse utility # lookup, done when checking permissions, with utility # definitions. Utility lookup is expensive after # utility definition, as extensive caches have to be # rebuilt. order=9999999, ) class ISecurityPolicyDirective(Interface): """Defines the security policy that will be used for Zope.""" component = GlobalObject( title=u"Component", description=u"Pointer to the object that will handle the security.", required=True) def securityPolicy(_context, component): _context.action( discriminator = 'defaultPolicy', callable = setSecurityPolicy, args = (component,) ) class IPermissionDirective(Interface): """Define a new security object.""" id = zope.schema.Id( title=u"Id", description=u"Id as which this object will be known and used.", required=True) title = MessageID( title=u"Title", description=u"Provides a title for the object.", required=True) description = MessageID( title=u"Description", description=u"Provides a description for the object.", required=False) def permission(_context, id, title, description=''): from zope.security.interfaces import IPermission from zope.security.permission import Permission from zope.component.zcml import utility permission = Permission(id, title, description) utility(_context, IPermission, permission, name=id) class IRedefinePermission(Interface): """Define a permission to replace another permission.""" from_ = Permission( title=u"Original permission", description=u"Original permission id to redefine.", required=True) to = Permission( title=u"Substituted permission", description=u"Substituted permission id.", required=True) def redefinePermission(_context, from_, to): _context = _context.context # check if context has any permission mappings yet if not hasattr(_context, 'permission_mapping'): _context.permission_mapping={} _context.permission_mapping[from_] = to zope.security-3.8.3/src/zope.security.egg-info/0000755000076600000240000000000011637266524021354 5ustar macstaff00000000000000zope.security-3.8.3/src/zope.security.egg-info/dependency_links.txt0000644000076600000240000000000111637266520025416 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope.security.egg-info/namespace_packages.txt0000644000076600000240000000000511637266520025676 0ustar macstaff00000000000000zope zope.security-3.8.3/src/zope.security.egg-info/not-zip-safe0000644000076600000240000000000111637266454023604 0ustar macstaff00000000000000 zope.security-3.8.3/src/zope.security.egg-info/PKG-INFO0000644000076600000240000011162511637266520022453 0ustar macstaff00000000000000Metadata-Version: 1.0 Name: zope.security Version: 3.8.3 Summary: Zope Security Framework Home-page: http://pypi.python.org/pypi/zope.security Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: The Security framework provides a generic mechanism to implement security policies on Python objects. .. contents:: ============== Zope3 Security ============== Introduction ------------ The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions ----------- Principal ~~~~~~~~~ A generalization of a concept of a user. Permission ~~~~~~~~~~ A kind of access, i.e. permission to READ vs. permission to WRITE. Fundamentally the whole security framework is organized around checking permissions on objects. Purpose ------- The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and implicit security checks on attribute access for objects. Attribute names are mapped onto permission names when checking access and the implementation of the security check is defined by the security policy, which receives the object, the permission name, and an interaction. Interactions are objects that represent the use of the system by one or more principals. An interaction contains a list of participations, which represents the way a single principal participates in the interaction. An HTTP request is one example of a participation. Its important to keep in mind that the policy provided is just a default, and it can be substituted with one which doesn't care about principals or interactions at all. Framework Components -------------------- Low Level Components ~~~~~~~~~~~~~~~~~~~~ These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. Checkers ~~~~~~~~ A checker is associated with an object kind, and provides the hooks that map attribute checks onto permissions deferring to the security manager (which in turn defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. There are several implementation variants of checkers, such as checkers that grant access based on attribute names. Proxies ~~~~~~~ Wrappers around Python objects that implicitly guard access to their wrapped contents by delegating to their associated checker. Proxies are also viral in nature, in that values returned by proxies are also proxied. High Level Components --------------------- Security Management ~~~~~~~~~~~~~~~~~~~ Provides accessors for setting up interactions and the global security policy. Interaction ~~~~~~~~~~~ Stores transient information on the list of participations. Participation ~~~~~~~~~~~~~ Stores information about a principal participating in the interaction. Security Policy ~~~~~~~~~~~~~~~ Provides a single method that accepts the object, the permission, and the interaction of the access being checked and is used to implement the application logic for the security framework. Narrative (agent sandbox) ------------------------- As an example we take a look at constructing a multi-agent distributed system, and then adding a security layer using the Zope security model onto it. Scenario ~~~~~~~~ Our agent simulation consists of autonomous agents that live in various agent homes/sandboxes and perform actions that access services available at their current home. Agents carry around authentication tokens which signify their level of access within any given home. Additionally agents attempt to migrate from home to home randomly. The agent simulation was constructed separately from any security aspects. Now we want to define and integrate a security model into the simulation. The full code for the simulation and the security model is available separately; we present only relevant code snippets here for illustration as we go through the implementation process. For the agent simulation we want to add a security model such that we group agents into two authentication groups, "norse legends", including the principals thor, odin, and loki, and "greek men", including prometheus, archimedes, and thucydides. We associate permissions with access to services and homes. We differentiate the homes such that certain authentication groups only have access to services or the home itself based on the local settings of the home in which they reside. We define the homes/sandboxes - origin - all agents start here, and have access to all services here. - valhalla - only agents in the authentication group 'norse legend' can reside here. - jail - all agents can come here, but only 'norse legend's can leave or access services. Process ~~~~~~~ Loosely we define a process for implementing this security model - mapping permissions onto actions - mapping authentication tokens onto permissions - implementing checkers and security policies that use our authentication tokens and permissions. - binding checkers to our simulation classes - inserting the hooks into the original simulation code to add proxy wrappers to automatically check security. - inserting hooks into the original simulation to register the agents as the active principal in an interaction. Defining a Permission Model ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We define the following permissions:: NotAllowed = 'Not Allowed' Public = Checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' and create a dictionary database mapping homes to authentication groups which are linked to associated permissions. Defining and Binding Checkers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Checkers are the foundational unit for the security framework. They define what attributes can be accessed or set on a given instance. They can be used implicitly via Proxy objects, to guard all attribute access automatically or explicitly to check a given access for an operation. Checker construction expects two functions or dictionaries, one is used to map attribute names to permissions for attribute access and another to do the same for setting attributes. We use the following checker factory function:: def PermissionMapChecker(permissions_map={}, setattr_permission_func=NoSetAttr): res = {} for k,v in permissions_map.items(): for iv in v: res[iv]=k return checker.Checker(res.get, setattr_permission_func) time_service_checker = PermissionMapChecker( # permission : [methods] {'AccessTimeService':['getTime']} ) with the NoSetAttr function defined as a lambda which always return the permission `NotAllowed`. To bind the checkers to the simulation classes we register our checkers with the security model's global checker registry:: import sandbox_simulation from zope.security.checker import defineChecker defineChecker(sandbox_simulation.TimeService, time_service_checker) Defining a Security Policy ~~~~~~~~~~~~~~~~~~~~~~~~~~ We implement our security policy such that it checks the current agent's authentication token against the given permission in the home of the object being accessed:: class SimulationSecurityPolicy: implements(ISecurityPolicy) createInteraction = staticmethod(simpleinteraction.createInteraction) def checkPermission(self, permission, object, interaction): home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if interaction is None: return False if not interaction.participations: return False for participation in interaction.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True There are no specific requirements for the interaction class, so we can just use `zope.security.simpleinteraction.Interaction`. Since an interaction can have more than one principal, we check that *all* of them are given the necessary permission. This is not really necessary since we only create interactions with a single active principal. There is some additional code present to allow for shortcuts in defining the permission database when defining permissions for all auth groups and all permissions. Integration ~~~~~~~~~~~ At this point we have implemented our security model, and we need to integrate it with our simulation model. We do so in three separate steps. First we make it such that agents only access homes that are wrapped in a security proxy. By doing this all access to homes and services (proxies have proxied return values for their methods) is implicitly guarded by our security policy. The second step is that we want to associate the active agent with the security context so the security policy will know which agent's authentication token to validate against. The third step is to set our security policy as the default policy for the Zope security framework. It is possible to create custom security policies at a finer grained than global, but such is left as an exercise for the reader. Interaction Access ~~~~~~~~~~~~~~~~~~ The *default* implementation of the interaction management interfaces defines interactions on a per thread basis with a function for an accessor. This model is not appropriate for all systems, as it restricts one to a single active interaction per thread at any given moment. Reimplementing the interaction access methods though is easily doable and is noted here for completeness. Perspectives ~~~~~~~~~~~~ It's important to keep in mind that there is a lot more that is possible using the security framework than what's been presented here. All of the interactions are interface based, such that if you need to re-implement the semantics to suite your application a new implementation of the interface will be sufficient. Additional possibilities range from restricted interpreters and dynamic loading of untrusted code to non Zope web application security systems. Insert imagination here ;-). Zope Perspective ~~~~~~~~~~~~~~~~ A Zope3 programmer will never commonly need to interact with the low level security framework. Zope3 defines a second security package over top the low level framework and authentication sources and checkers are handled via zcml registration. Still those developing Zope3 will hopefully find this useful as an introduction into the underpinnings of the security framework. Code ~~~~ The complete code for this example is available. - sandbox.py - the agent framework - sandbox_security.py - the security implementation and binding to the agent framework. Authors ~~~~~~~ - Kapil Thangavelu - Guido Wesdorp - Marius Gedminas ====================== Untrusted interpreters ====================== Untrusted programs are executed by untrusted interpreters. Untrusted interpreters make use of security proxies to prevent un-mediated access to assets. An untrusted interpreter defines an environment for running untrusted programs. All objects within the environment are either: - "safe" objects created internally by the environment or created in the course of executing the untrusted program, or - "basic" objects - security-proxied non-basic objects The environment includes proxied functions for accessing objects outside of the environment. These proxied functions provide the only way to access information outside the environment. Because these functions are proxied, as described below, any access to objects outside the environment is mediated by the target security functions. Safe objects are objects whose operations, except for attribute retrieval, and methods access only information stored within the objects or passed as arguments. Safe objects contained within the interpreter environment can contain only information that is already in the environment or computed directly from information that is included in the environment. For this reason, safe objects created within the environment cannot be used to directly access information outside the environment. Safe objects have some attributes that could (very) indirectly be used to access assets. For this reason, an untrusted interpreter always proxies the results of attribute accesses on a safe objects. Basic objects are safe objects that are used to represent elemental data values such as strings and numbers. Basic objects require a lower level of protection than non-basic objects, as will be described detail in a later section. Security proxies mediate all object operations. Any operation access is checked to see whether a subject is authorized to perform the operation. All operation results other than basic objects are, in turn, security proxied. Security proxies will be described in greater detail in a later section. Any operation on a security proxy that results in a non-basic object is also security proxied. All external resources needed to perform an operation are security proxied. Let's consider the trusted interpreter for evaluating URLs. In operation 1 of the example, the interpreter uses a proxied method for getting the system root object. Because the method is proxied, the result of calling the method and the operation is also proxied. The interpreter has a function for traversing objects. This function is proxied. When traversing an object, the function is passed an object and a name. In operation 2, the function is passed the result of operation 1, which is the proxied root object and the name 'A'. We may traverse an object by invoking an operation on it. For example, we may use an operation to get a sub-object. Because any operation on a proxied object returns a proxied object or a basic object, the result is either a proxied object or a basic object. Traversal may also look up a component. For example, in operation 1, we might look up a presentation component named "A" for the root object. In this case, the external object is not proxied, but, when it is returned from the traversal function, it is proxied (unless it is a a basic object) because the traversal function is proxied, and the result of calling a proxied function is proxied (unless the result is a basic object). Operation 3 proceeds in the same way. When we get to operation 4, we use a function for computing the default presentation of the result of operation 3. As with traversal, the result of getting the default presentation is either a proxied object or a basic object because the function for getting the default presentation is proxied. When we get to the last operation, we have either a proxied object or a basic object. If the result of operation 4 is a basic object, we simply convert it to a string and return it as the result page. If the result of operation 4 is a non-basic object, we invoke a render operation on it and return the result as a string. Note that an untrusted interpreter may or may not provide protection against excessive resource usage. Different interpreters will provide different levels of service with respect to limitations on resource usage. If an untrusted interpreter performs an attribute access, the trusted interpreter must proxy the result unless the result is a basic object. In summary, an untrusted interpreter assures that any access to assets is mediated through security proxies by creating an environment to run untrusted code and making sure that: - The only way to access anything from outside of the environment is to call functions that are proxied in the environment. - Results of any attribute access in the environment are proxied unless the results are basic objects. Security proxies ---------------- Security proxies are objects that wrap and mediate access to objects. The Python programming language used by Zope defines a set of specific named low-level operations. In addition to operations, Python objects can have attributes, used to represent data and methods. Attributes are accessed using a dot notation. Applications can, and usually do, define methods to provide extended object behaviors. Methods are accessed as attributes through the low-level operation named "__getattribute__". The Python code:: a.b() invokes 2 operations: 1. Use the low-level `__getattribute__` operation with the name "b". 2. Use the low-level '__call__' operation on the result of the first operation. For all operations except the `__getattribute__` and `__setattribute__` operations, security proxies have a permission value defined by the permission-declaration subsystem. Two special permission values indicate that access is either forbidden (never allowed) or public (always allowed). For all other permission values, the authorization subsystem is used to decide whether the subject has the permission for the proxied object. If the subject has the permission, then access to the operation is allowed. Otherwise, access is denied. For getting or setting attributes, a proxy has permissions for getting and a permission for setting attribute values for a given attribute name. As described above, these permissions may be one of the two special permission values indicating forbidden or public access, or another permission value that must be checked with the authorization system. For all objects, Zope defines the following operations to be always public: comparison "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" hash "__hash__" boolean value "__nonzero__" class introspection "__class__" interface introspection "__providedBy__", "__implements__" adaptation "__conform__" low-level string representation "__repr__" The result of an operation on a proxied object is a security proxy unless the result is a basic value. Basic objects ------------- Basic objects are safe immutable objects that contain only immutable subobjects. Examples of basic objects include: - Strings, - Integers (long and normal), - Floating-point objects, - Date-time objects, - Boolean objects (True and False), and - The special (nil) object, None. Basic objects are safe, so, as described earlier, operations on basic objects, other than attribute access, use only information contained within the objects or information passed to them. For this reason, basic objects cannot be used to access information outside of the untrusted interpreter environment. The decision not to proxy basic objects is largely an optimization. It allows low-level safe computation to be performed without unnecessary overhead, Note that a basic object could contain sensitive information, but such a basic object would need to be obtained by making a call on a proxied object. Therefore, the access to the basic object in the first place is mediated by the security functions. Rationale for mutable safe objects ---------------------------------- Some safe objects are not basic. For these objects, we proxy the objects if they originate from outside of the environment. We do this for two reasons: 1. Non-basic objects from outside the environment need to be proxied to prevent unauthorized access to information. 2. We need to prevent un-mediated change of information from outside of the environment. We don't proxy safe objects created within the environment. This is safe to do because such safe objects can contain and provide access to information already in the environment. Sometimes the interpreter or the interpreted program needs to be able to create simple data containers to hold information computed in the course of the program execution. Several safe container types are provided for this purpose. ======= CHANGES ======= 3.8.3 (2011-09-24) ------------------ - Fixed a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ------------------ - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ------------------ - Fixed circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ------------------ - Added tests for our own ``configure.zcml``. - Added ``zcml`` extra dependencies, run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ------------------ - Prefer the standard libraries doctest module to the one from zope.testing. - Fixed directlyProvides IVocabularyFactory for PermissionIdsVocabulary in Python code, even if it's unnecessary because IVocabularyFactory is provided in zcml. - Removed the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ------------------ - Added compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ------------------ - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ------------------ - Made ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ------------------ - Ensure that simple zope.schema's VocabularyRegistry is used for PermissionVocabulary tests, because it's replaced implicitly in environments with zope.app.schema installed that makes that tests fail. - Fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ------------------ - Add zope.i18nmessageid.Message to non-proxied basic types. It's okay, because messages are immutable. It was done by zope.app.security before. - Add "__name__" and "__parent__" attributes to list of available by default. This was also done by zope.app.security package before. - Added PermissionsVocabulary and PermissionIdsVocabulary vocabularies to the ``zope.security.permission`` module. They were moved from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like "zope.View" and "zope.ManageContent", as well as for the special "zope.Public" permission. They are placed in a separate "permissions.zcml" file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many zope.* packages. - Add `addCheckerPublic` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ------------------ - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise NoInteraction when zope.security.checkPermission is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and `set` and `frozenset` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ------------------ - Install decorated security checker support on LocationProxy from the outside. - Added support to bootstrap on Jython. - Moved the `protectclass` module from `zope.app.security` to this package to reduce the number of dependencies on `zope.app.security`. - Moved the directive implementation from `zope.app.security` to this package. - Moved the directive implementation from `zope.app.component` to this package. 3.5.2 (2008-07-27) ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ------------------ - Add `frozenset`, `set`, `reversed`, and `sorted` to the list of safe builtins. 3.5.0 (2008-03-05) ------------------ - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) -------------------- - Backported a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) -------------------- - Added dependency 'zope.thread' to setup.py, without the tests were failing. - Backported a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ------------------ - Made C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ------------------ - Updated meta-data. 3.4.0b5 (2007-08-15) -------------------- - Bug: Fixed a circular import in the C implementation. 3.4.0b4 (2007-08-14) -------------------- - Bug: ``zope.security.management.system_user`` had an ugly/brittle id. 3.4.0b3 (2007-08-14) -------------------- - ``zope.security`` now works on Python 2.5 - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fixed inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) -------------------- - Bug: Removed stack extraction in newInteraction. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ------- - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.2.0 release. - Removed deprecated helper functions, 'proxy.trustedRemoveSecurityProxy' and 'proxy.getProxiedObject'. - Made handling of 'management.{end,restore}Interaction' more careful w.r.t. edge cases. - Made behavior of 'canWrite' consistent with 'canAccess': if 'canAccess' does not raise 'ForbiddenAttribute', then neither will 'canWrite'. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ------------------ - Added support for use of the new Python 2.4 datatypes, 'set' and 'frozenset', within checked code. - C security proxy acquired a dependency on the 'proxy.h' header from the 'zope.proxy' package. - XXX: the spelling of the '#include' is bizarre! It seems to be related to 'zpkg'-based builds, and should likely be revisited. For the moment, I have linked in the 'zope.proxy' package into our own 'include' directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Updated checker to avoid re-proxying objects which have and explicit '__Security_checker__' assigned. - Corresponds to the verison of the zope.security package shipped as part of the Zope 3.1.0 release. - Clarified contract of 'IChecker' to indicate that its 'check*' methods may raise only 'Forbidden' or 'Unauthorized' exceptions. - Added interfaces, ('IPrincipal', 'IGroupAwarePrincipal', 'IGroup', and 'IPermission') specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ------------------ - Corresponds to the version of the zope.security package shipped as part of the Zope X3.0.0 release. Keywords: zope security policy principal permission Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.security-3.8.3/src/zope.security.egg-info/requires.txt0000644000076600000240000000031211637266520023744 0ustar macstaff00000000000000setuptools zope.component zope.i18nmessageid zope.interface zope.location zope.proxy >= 3.4.2 zope.schema [test] zope.testing [pytz] pytz [zcml] zope.configuration [untrustedpython] RestrictedPythonzope.security-3.8.3/src/zope.security.egg-info/SOURCES.txt0000644000076600000240000000561111637266520023237 0ustar macstaff00000000000000CHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py include/zope.proxy/__init__.py include/zope.proxy/_zope_proxy_proxy.c include/zope.proxy/decorator.py include/zope.proxy/interfaces.py include/zope.proxy/proxy.h include/zope.proxy/tests/__init__.py include/zope.proxy/tests/test_decorator.py include/zope.proxy/tests/test_proxy.py src/zope/__init__.py src/zope.security.egg-info/PKG-INFO src/zope.security.egg-info/SOURCES.txt src/zope.security.egg-info/dependency_links.txt src/zope.security.egg-info/namespace_packages.txt src/zope.security.egg-info/not-zip-safe src/zope.security.egg-info/requires.txt src/zope.security.egg-info/top_level.txt src/zope/security/README.txt src/zope/security/__init__.py src/zope/security/_definitions.py src/zope/security/_proxy.c src/zope/security/_zope_security_checker.c src/zope/security/adapter.py src/zope/security/checker.py src/zope/security/configure.zcml src/zope/security/decorator.py src/zope/security/i18n.py src/zope/security/interfaces.py src/zope/security/management.py src/zope/security/meta.zcml src/zope/security/metaconfigure.py src/zope/security/metadirectives.py src/zope/security/permission.py src/zope/security/permissions.zcml src/zope/security/protectclass.py src/zope/security/proxy.py src/zope/security/setup.py src/zope/security/simplepolicies.py src/zope/security/testing.py src/zope/security/untrustedinterpreter.txt src/zope/security/zcml.py src/zope/security/examples/sandbox.py src/zope/security/examples/sandbox_security.py src/zope/security/tests/__init__.py src/zope/security/tests/adapter.py src/zope/security/tests/components.py src/zope/security/tests/configure.txt src/zope/security/tests/emptymodule.py src/zope/security/tests/exampleclass.py src/zope/security/tests/module.py src/zope/security/tests/modulehookup.py src/zope/security/tests/redefineperms.zcml src/zope/security/tests/test_adapter.py src/zope/security/tests/test_checker.py src/zope/security/tests/test_configure.py src/zope/security/tests/test_contentdirective.py src/zope/security/tests/test_decorator.py src/zope/security/tests/test_directives.py src/zope/security/tests/test_location.py src/zope/security/tests/test_management.py src/zope/security/tests/test_module_directives.py src/zope/security/tests/test_permission.py src/zope/security/tests/test_protectclass.py src/zope/security/tests/test_protectsubclass.py src/zope/security/tests/test_proxy.py src/zope/security/tests/test_set_checkers.py src/zope/security/tests/test_simpleinteraction.py src/zope/security/tests/test_standard_checkers.py src/zope/security/untrustedpython/__init__.py src/zope/security/untrustedpython/builtins.py src/zope/security/untrustedpython/builtins.txt src/zope/security/untrustedpython/interpreter.py src/zope/security/untrustedpython/interpreter.txt src/zope/security/untrustedpython/rcompile.py src/zope/security/untrustedpython/rcompile.txt src/zope/security/untrustedpython/tests.pyzope.security-3.8.3/src/zope.security.egg-info/top_level.txt0000644000076600000240000000000511637266520024075 0ustar macstaff00000000000000zope