diff options
Diffstat (limited to 'src/zope')
40 files changed, 0 insertions, 7517 deletions
diff --git a/src/zope/security/DEPENDENCIES.cfg b/src/zope/security/DEPENDENCIES.cfg deleted file mode 100644 index 5fc1057..0000000 --- a/src/zope/security/DEPENDENCIES.cfg +++ /dev/null @@ -1,6 +0,0 @@ -RestrictedPython -zope.exceptions -zope.interface -zope.proxy -zope.testing -zope.thread diff --git a/src/zope/security/README.txt b/src/zope/security/README.txt deleted file mode 100644 index 4940503..0000000 --- a/src/zope/security/README.txt +++ /dev/null @@ -1,340 +0,0 @@ -============== -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 <hazmat at objectrealms.net> -- Guido Wesdorp <guido at infrae.com> -- Marius Gedminas <marius at pov.lt> - diff --git a/src/zope/security/SETUP.cfg b/src/zope/security/SETUP.cfg deleted file mode 100644 index f57d6f5..0000000 --- a/src/zope/security/SETUP.cfg +++ /dev/null @@ -1,9 +0,0 @@ -# Extension information for zpkg. - -<extension _proxy> - source _proxy.c -</extension> - -<extension _zope_security_checker> - source _zope_security_checker.c -</extension> diff --git a/src/zope/security/__init__.py b/src/zope/security/__init__.py deleted file mode 100644 index 18360ed..0000000 --- a/src/zope/security/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Base security system - -$Id$ - -""" - -import zope.deferredimport - -zope.deferredimport.define( - checkPermission = 'zope.security.management:checkPermission', - canWrite = 'zope.security.checker:canWrite', - canAccess = 'zope.security.checker:canAccess', - ) diff --git a/src/zope/security/_definitions.py b/src/zope/security/_definitions.py deleted file mode 100644 index aa0d445..0000000 --- a/src/zope/security/_definitions.py +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################## -# -# Copyright (c) 2005 Zope Corporation 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 - -import zope.security.interfaces - -thread_local = threading.local() - -class system_user(object): - zope.interface.classProvides(zope.security.interfaces.IPrincipal) - id = u'zope.security.management.system_user' - title = u'Special System User that typically has all permissions' - description = u'' diff --git a/src/zope/security/_proxy.c b/src/zope/security/_proxy.c deleted file mode 100644 index b0e3a81..0000000 --- a/src/zope/security/_proxy.c +++ /dev/null @@ -1,1010 +0,0 @@ -/***************************************************************************** -* -* Copyright (c) 2003, 2004 Zope Corporation 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 - -$Id$ -*/ - -#include <Python.h> -#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) -{ - if (visit(self->proxy.proxy_object, arg) < 0) - return -1; - if (visit(self->proxy_checker, arg) < 0) - return -1; - 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("<security proxied %s.%s instance at %p>", - smodule, sname, object); - } - else { - PyErr_Clear(); - result = PyString_FromFormat("<security proxied %s instance at %p>", - 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 int -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, int 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, int 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, int start, int 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, int i, int 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 = { - (inquiry)proxy_length, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - (intargfunc)proxy_igetitem, /* sq_item */ - (intintargfunc)proxy_slice, /* sq_slice */ - (intobjargproc)proxy_isetitem, /* sq_ass_item */ - (intintobjargproc)proxy_ass_slice, /* sq_ass_slice */ - (objobjproc)proxy_contains, /* sq_contains */ -}; - -static PyMappingMethods -proxy_as_mapping = { - (inquiry)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); -} diff --git a/src/zope/security/_zope_security_checker.c b/src/zope/security/_zope_security_checker.c deleted file mode 100644 index 44eaf2c..0000000 --- a/src/zope/security/_zope_security_checker.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - - Copyright (c) 2004 Zope Corporation and Contributors. - All Rights Reserved. - - This software is subject to the provisions of the Zope Public License, - Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. - THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED - WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS - FOR A PARTICULAR PURPOSE. - -*/ -#include <Python.h> - -static PyObject *_checkers, *_defaultChecker, *_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 gere because we will use it for - __setitem__, as describd below */ - -static int -Checker_check_int(Checker *self, PyObject *object, PyObject *name) -{ - PyObject *permission=NULL; - int operator; - -/* permission = self._permission_func(name) */ - if (self->getperms) - permission = PyDict_GetItem(self->getperms, name); - -/* if permission is not None: */ - if (permission != NULL) - { -/* if permission is CheckerPublic: */ -/* return # Public */ - if (permission == CheckerPublic) - return 0; - - if (checkPermission(permission, object, name) < 0) - return -1; - return 0; - } - - - operator = (PyString_Check(name) - && PyString_AS_STRING(name)[0] == '_' - && PyString_AS_STRING(name)[1] == '_'); - - if (operator) - { -/* elif name in _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 */ (inquiry)NULL, - /* mp_subscript */ (binaryfunc)Checker_proxy, - /* mp_ass_subscript */ (objobjargproc)Checker_check_int, -}; - - - -static PyTypeObject CheckerType = { - PyObject_HEAD_INIT(NULL) - /* ob_size */ 0, - /* tp_name */ "zope.security.checker." - "Checker", - /* tp_basicsize */ sizeof(Checker), - /* tp_itemsize */ 0, - /* tp_dealloc */ (destructor)&Checker_dealloc, - /* tp_print */ (printfunc)0, - /* tp_getattr */ (getattrfunc)0, - /* tp_setattr */ (setattrfunc)0, - /* tp_compare */ (cmpfunc)0, - /* tp_repr */ (reprfunc)0, - /* tp_as_number */ 0, - /* tp_as_sequence */ 0, - /* tp_as_mapping */ &Checker_as_mapping, - /* tp_hash */ (hashfunc)0, - /* tp_call */ (ternaryfunc)0, - /* tp_str */ (reprfunc)0, - /* tp_getattro */ (getattrofunc)0, - /* tp_setattro */ (setattrofunc)0, - /* tp_as_buffer */ 0, - /* tp_flags */ Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, - /* tp_doc */ "Security checker", - /* tp_traverse */ (traverseproc)Checker_traverse, - /* tp_clear */ (inquiry)Checker_clear, - /* tp_richcompare */ (richcmpfunc)0, - /* tp_weaklistoffset */ (long)0, - /* tp_iter */ (getiterfunc)0, - /* tp_iternext */ (iternextfunc)0, - /* tp_methods */ Checker_methods, - /* tp_members */ 0, - /* tp_getset */ Checker_getset, - /* tp_base */ 0, - /* tp_dict */ 0, /* internal use */ - /* tp_descr_get */ (descrgetfunc)0, - /* tp_descr_set */ (descrsetfunc)0, - /* tp_dictoffset */ 0, - /* tp_init */ (initproc)Checker_init, - /* tp_alloc */ (allocfunc)0, - /* tp_new */ (newfunc)0, - /* tp_free */ 0, /* Low-level free-mem routine */ - /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */ -}; - - - - - -/* def selectChecker(object): */ -/* """Get a checker for the given object */ -/* The appropriate checker is returned or None is returned. If the */ -/* return value is None, then object should not be wrapped in a proxy. */ -/* """ */ - -static char selectChecker_doc[] = -"Get a checker for the given object\n" -"\n" -"The appropriate checker is returned or None is returned. If the\n" -"return value is None, then object should not be wrapped in a proxy.\n" -; - -static PyObject * -selectChecker(PyObject *ignored, PyObject *object) -{ - PyObject *checker; - -/* checker = _getChecker(type(object), _defaultChecker) */ - - checker = PyDict_GetItem(_checkers, (PyObject*)(object->ob_type)); - if (checker == NULL) - checker = _defaultChecker; - -/* if checker is NoProxy: */ -/* return None */ - - if (checker == NoProxy) - { - Py_INCREF(Py_None); - return Py_None; - } - -/* if checker is _defaultChecker and isinstance(object, Exception): */ -/* return None */ - - if (checker == _defaultChecker - && PyObject_IsInstance(object, PyExc_Exception)) - { - Py_INCREF(Py_None); - return Py_None; - } - -/* while not isinstance(checker, Checker): */ -/* checker = checker(object) */ -/* if checker is NoProxy or checker is None: */ -/* return None */ - - Py_INCREF(checker); - while (! PyObject_TypeCheck(checker, &CheckerType)) - { - PyObject *newchecker; - newchecker = PyObject_CallFunctionObjArgs(checker, object, NULL); - Py_DECREF(checker); - if (newchecker == NULL) - return NULL; - checker = newchecker; - if (checker == NoProxy || checker == Py_None) - { - Py_DECREF(checker); - Py_INCREF(Py_None); - return Py_None; - } - } - -/* return checker */ - - return checker; -} - - -static PyMethodDef module_methods[] = { - {"selectChecker", (PyCFunction)selectChecker, METH_O, selectChecker_doc}, - {NULL} /* Sentinel */ -}; - -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif -PyMODINIT_FUNC -init_zope_security_checker(void) -{ - PyObject* m; - - CheckerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&CheckerType) < 0) - return; - - _defaultChecker = PyObject_CallFunction((PyObject*)&CheckerType, "{}"); - if (_defaultChecker == NULL) - return; - -#define INIT_STRING(S) \ -if((str_##S = PyString_InternFromString(#S)) == NULL) return - - INIT_STRING(checkPermission); - INIT_STRING(__Security_checker__); - 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); -} diff --git a/src/zope/security/adapter.py b/src/zope/security/adapter.py deleted file mode 100644 index 2c9ddff..0000000 --- a/src/zope/security/adapter.py +++ /dev/null @@ -1,134 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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 - -$Id$ -""" - -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]) diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py deleted file mode 100644 index d3b29aa..0000000 --- a/src/zope/security/checker.py +++ /dev/null @@ -1,735 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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. - -$Id$ -""" -import os -import sys -import sets -import types -import datetime -import decimal -import pytz -import weakref - -from zope.exceptions import DuplicationError -import zope.interface.interface -import zope.interface.interfaces -import zope.interface.declarations -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 - -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__', - ] - -_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) - -BasicTypes = BasicTypes({ - object: NoProxy, - int: NoProxy, - float: NoProxy, - long: NoProxy, - complex: NoProxy, - types.NoneType: NoProxy, - str: NoProxy, - unicode: NoProxy, - bool: NoProxy, - datetime.timedelta: NoProxy, - datetime.datetime: NoProxy, - datetime.date: NoProxy, - datetime.time: NoProxy, - datetime.tzinfo: NoProxy, - type(pytz.UTC): NoProxy, -}) - -# 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) -} - - -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__', ]), - sets.Set: _setChecker, - sets.ImmutableSet: _setChecker, - 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, -} - -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) - diff --git a/src/zope/security/decorator.py b/src/zope/security/decorator.py deleted file mode 100644 index 035b68d..0000000 --- a/src/zope/security/decorator.py +++ /dev/null @@ -1,198 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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. - -$Id$ -""" -__docformat__ = "reStructuredText" - -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, getChecker -from zope.interface.declarations import ObjectSpecification - -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', <zope.security.decorator.Foo object ...>) - - 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', <zope.security.decorator.Foo object ...>) - - (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) - <class 'zope.security.checker.CombinedChecker'> - >>> 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', <zope.security.decorator.Foo object ...>) - - when we wrap the secured `foo`: - - >>> wrapper = Wrapper(secure_foo) - - we still get a combined checker: - - >>> checker = wrapper.__Security_checker__ - >>> type(checker) - <class 'zope.security.checker.CombinedChecker'> - >>> 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__ is None - 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: - 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.""" - - diff --git a/src/zope/security/examples/sandbox.py b/src/zope/security/examples/sandbox.py deleted file mode 100644 index 2ef25c2..0000000 --- a/src/zope/security/examples/sandbox.py +++ /dev/null @@ -1,318 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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. - -$Id$ -""" -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() diff --git a/src/zope/security/examples/sandbox_security.py b/src/zope/security/examples/sandbox_security.py deleted file mode 100644 index a5e8d39..0000000 --- a/src/zope/security/examples/sandbox_security.py +++ /dev/null @@ -1,204 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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 - -$Id$ -""" -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() diff --git a/src/zope/security/interfaces.py b/src/zope/security/interfaces.py deleted file mode 100644 index 0376c74..0000000 --- a/src/zope/security/interfaces.py +++ /dev/null @@ -1,321 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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. - -$Id$ -""" - -from zope.interface import Interface, Attribute, implements -from zope.interface.common.interfaces import IException, IAttributeError -from zope.schema import Text, TextLine -from zope.i18nmessageid import MessageFactory -_ = MessageFactory('zope') - -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) diff --git a/src/zope/security/management.py b/src/zope/security/management.py deleted file mode 100644 index 3a106bf..0000000 --- a/src/zope/security/management.py +++ /dev/null @@ -1,144 +0,0 @@ -############################################################################## -# -# 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.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 - -$Id$ -""" - - -import zope.interface -import zope.thread - -import zope.security.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( - zope.security.interfaces.ISecurityManagement, - zope.security.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 zope.security.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: - interaction = thread_local.interaction - return interaction.checkPermission(permission, object) - -addCleanUp(endInteraction) diff --git a/src/zope/security/meta.zcml b/src/zope/security/meta.zcml deleted file mode 100644 index c3f54e2..0000000 --- a/src/zope/security/meta.zcml +++ /dev/null @@ -1,24 +0,0 @@ -<configure - xmlns="http://namespaces.zope.org/zope" - xmlns:meta="http://namespaces.zope.org/meta"> - - <meta:directive - namespace="http://namespaces.zope.org/zope" - name="permission" - schema=".zcml.IPermissionDirective" - handler=".zcml.permission" /> - - <meta:directive - name="securityPolicy" - namespace="http://namespaces.zope.org/zope" - schema=".zcml.ISecurityPolicyDirective" - handler=".zcml.securityPolicy" - /> - - <meta:directive - name="redefinePermission" - namespace="http://namespaces.zope.org/meta" - schema=".zcml.IRedefinePermission" - handler=".zcml.redefinePermission" /> - -</configure> diff --git a/src/zope/security/permission.py b/src/zope/security/permission.py deleted file mode 100644 index 1999f85..0000000 --- a/src/zope/security/permission.py +++ /dev/null @@ -1,64 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Corporation 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 - -$Id$ -""" -__docformat__ = "reStructuredText" - -from zope.interface import implements -from zope.component import queryUtility, getUtilitiesFor -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') - """ - 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 diff --git a/src/zope/security/proxy.py b/src/zope/security/proxy.py deleted file mode 100644 index acd3a91..0000000 --- a/src/zope/security/proxy.py +++ /dev/null @@ -1,73 +0,0 @@ -############################################################################## -# -# 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.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. - -$Id$ -""" -__docformat__ = 'restructuredtext' - -from zope.security._proxy import getChecker, getObject -from zope.security._proxy import _Proxy as Proxy - -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) diff --git a/src/zope/security/setup.py b/src/zope/security/setup.py deleted file mode 100644 index 4ecc3be..0000000 --- a/src/zope/security/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#! /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.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 - -$Id$ -""" -from distutils.core import setup, Extension - -setup(name="_Proxy", version = "0.1", - ext_modules=[Extension("_Proxy", ["_Proxy.c"])]) diff --git a/src/zope/security/simplepolicies.py b/src/zope/security/simplepolicies.py deleted file mode 100644 index 3cca0b9..0000000 --- a/src/zope/security/simplepolicies.py +++ /dev/null @@ -1,61 +0,0 @@ -############################################################################## -# -# 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.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. - -$Id$ -""" -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 diff --git a/src/zope/security/testing.py b/src/zope/security/testing.py deleted file mode 100644 index 804e493..0000000 --- a/src/zope/security/testing.py +++ /dev/null @@ -1,42 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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. - -$Id$ -""" - -from zope import interface -from zope.security import interfaces - -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 diff --git a/src/zope/security/tests/__init__.py b/src/zope/security/tests/__init__.py deleted file mode 100644 index b711d36..0000000 --- a/src/zope/security/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -# This file is necessary to make this directory a package. diff --git a/src/zope/security/tests/test_adapter.py b/src/zope/security/tests/test_adapter.py deleted file mode 100644 index 88465f6..0000000 --- a/src/zope/security/tests/test_adapter.py +++ /dev/null @@ -1,28 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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. -# -############################################################################## -""" -$Id$ -""" -import unittest -from zope.testing.doctestunit import DocTestSuite - - -def test_suite(): - return unittest.TestSuite(( - DocTestSuite('zope.security.adapter'), - )) - - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') diff --git a/src/zope/security/tests/test_checker.py b/src/zope/security/tests/test_checker.py deleted file mode 100644 index f739c71..0000000 --- a/src/zope/security/tests/test_checker.py +++ /dev/null @@ -1,629 +0,0 @@ -############################################################################## -# -# 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.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 - -$Id$ -""" -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) - - 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') diff --git a/src/zope/security/tests/test_decorator.py b/src/zope/security/tests/test_decorator.py deleted file mode 100644 index 6067ce4..0000000 --- a/src/zope/security/tests/test_decorator.py +++ /dev/null @@ -1,28 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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 - -$Id$ -""" -import unittest -from zope.testing import doctest - -def test_suite(): - suite = doctest.DocTestSuite() - suite.addTest(doctest.DocTestSuite('zope.security.decorator')) - return suite - - -if __name__ == '__main__': - unittest.main() diff --git a/src/zope/security/tests/test_management.py b/src/zope/security/tests/test_management.py deleted file mode 100644 index 63f0000..0000000 --- a/src/zope/security/tests/test_management.py +++ /dev/null @@ -1,131 +0,0 @@ -############################################################################## -# -# 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.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 - -$Id$ -""" - -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 - - 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) - - 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.assert_(system_user.title) - 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()) diff --git a/src/zope/security/tests/test_permission.py b/src/zope/security/tests/test_permission.py deleted file mode 100644 index 9d90f20..0000000 --- a/src/zope/security/tests/test_permission.py +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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 - -$Id$ -""" -import unittest -from zope.testing.doctestunit 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), - ]) - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') diff --git a/src/zope/security/tests/test_proxy.py b/src/zope/security/tests/test_proxy.py deleted file mode 100644 index b983ec7..0000000 --- a/src/zope/security/tests/test_proxy.py +++ /dev/null @@ -1,447 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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 - -$Id$ -""" - -import unittest -from zope.testing.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( - "<security proxied %s.%s instance at" - % (x.__class__.__module__, x.__class__.__name__)), - s) - - - def testRepr(self): - self.assertEqual(repr(self.p), repr(self.x)) - - x = Something() - c = Checker() - c.ok = 0 - p = ProxyFactory(x, c) - s = repr(p) - self.failUnless(s.startswith( - "<security proxied %s.%s instance at" - % (x.__class__.__module__, x.__class__.__name__)), - s) - - def testGetAttrOK(self): - self.assertEqual(removeSecurityProxy(self.p.foo), [1,2,3]) - - def testGetAttrFail(self): - self.assertRaises(RuntimeError, lambda: self.p.bar) - - def testSetAttrOK(self): - self.p.foo = 42 - self.assertEqual(self.p.foo, 42) - - def testSetAttrFail(self): - def doit(): self.p.bar = 42 - self.assertRaises(RuntimeError, doit) - - def testGetItemOK(self): - self.assertEqual(self.p[0], 1) - - def testGetItemFail(self): - self.shouldFail(lambda: self.p[10]) - - def testSetItemOK(self): - self.p[0] = 42 - self.assertEqual(self.p[0], 42) - - def testSetItemFail(self): - def doit(): del self.p[0] - self.shouldFail(doit) - - def testDelItemOK(self): - self.p[0] = 42 - self.assertEqual(self.p[0], 42) - del self.p[0] - self.shouldFail(lambda: self.p[0]) - - def testDelItemFail(self): - def doit(): self.p[10] = 42 - self.shouldFail(doit) - - def testCallOK(self): - self.assertEqual(self.p(None), 42) - - def testCallFail(self): - self.shouldFail(self.p, None) - - def testRichCompareOK(self): - self.failUnless(self.p == self.x) - -## def testRichCompareFail(self): -## self.shouldFail(lambda: self.p == self.x) - - def testIterOK(self): - self.assertEqual(removeSecurityProxy(iter(self.p)), self.x) - - def testIterFail(self): - self.shouldFail(iter, self.p) - - def testNextOK(self): - self.assertEqual(self.p.next(), 42) - - def testNextFail(self): - self.shouldFail(self.p.next) - - def testCompareOK(self): - self.assertEqual(cmp(self.p, self.x), 0) - -## def testCompareFail(self): -## self.shouldFail(cmp, self.p, self.x) - - def testHashOK(self): - self.assertEqual(hash(self.p), hash(self.x)) - -## def testHashFail(self): -## self.shouldFail(hash, self.p) - - def testNonzeroOK(self): - self.assertEqual(not self.p, 0) - -## def testNonzeroFail(self): -## self.shouldFail(lambda: not self.p) - - def testLenOK(self): - self.assertEqual(len(self.p), 42) - - def testLenFail(self): - self.shouldFail(len, self.p) - - def testSliceOK(self): - self.assertEqual(removeSecurityProxy(self.p[:]), [42]) - - def testSliceFail(self): - self.shouldFail(lambda: self.p[:]) - - def testSetSliceOK(self): - self.p[:] = [42] - - def testSetSliceFail(self): - def doit(): self.p[:] = [42] - self.shouldFail(doit) - - def testContainsOK(self): - self.failUnless(42 in self.p) - - def testContainsFail(self): - self.shouldFail(lambda: 42 in self.p) - - def testGetObject(self): - self.assertEqual(self.x, removeSecurityProxy(self.p)) - - def testGetChecker(self): - self.assertEqual(self.c, getChecker(self.p)) - - def testProxiedClassicClassAsDictKey(self): - class C(object): - pass - d = {C: C()} - pC = ProxyFactory(C, self.c) - self.assertEqual(d[pC], d[C]) - - def testProxiedNewClassAsDictKey(self): - class C(object): - pass - d = {C: C()} - pC = ProxyFactory(C, self.c) - self.assertEqual(d[pC], d[C]) - - unops = [ - "-x", "+x", "abs(x)", "~x", - "int(x)", "long(x)", "float(x)", - ] - - def test_unops(self): - # We want the starting value of the expressions to be a proxy, - # but we don't want to create new proxies as a result of - # evaluation, so we have to extend the list of types that - # aren't proxied. - self.c.unproxied_types = str, int, long, float - for expr in self.unops: - x = 1 - y = eval(expr) - # Make sure 'x' is a proxy always: - x = ProxyFactory(1, self.c) - z = eval(expr) - self.assertEqual(removeSecurityProxy(z), y, - "x=%r; expr=%r" % (x, expr)) - self.shouldFail(lambda x: eval(expr), x) - - def test_odd_unops(self): - # unops that don't return a proxy - P = self.c.proxy - for func in ( - hex, oct, - # lambda x: not x, - ): - self.assertEqual(func(P(100)), func(100)) - self.shouldFail(func, P(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", "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 - -if __name__=='__main__': - from unittest import main - main(defaultTest='test_suite') diff --git a/src/zope/security/tests/test_set_checkers.py b/src/zope/security/tests/test_set_checkers.py deleted file mode 100644 index 0c28dec..0000000 --- a/src/zope/security/tests/test_set_checkers.py +++ /dev/null @@ -1,219 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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. - -$Id$ -""" -import unittest -from zope.testing.doctestunit import DocTestSuite -from zope.security.checker import ProxyFactory -from zope.security.interfaces import ForbiddenAttribute -import sets - -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): - test.globs['set'] = sets.Set - -def setUpImmutableSet(test): - test.globs['set'] = sets.ImmutableSet - -def test_suite(): - return unittest.TestSuite(( - DocTestSuite(), - DocTestSuite(setUp=setUpFrozenSet), - DocTestSuite(setUp=setUpSet), - DocTestSuite(setUp=setUpImmutableSet), - )) - -if __name__ == '__main__': - import unittest - unittest.main() diff --git a/src/zope/security/tests/test_simpleinteraction.py b/src/zope/security/tests/test_simpleinteraction.py deleted file mode 100644 index b6a5bbb..0000000 --- a/src/zope/security/tests/test_simpleinteraction.py +++ /dev/null @@ -1,81 +0,0 @@ -############################################################################## -# -# 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.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. - -$Id$ -""" -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() diff --git a/src/zope/security/tests/test_standard_checkers.py b/src/zope/security/tests/test_standard_checkers.py deleted file mode 100644 index 3c33948..0000000 --- a/src/zope/security/tests/test_standard_checkers.py +++ /dev/null @@ -1,498 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation 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. - -$Id$ -""" -from zope.security.checker import ProxyFactory, NamesChecker -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 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 - - >>> from pytz import UTC - >>> int(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 - """ - - -from zope.testing.doctestunit import DocTestSuite - -def test_suite(): - return DocTestSuite() - -if __name__ == '__main__': - import unittest - unittest.main() diff --git a/src/zope/security/untrustedinterpreter.txt b/src/zope/security/untrustedinterpreter.txt deleted file mode 100644 index ecbd17e..0000000 --- a/src/zope/security/untrustedinterpreter.txt +++ /dev/null @@ -1,220 +0,0 @@ -====================== -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. diff --git a/src/zope/security/untrustedpython/__init__.py b/src/zope/security/untrustedpython/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/src/zope/security/untrustedpython/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/src/zope/security/untrustedpython/builtins.py b/src/zope/security/untrustedpython/builtins.py deleted file mode 100644 index 9f47840..0000000 --- a/src/zope/security/untrustedpython/builtins.py +++ /dev/null @@ -1,126 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Corporation 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. - -$Id$ -""" -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', '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', 'round', - 'setattr', 'slice', 'staticmethod', 'str', 'super', 'tuple', - 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip', - 'True', 'False', - - # TODO: 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. - #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()) diff --git a/src/zope/security/untrustedpython/builtins.txt b/src/zope/security/untrustedpython/builtins.txt deleted file mode 100644 index f3c3071..0000000 --- a/src/zope/security/untrustedpython/builtins.txt +++ /dev/null @@ -1,114 +0,0 @@ -Safe 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 nly 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 - - - - - - - diff --git a/src/zope/security/untrustedpython/interpreter.py b/src/zope/security/untrustedpython/interpreter.py deleted file mode 100644 index c64c98c..0000000 --- a/src/zope/security/untrustedpython/interpreter.py +++ /dev/null @@ -1,78 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Corporation 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!!! - -$Id$ -""" -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, '<string>', '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, '<string>', 'exec') - exec code in globals, locals - - -class CompiledExpression(object): - """A compiled expression - """ - - def __init__(self, source, filename='<string>'): - 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='<string>'): - 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 diff --git a/src/zope/security/untrustedpython/interpreter.txt b/src/zope/security/untrustedpython/interpreter.txt deleted file mode 100644 index dce15da..0000000 --- a/src/zope/security/untrustedpython/interpreter.txt +++ /dev/null @@ -1,112 +0,0 @@ -Untrusted 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', '<mycode>', '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 diff --git a/src/zope/security/untrustedpython/rcompile.py b/src/zope/security/untrustedpython/rcompile.py deleted file mode 100644 index 93452c8..0000000 --- a/src/zope/security/untrustedpython/rcompile.py +++ /dev/null @@ -1,97 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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. - -$Id$ -""" - -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") - diff --git a/src/zope/security/untrustedpython/rcompile.txt b/src/zope/security/untrustedpython/rcompile.txt deleted file mode 100644 index 919757e..0000000 --- a/src/zope/security/untrustedpython/rcompile.txt +++ /dev/null @@ -1,128 +0,0 @@ -================================== -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", "<string>", "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", "<string>", "exec") - >>> x - 1 - -Trying to compile exec, raise or try/except sattements gives -syntax errors: - - >>> compile("exec 'x = 2'", "<string>", "exec") - Traceback (most recent call last): - ... - SyntaxError: Line 1: exec statements are not supported - - >>> compile("raise KeyError('x')", "<string>", "exec") - Traceback (most recent call last): - ... - SyntaxError: Line 1: raise statements are not supported - - >>> compile("try: pass\nexcept: pass", "<string>", "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} - - diff --git a/src/zope/security/untrustedpython/tests.py b/src/zope/security/untrustedpython/tests.py deleted file mode 100644 index 6557640..0000000 --- a/src/zope/security/untrustedpython/tests.py +++ /dev/null @@ -1,36 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Untrusted python tests - -$Id$ -""" -import unittest -import re -from zope.testing import doctestunit,renormalizing - -def test_suite(): - checker = renormalizing.RENormalizing([ - (re.compile(r"'ImmutableModule' object"), - r'object'), - ]) - return unittest.TestSuite(( - doctestunit.DocFileSuite('builtins.txt', - 'rcompile.txt', - 'interpreter.txt',checker=checker - ), - )) - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') - diff --git a/src/zope/security/zcml.py b/src/zope/security/zcml.py deleted file mode 100644 index 0c0c3c3..0000000 --- a/src/zope/security/zcml.py +++ /dev/null @@ -1,146 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation 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. - -$Id$ -""" -__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 |