[Zope3-checkins] CVS: Zope3/src/zope/security - .cvsignore:1.2 __init__.py:1.2 _proxy.c:1.2 checker.py:1.2 interfaces.py:1.2 proxy.py:1.2 readme.txt:1.2 restrictedbuiltins.py:1.2 restrictedinterpreter.py:1.2 securitycontext.py:1.2 securitymanagement.py:1.2 securitymanager.py:1.2 setup.py:1.2 simplesecuritypolicies.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:15:52 -0500
Update of /cvs-repository/Zope3/src/zope/security
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/security
Added Files:
.cvsignore __init__.py _proxy.c checker.py interfaces.py
proxy.py readme.txt restrictedbuiltins.py
restrictedinterpreter.py securitycontext.py
securitymanagement.py securitymanager.py setup.py
simplesecuritypolicies.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/security/.cvsignore 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/.cvsignore Wed Dec 25 09:15:21 2002
@@ -0,0 +1 @@
+build
=== Zope3/src/zope/security/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/__init__.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
=== Zope3/src/zope/security/_proxy.c 1.1 => 1.2 === (827/927 lines abridged)
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/_proxy.c Wed Dec 25 09:15:21 2002
@@ -0,0 +1,924 @@
+/*
+ * Security proxy.
+ */
+
+#include <Python.h>
+#include "zope/proxy/proxy.h"
+
+static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0;
+
+typedef struct {
+ ProxyObject proxy;
+ PyObject *proxy_checker;
+} SecurityProxy;
+
+#undef Proxy_Check
+#define Proxy_Check(proxy) \
+ PyObject_TypeCheck(proxy, &SecurityProxyType)
+
+#define Proxy_GetChecker(proxy) \
+ (((SecurityProxy *)proxy)->proxy_checker)
+
+/* Replace the "safe" version from the proxy.h API with a faster version. */
+#undef Proxy_GetObject
+#define Proxy_GetObject(o) \
+ (((SecurityProxy *)o)->proxy.proxy_object)
+
+
+static PyTypeObject SecurityProxyType;
+
+
+/*
+ * Machinery to call the checker.
+ */
+
+typedef PyObject *(*function1)(PyObject *);
+
+static int
+check(PyObject *checker, char *opname, PyObject *object)
+{
+ PyObject *checked;
+
+ checked = PyObject_CallMethod(checker, "check", "(Os)",
+ object, opname);
+ if (checked == NULL)
+ return 0;
+ Py_DECREF(checked);
+ return 1;
[-=- -=- -=- 827 lines omitted -=- -=- -=-]
+ PyErr_SetString(PyExc_TypeError,
+ "getChecker argument must be a _Proxy");
+ return NULL;
+ }
+ result = Proxy_GetChecker(arg);
+ Py_INCREF(result);
+ return result;
+}
+
+static PyMethodDef
+module_functions[] = {
+ {"getObject", module_getObject, METH_O, "get object from proxy"},
+ {"getChecker", module_getChecker, METH_O, "get checker from proxy"},
+ {NULL}
+};
+
+static char
+module___doc__[] = "Security proxy implementation.";
+
+void
+init_proxy(void)
+{
+ PyObject *m;
+
+ if (Proxy_Import() < 0)
+ return;
+
+ __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);
+}
=== Zope3/src/zope/security/checker.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/checker.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,429 @@
+from zope.security.interfaces import IChecker
+from zope.exceptions \
+ import Unauthorized, ForbiddenAttribute, Forbidden, DuplicationError
+from zope.interface.interfaces import IInterface
+from zope.interface import Interface
+from zope.security._proxy import _Proxy as Proxy
+from zope.security.interfaces import ISecurityProxyFactory
+from zope.security.securitymanagement import getSecurityManager
+import sys, os, types
+import datetime
+
+if os.environ.get('ZOPE_WATCH_CHECKERS'):
+ WATCH_CHECKERS = 1
+else:
+ WATCH_CHECKERS = 0
+
+
+# Marker for public attributes
+CheckerPublic = object()
+
+def ProxyFactory(object, checker=None):
+ """Factory function that creates a proxy for an object
+
+ The proxy checker is looked up if not provided.
+ """
+
+ if checker is None:
+ checker = getattr(object, '__Security_checker__', None)
+
+ if checker is None:
+
+ checker = selectChecker(object)
+ if checker is None:
+ return object
+
+ else:
+ # Maybe someone passed us a proxy and a checker
+ if type(object) is Proxy:
+ # XXX should we keep the existing proxy or create a new one.
+ return object
+
+ return Proxy(object, checker)
+
+ProxyFactory.__implements__ = ISecurityProxyFactory
+
+class Checker:
+
+ __implements__ = IChecker
+
+ def __init__(self, permission_func,
+ setattr_permission_func=lambda name: None
+ ):
+ """Create a checker
+
+ A dictionary or a callable must be provided for computing
+ permissions for names. The callable will be called with
+ attribute names and must return a permission id, None, or the
+ special marker, CheckerPublic. If None is returned, then
+ access to the name is forbidden. If CheckerPublic is returned,
+ then access will be granted without checking a permission.
+
+ An optional setattr permission function or dictionary may be
+ provided for checking set attribute access.
+ """
+
+ if type(permission_func) is dict:
+ permission_func = permission_func.get
+ self._permission_func = permission_func
+
+ if type(setattr_permission_func) is dict:
+ setattr_permission_func = setattr_permission_func.get
+ self._setattr_permission_func = setattr_permission_func
+
+
+ def getPermission_func(self):
+ return self._permission_func
+
+ def getSetattrPermission_func(self):
+ return self._setattr_permission_func
+
+ def permission_id(self, name):
+ """Return the result of calling the permission func
+ """
+ return self._permission_func(name)
+
+ def setattr_permission_id(self, name):
+ """Return the result of calling the permission func
+ """
+ return self._setattr_permission_func(name)
+
+ def check_getattr(self, object, name):
+ 'See IChecker'
+ self.check(object, name)
+
+ def check_setattr(self, object, name):
+ 'See IChecker'
+
+ if WATCH_CHECKERS:
+ print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
+
+ # We have the information we need already
+ permission = self._setattr_permission_func(name)
+ if permission:
+ if permission is CheckerPublic:
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Public.'
+ return # Public
+ manager = getSecurityManager()
+ if manager.checkPermission(permission, object):
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Granted.'
+ return
+ else:
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Unauthorized.'
+ raise Unauthorized(name=name)
+
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Forbidden.'
+
+ raise ForbiddenAttribute(name)
+
+ def check(self, object, name):
+ 'See IChecker'
+
+ if WATCH_CHECKERS:
+ print >> sys.stderr, ('Checking %r.%s:' % (object, name)),
+
+ # We have the information we need already
+ permission = self._permission_func(name)
+ if permission:
+ if permission is CheckerPublic:
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Public.'
+ return # Public
+ manager = getSecurityManager()
+ if manager.checkPermission(permission, object):
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Granted.'
+ return
+ else:
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Unauthorized.'
+ raise Unauthorized(name=name)
+ elif name in _always_available:
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Always available.'
+ return
+
+ if WATCH_CHECKERS:
+ print >> sys.stderr, 'Forbidden.'
+
+ raise ForbiddenAttribute(name)
+
+ def proxy(self, value):
+ 'See IChecker'
+ # Now we need to create a proxy
+
+ checker = getattr(value, '__Security_checker__', None)
+ if checker is None:
+ checker = selectChecker(value)
+ if checker is None:
+ return value
+
+ return Proxy(value, checker)
+
+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 persmission 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.get)
+
+def InterfaceChecker(interface, permission_id=CheckerPublic):
+ return NamesChecker(interface.names(1), permission_id)
+
+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 anitems 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.isImplementedBy(names):
+ names = names.names(1)
+ 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.get)
+
+def NonPrivateChecker(permission_id = CheckerPublic):
+
+ def check(name, permission_id=permission_id):
+ if name.startswith('_'):
+ return None
+ return permission_id
+
+ return Checker(check)
+
+
+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.
+
+ # XXX we really need formal proxy introspection
+
+ if type(object) is Proxy:
+ # Is this already a security proxy?
+ return None
+
+ checker = _getChecker(getattr(object, '__class__', type(object)),
+ _defaultChecker)
+
+ if checker is NoProxy:
+ return None
+
+ if checker is _defaultChecker and isinstance(object, Exception):
+ 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
+
+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 = {}
+_getChecker = _checkers.get
+
+_defaultChecker = Checker({}.get)
+
+def _instanceChecker(inst):
+ checker = _checkers.get(inst.__class__, _defaultChecker)
+ if checker is _defaultChecker and isinstance(inst, Exception):
+ return NoProxy # XXX we should be more careful
+ return checker
+
+def _classChecker(class_):
+ checker = _checkers.get(class_, _typeChecker)
+ if checker is _typeChecker and issubclass(class_, Exception):
+ return NoProxy # XXX we should be more careful
+
+ return checker
+
+def _moduleChecker(module):
+ return _checkers.get(module, _typeChecker)
+
+
+
+_always_available = ['__lt__', '__le__', '__eq__',
+ '__gt__', '__ge__', '__ne__',
+ '__hash__', '__nonzero__',
+ '__class__', '__implements__',
+ ]
+
+_callableChecker = NamesChecker(['__str__', '__repr__', '__name__',
+ '__call__'])
+_typeChecker = NamesChecker(['__str__', '__repr__', '__name__', '__module__',
+ '__bases__'])
+
+_interfaceChecker = NamesChecker(['__str__', '__repr__', '__name__',
+ '__module__', '__bases__', 'getBases',
+ 'isImplementedBy', 'extends'])
+
+_iteratorChecker = NamesChecker(['next'])
+
+BasicTypes = {
+ object: NoProxy,
+ int: NoProxy,
+ float: NoProxy,
+ long: NoProxy,
+ complex: NoProxy,
+ types.NoneType: NoProxy,
+ str: NoProxy,
+ unicode: NoProxy,
+ type(not 1): NoProxy, # Boolean, if available :)
+}
+
+class _Sequence(object):
+ def __len__(self): return 0
+ def __getitem__(self, i): raise IndexError
+
+_default_checkers = {
+ dict: NamesChecker(['__getitem__', '__len__', '__iter__',
+ 'get', 'has_key', '__copy__', '__str__', '__repr__',
+ 'keys', 'values', 'items',
+ 'iterkeys', 'iteritems', 'itervalues', '__contains__',
+ ]),
+ list: NamesChecker(['__getitem__', '__getslice__', '__len__', '__iter__',
+ '__contains__', 'index', 'count', '__str__',
+ '__repr__']),
+
+ # YAGNI: () a rock
+ tuple: NamesChecker(['__getitem__', '__getslice__', '__add__',
+ '__contains__', '__len__', '__iter__', '__iadd__',
+ '__str__', '__repr__']),
+ types.InstanceType: _instanceChecker,
+ Proxy: NoProxy,
+ types.ClassType: _classChecker,
+ types.FunctionType: _callableChecker,
+ types.MethodType: _callableChecker,
+ types.BuiltinFunctionType: _callableChecker,
+ types.BuiltinMethodType: _callableChecker,
+ type: _typeChecker,
+ types.ModuleType: _moduleChecker,
+ type(iter([])): _iteratorChecker, # same types in Python 2.2.1,
+ type(iter(())): _iteratorChecker, # different in Python 2.3
+ type(iter(_Sequence())): NamesChecker(['next']),
+ type(Interface): _interfaceChecker,
+ datetime.timedelta: NamesChecker(['__repr__', '__str__', '__add__',
+ '__radd__', '__sub__', '__rsub__',
+ '__neg__', '__pos__', '__abs__',
+ '__mul__', '__rmul__', '__div__',
+ '__floordiv__', '__cmp__', 'days',
+ 'seconds', 'microseconds']),
+ datetime.date: NamesChecker(['__repr__', '__str__', 'year', 'month', 'day',
+ 'timetuple', 'toordinal', '__cmp__',
+ '__hash__', 'ctime', 'strftime', '__add__',
+ '__radd__', '__sub__', '__rsub__', 'weekday',
+ 'isoweekday', 'isocalendar', 'isoformat',
+ 'min', 'max', 'resolution']),
+ datetime.datetime: NamesChecker(['__repr__', '__str__', 'year', 'month',
+ 'day', 'hour', 'minute', 'second',
+ 'microsecond', 'timetuple',
+ 'toordinal', '__cmp__',
+ '__hash__', 'ctime', 'strftime',
+ '__add__', '__radd__', '__sub__',
+ '__rsub__', 'weekday', 'isoweekday',
+ 'isocalendar', 'isoformat', 'min', 'max',
+ 'resolution']),
+ datetime.datetimetz: NamesChecker(['__repr__', '__str__', 'year', 'month',
+ 'day', 'hour', 'minute', 'second',
+ 'microsecond', 'tzinfo', 'timetuple',
+ 'utctimetuple', 'toordinal', '__cmp__',
+ '__hash__', 'ctime', 'strftime',
+ '__add__', '__radd__', '__sub__',
+ '__rsub__', 'weekday', 'isoweekday',
+ 'isocalendar', 'isoformat', 'min',
+ 'max', 'resolution', 'utcoffset',
+ 'tzname', 'dst']),
+ datetime.time: NamesChecker(['hour', 'minute', 'second', 'microsecond',
+ '__cmp__', '__hash__', '__repr__', '__str__',
+ 'isoformat', 'strftime', 'min', 'max',
+ 'resolution']),
+ datetime.timetz: NamesChecker(['hour', 'minute', 'second', 'microsecond',
+ '__cmp__', '__hash__', '__repr__',
+ '__str__', 'isoformat', 'strftime', 'min',
+ 'max', 'resolution', 'tzinfo', 'utcoffset',
+ 'tzname', 'dst'])
+}
+
+
+def _clear():
+ _checkers.clear()
+ _checkers.update(_default_checkers)
+ _checkers.update(BasicTypes)
+
+_clear()
+
+from zope.testing.cleanup import addCleanUp
+addCleanUp(_clear)
=== Zope3/src/zope/security/interfaces.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/interfaces.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,202 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Interfaces for security machinery.
+
+$Id$
+"""
+
+from zope.interface import Interface
+from zope.interface import Attribute
+
+
+class ISecurityManagementSetup( Interface ):
+ """
+ Infrastructure (including tests, etc.) calls these things to
+ tweak the security manager.
+ """
+ def newSecurityManager( user ):
+ """
+ Install a new SecurityManager, using user. Return the
+ old SecurityManager, if any, or None.
+ """
+
+ def replaceSecurityManager( old_manager ):
+ """
+ Replace the SecurityManager with 'old_manager', which
+ must implement ISecurityManager.
+ """
+
+ def noSecurityManager():
+ """
+ Clear any existing SecurityManager.
+ """
+
+
+class ISecurityManagement( Interface ):
+ """
+ "Public" SM API.
+ """
+ def getSecurityManager():
+ """
+ Get a SecurityManager (create if needed).
+ """
+
+ 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 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.
+ """
+
+
+# XXX This interface has too much Zope application dependence. This
+# needs to be refactored somehow.
+
+class ISecurityManager( Interface ):
+ """
+ A security manager provides methods for checking access and managing
+ executable context and policies.
+ """
+
+ def getPrincipal():
+ """
+ Return the authenticated principal.
+
+ This is equivalent to something like::
+
+ REQUEST['AUTHENTICATED_USER']
+
+ but is a bit cleaner, especially if 'REQUEST' isn't handy.
+ """
+
+ def checkPermission( permission, object ):
+ """
+ Check whether the security context allows the given
+ permission on the given object. Return a boolean value.
+
+ Arguments:
+
+ permission -- A permission name
+
+ object -- The object being accessed according to the permission
+ """
+
+ def pushExecutable( anExecutableObject ):
+ """
+ Push an ExecutableObject onto the manager's stack, and
+ activate its custom security policy, if any.
+ """
+
+ def popExecutable( anExecutableObject ):
+ """
+ Pop the topmost ExecutableObject from the stack, deactivating
+ any custom security policy it might have installed.
+ """
+
+ def calledByExecutable():
+ """
+ Return a boolean indicating whether the current request has
+ invoked any IExecutableObjects.
+
+ This can be used to determine if an object was called
+ (more or less) directly from a URL, or if it was called by
+ through-the-web provided code.
+ """
+
+
+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.
+ """
+
+ def check_setattr(ob, name):
+ """Check whether attribute assignment is allowed.
+ """
+
+ def check(ob, operation):
+ """Check whether operation is allowed.
+
+ The operation name is the Python special method name,
+ e.g. "__getitem__".
+ """
+
+ def proxy(value):
+ """Return a security proxy for the value.
+ """
+
+
+class ISecurityPolicy( Interface ):
+
+ def checkPermission( permission
+ , object
+ , context
+ ):
+ """
+ Check whether the security context allows the given permission on
+ the given object, returning a boolean value.
+
+ Arguments:
+
+ permission -- A permission name
+
+ object -- The object being accessed according to the permission
+
+ context -- A SecurityContext, which provides access to information
+ shuch as the context stack and AUTHENTICATED_USER.
+ """
+
+
+class ISecurityContext( Interface ):
+ """
+ Capture transient request-specific security information.
+ """
+ Attribute( 'stack'
+ , 'A stack of elements, each either be an ExecutableObject'
+ 'or a tuple consisting of an ExecutableObject and a'
+ 'custom SecurityPolicy.'
+ )
+
+ Attribute( 'user'
+ , 'The AUTHENTICATED_USER for the request.'
+ )
=== Zope3/src/zope/security/proxy.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/proxy.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+from zope.security._proxy import getObject, getChecker
+from zope.security._proxy import _Proxy as Proxy
+from zope.security.checker import ProxyFactory, Checker as _trustedChecker
+
+def trustedRemoveSecurityProxy(object):
+ if ((type(object) is Proxy) and
+ isinstance(getChecker(object), _trustedChecker)
+ ):
+ return getObject(object)
+
+ return object
+
+
+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 retirned.
+ """
+ checker = getChecker(proxy)
+ func = checker.getPermission_func()
+ dict = getattr(func, '__self__', None)
+ if dict is None:
+ return None
+ items = dict.items()
+ items.sort()
+ return items
=== Zope3/src/zope/security/readme.txt 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/readme.txt Wed Dec 25 09:15:21 2002
@@ -0,0 +1,325 @@
+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. A principal may be
+ associated with different roles and permissions.
+
+ 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.
+
+ Roles
+
+ Represents a responsibility of a user in the context of an
+ object. Roles are associated with the permissions necessary to
+ fulfill the user's responsibility.
+
+ 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 a context.
+
+ Security contexts are containers of transient information such as
+ the current principal and the context stack.
+
+ To explain the concept and usage of the context stack, a little
+ background into the design influences of the default Zope policy
+ is needed, namely the Java language security model. Within the
+ base language, code is associated with identifiers. I.e. this code
+ came from "Joe Schmoe", and another code archive comes signed from
+ Verisign. When executing restricted code, it's important access
+ is checked not only for the code currently executing but for the
+ entire call/context stack (unless explicitly short-circuited).
+ I.e. if Joe Schmoe's code does haven't access to the filesystem,
+ but if the Verisign code does, Joe's code could circumvent the
+ security policy by accessing the filesystem via the Verisign code.
+
+ 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 context stacks 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 security manager and global
+ security policy.
+
+ Security Context
+
+ Stores transient information on the current principal and the
+ context stack.
+
+ Security Manager
+
+ Manages security context (execution stack) and delegates
+ permission checks to security policy.
+
+ Security Policy
+
+ Provides a single method that accepts the object, the
+ permission, and the context 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 within a security manager's
+ context....
+
+ Defining 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
+
+ def checkPermission(self, permission, object, context):
+
+ token = context.user.getAuthenticationToken()
+ 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
+
+ allowed = db.get(token, ())
+ if permission in allowed:
+ return True
+
+ return False
+
+ 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.
+
+ Security Manager Access
+
+ The *default* implementation of the security management
+ interfaces defines security managers 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 user per
+ thread at any given moment. Reimplementing the manager 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 that implements concepts
+ of roles, 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.
+
+ Author
+
+ Kapil Thangavelu <hazmat at objectrealms.net>
+
+ Guido Wesdorp <guido at infrae.com>
=== Zope3/src/zope/security/restrictedbuiltins.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/restrictedbuiltins.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,103 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+import sys
+
+def RestrictedBuiltins():
+
+ from zope.security.proxy import ProxyFactory
+ from zope.security.checker import NamesChecker
+
+ # It's better to say what is safe than it say what is not safe
+ _safe = [
+ '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__', '__doc__', '__name__', 'abs', 'apply', 'buffer',
+ 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile',
+ 'complex', 'copyright', 'credits', 'delattr', 'dict',
+ 'divmod', 'eval', 'filter', 'float', 'getattr', 'globals',
+ 'hasattr', 'hash', 'hex', 'id', 'int', 'isinstance',
+ 'issubclass', 'iter', 'len', 'license', 'list', 'locals',
+ 'long', 'map', 'max', 'min', 'object', 'oct', 'ord', 'pow',
+ 'property', 'quit', 'range', 'reduce', 'repr', 'round',
+ 'setattr', 'slice', 'staticmethod', 'str', 'super', 'tuple',
+ 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip',
+ ]
+
+ # XXX dir segfaults with a seg fault due to a bas 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.
+
+ # Anything that accesses an external file is a no no:
+ # 'open', 'execfile', 'file'
+
+ # We dont want restricted code to call exit: 'SystemExit', 'exit'
+
+ # Other no nos:
+ # help prints
+ # input does I/O
+ # raw_input does I/O
+ # intern's effect is too global
+ # reload does import, XXX doesn't it use __import__?
+
+ _builtinTypeChecker = NamesChecker(
+ ['__str__', '__repr__', '__name__', '__module__',
+ '__bases__', '__call__'])
+
+ import __builtin__
+
+ builtins = {}
+ for name in _safe:
+ value = getattr(__builtin__, name)
+ if isinstance(value, type):
+ value = ProxyFactory(value, _builtinTypeChecker)
+ else:
+ value = ProxyFactory(value)
+ builtins[name] = value
+
+ def __import__(name, globals=None, locals=None, fromlist=()):
+ # Waaa, we have to emulate __import__'s weird semantics.
+ try:
+ module = sys.modules[name]
+ if fromlist:
+ return module
+
+ l = name.find('.')
+ if l < 0:
+ return module
+
+ return sys.modules[name[:l]]
+
+ except KeyError:
+ raise ImportError(name)
+
+ builtins['__import__'] = ProxyFactory(__import__)
+
+ return builtins
+
+RestrictedBuiltins = RestrictedBuiltins()
=== Zope3/src/zope/security/restrictedinterpreter.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/restrictedinterpreter.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+import sys
+
+from zope.security.proxy import ProxyFactory
+from zope.security.restrictedbuiltins import RestrictedBuiltins
+
+class RestrictedInterpreter:
+
+ def __init__(self):
+ self.globals = {'__builtins__' : RestrictedBuiltins}
+
+ def ri_exec(self, code):
+ # what is the type of code?
+ exec code in self.globals
=== Zope3/src/zope/security/securitycontext.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/securitycontext.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" Default ISecurityContext impl """
+
+from zope.security.interfaces import ISecurityContext
+
+class SecurityContext:
+ """
+ Capture transient request-specific security information.
+
+ Attribute( 'stack'
+ , 'A stack of elements, each either be an ExecutableObject'
+ 'or a tuple consisting of an ExecutableObject and a'
+ 'custom SecurityPolicy.'
+ )
+
+ Attribute( 'user'
+ , 'The AUTHENTICATED_USER for the request.'
+ )
+ """
+
+ def __init__( self, user ):
+
+ self.stack = []
+ self.user = user
+ self.objectCache = {}
=== Zope3/src/zope/security/securitymanagement.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/securitymanagement.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" Default ISecurityManagement implementation """
+
+system_user = object() # Special system user that has all permissions
+
+
+from zope.security.interfaces import ISecurityManagement, ISecurityManagementSetup
+from zope.security.securitymanager import SecurityManager
+from zope.security.securitymanager import setSecurityPolicy as _setSecurityPolicy
+from zope.security.securitycontext import SecurityContext
+
+__implements__ = ( ISecurityManagement, ISecurityManagementSetup )
+
+try:
+ import thread
+except:
+ get_ident=lambda: 0
+else:
+ get_ident=thread.get_ident
+
+_managers={}
+
+from zope.testing.cleanup import addCleanUp
+addCleanUp(_managers.clear)
+
+
+#
+# ISecurityManagementSetup implementation
+#
+def newSecurityManager( user ):
+ """
+ Install a new SecurityManager, using user. Return the
+ old SecurityManager, if any, or None.
+ """
+ return replaceSecurityManager( SecurityManager( SecurityContext( user ) ) )
+
+def replaceSecurityManager( old_manager ):
+ """
+ Replace the SecurityManager with 'old_manager', which
+ must implement ISecurityManager.
+ """
+ thread_id = get_ident()
+ old = _managers.get( thread_id, None )
+ _managers[ thread_id ] = old_manager
+ return old
+
+def noSecurityManager():
+ """
+ Clear any existing SecurityManager.
+ """
+ try:
+ del _managers[ get_ident() ]
+ except KeyError:
+ pass
+
+#
+# ISecurityManagement implementation
+#
+def getSecurityManager():
+ """
+ Get a SecurityManager (create if needed).
+ """
+ thread_id = get_ident()
+ manager=_managers.get( thread_id, None )
+
+ if manager is None:
+ newSecurityManager( None )
+ manager=_managers.get( thread_id, None )
+
+ return manager
+
+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.
+ """
+ return _setSecurityPolicy( aSecurityPolicy )
=== Zope3/src/zope/security/securitymanager.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/securitymanager.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,159 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" Default ISecurityManager implementation """
+
+import os
+from zope.security.simplesecuritypolicies import ParanoidSecurityPolicy
+
+MAX_STACK_SIZE = 100
+
+_defaultPolicy = ParanoidSecurityPolicy()
+
+def _clear():
+ global _defaultPolicy
+ _defaultPolicy = ParanoidSecurityPolicy()
+
+from zope.testing.cleanup import addCleanUp
+addCleanUp(_clear)
+
+
+def setSecurityPolicy( aSecurityPolicy ):
+ """
+ Set the system default security policy.
+
+ This method should only be caused by system startup code. It should
+ never, for example, be called during a web request.
+ """
+ global _defaultPolicy
+
+ last, _defaultPolicy = _defaultPolicy, aSecurityPolicy
+
+ return last
+
+from zope.security.interfaces import ISecurityManager
+
+class SecurityManager:
+ """
+ A security manager provides methods for checking access and managing
+ executable context and policies.
+ """
+ __implements__ = ISecurityManager
+
+ def __init__( self, context ):
+ self._context = context
+ self._policy = None
+
+ def _getPolicy( self ):
+ """
+ Find current policy, or default.
+ """
+ policy = self._policy
+ if policy is None:
+ policy = _defaultPolicy
+ return policy
+
+ #
+ # ISecurityManager implementation
+ #
+ def getPrincipal( self ):
+ """
+ Return the authenticated user.
+
+ This is equivalent to something like::
+
+ REQUEST['AUTHENTICATED_USER']
+
+ but is a bit cleaner, especially if 'REQUEST' isn't handy.
+ """
+ return self._context.user
+
+ def checkPermission( self, permission, object ):
+ """
+ Check whether the security context allows the given
+ permission on the given object. Return a boolean value.
+
+ Arguments:
+
+ permission -- A permission name
+
+ object -- The object being accessed according to the permission
+ """
+ return self._getPolicy().checkPermission( permission, object
+ , self._context )
+
+ def pushExecutable( self, anExecutableObject ):
+ """
+ Push an ExecutableObject onto the manager's stack, and
+ activate its custom security policy, if any.
+ """
+ stack=self._context.stack
+
+ if len( stack ) >= MAX_STACK_SIZE:
+ raise SystemError, 'Excessive recursion'
+
+ stack.append( anExecutableObject )
+ p = getattr( anExecutableObject, '_customSecurityPolicy', None )
+
+ if p is not None:
+ p = p()
+
+ self._policy = p
+
+ def popExecutable( self, anExecutableObject ):
+ """
+ Pop the topmost ExecutableObject from the stack, deactivating
+ any custom security policy it might have installed.
+ """
+ stack=self._context.stack
+
+ if not stack:
+ return
+
+ top = stack[-1]
+
+ if top is anExecutableObject:
+ del stack[-1]
+ else:
+ indexes = range(len(stack))
+ indexes.reverse()
+ for i in indexes:
+ top=stack[i]
+ if top is anExecutableObject:
+ del stack[i:]
+ break
+ else:
+ return
+
+ if stack:
+
+ top = stack[-1]
+ p = getattr( top, '_customSecurityPolicy', None )
+
+ if p is not None:
+ p=p()
+ self._policy=p
+
+ else:
+ self._policy=None
+
+ def calledByExecutable( self ):
+ """
+ Return a boolean indicating whether the current request has
+ invoked any IExecutableObjects.
+
+ This can be used to determine if an object was called
+ (more or less) directly from a URL, or if it was called by
+ through-the-web provided code.
+ """
+ return len( self._context.stack )
=== Zope3/src/zope/security/setup.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/setup.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from distutils.core import setup, Extension
+
+setup(name="_Proxy", version = "0.1",
+ ext_modules=[Extension("_Proxy", ["_Proxy.c"])])
=== Zope3/src/zope/security/simplesecuritypolicies.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:52 2002
+++ Zope3/src/zope/security/simplesecuritypolicies.py Wed Dec 25 09:15:21 2002
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" Simple ISecurityPolicy implementations."""
+
+from zope.security.interfaces import ISecurityPolicy
+from zope.exceptions import Unauthorized
+from zope.security.securitymanagement import system_user
+
+class ParanoidSecurityPolicy:
+ """
+ Deny all access.
+ """
+ __implements__ = ISecurityPolicy
+
+ def checkPermission( sel, permission, object, context ):
+ if (context.user is system_user # no user
+ and not context.stack # no untrusted code
+ ):
+ return 1 # Nobody not to trust!
+
+ return 0
+
+class PermissiveSecurityPolicy:
+ """
+ Allow all access
+ """
+ __implements__ = ISecurityPolicy
+
+ def checkPermission( self, permission, object, context ):
+ return 1