Commit 8997c967 authored by matt@zope.com's avatar matt@zope.com

Document Template accelerations and additonal C Security Manager accelerations

from cAccessControl-review-branch.
parent a7131d9f
......@@ -89,6 +89,11 @@ Zope Changes
- API help topics can now document functions as well as classes.
- Security accelerations in c
- Accelerated C Document Template handling; Document Templates
can now do additional rendering in C, improving performance.
Bugs fixed
- WebDAV: Zope escaped nested object properties derived from
......
......@@ -148,6 +148,16 @@ Security issues
2.5+)
ZSP_OWNEROUS_SKIP
If set, will cause the Zope Security Policy to skip checks relating
to ownership, for servers on which ownership is not important.
ZSP_AUTHENTICATED_SKIP
If set, will cause the Zope Security Policy to skip checks relating
to authentication, for servers which serve only anonymous content.
DISALLOW_LOCAL_PRODUCTS
unknown
......
......@@ -84,26 +84,42 @@
##############################################################################
'''Add security system support to Document Templates
$Id: DTML.py,v 1.7 2001/06/21 17:16:31 shane Exp $'''
__version__='$Revision: 1.7 $'[11:-2]
$Id: DTML.py,v 1.8 2001/10/26 16:07:50 matt Exp $'''
__version__='$Revision: 1.8 $'[11:-2]
from DocumentTemplate import DT_Util
import SecurityManagement, string, math, whrandom, random
import DocumentTemplate.sequence
from ZopeGuards import guarded_getattr, guarded_getitem, _marker
from ZopeGuards import guarded_getattr, guarded_getitem
class RestrictedDTML:
'''
A mix-in for derivatives of DT_String.String that adds Zope security.
'''
def guarded_getattr(self, ob, name, default=_marker):
return guarded_getattr(ob, name, default)
def guarded_getattr(self, *args): # ob, name [, default]
return guarded_getattr(*args)
def guarded_getitem(self, ob, index):
return guarded_getitem(ob, index)
try:
#raise ImportError
import os
if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
raise ImportError # :)
from cAccessControl import RestrictedDTMLMixin
except ImportError:
pass
else:
class RestrictedDTML(RestrictedDTMLMixin, RestrictedDTML):
'''
A mix-in for derivatives of DT_String.String that adds Zope security.
'''
# Allow access to unprotected attributes
DT_Util.TemplateDict.__allow_access_to_unprotected_subobjects__=1
string.__allow_access_to_unprotected_subobjects__=1
......
......@@ -85,8 +85,8 @@
__doc__='''Objects that implement Permission-based roles.
$Id: PermissionRole.py,v 1.12 2001/10/19 15:12:25 shane Exp $'''
__version__='$Revision: 1.12 $'[11:-2]
$Id: PermissionRole.py,v 1.13 2001/10/26 16:07:50 matt Exp $'''
__version__='$Revision: 1.13 $'[11:-2]
_use_python_impl = 0
import os
......
......@@ -85,8 +85,21 @@
__doc__='''short description
$Id: SecurityManagement.py,v 1.4 2001/07/02 16:29:55 evan Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
$Id: SecurityManagement.py,v 1.5 2001/10/26 16:07:50 matt Exp $'''
__version__='$Revision: 1.5 $'[11:-2]
def getSecurityManager():
"""Get a security manager, for the current thread.
"""
thread_id=get_ident()
manager=_managers.get(thread_id, None)
if manager is None:
manager=SecurityManager(
thread_id,
SecurityContext(SpecialUsers.nobody))
_managers[thread_id]=manager
return manager
import SpecialUsers
from SecurityManager import SecurityManager
......@@ -110,19 +123,6 @@ def noSecurityManager():
except: pass
def getSecurityManager():
"""Get a security manager, for the current thread.
"""
thread_id=get_ident()
manager=_managers.get(thread_id, None)
if manager is None:
manager=SecurityManager(
thread_id,
SecurityContext(SpecialUsers.nobody))
_managers[thread_id]=manager
return manager
def setSecurityPolicy(aSecurityPolicy):
"""Set the system default security policy.
......
......@@ -85,8 +85,8 @@
__doc__='''short description
$Id: SecurityManager.py,v 1.7 2001/10/19 15:12:25 shane Exp $'''
__version__='$Revision: 1.7 $'[11:-2]
$Id: SecurityManager.py,v 1.8 2001/10/26 16:07:50 matt Exp $'''
__version__='$Revision: 1.8 $'[11:-2]
import ZopeSecurityPolicy, os, string
......@@ -95,7 +95,12 @@ _noroles = ZopeSecurityPolicy._noroles
try: max_stack_size=string.atoi(os.environ.get('Z_MAX_STACK_SIZE','100'))
except: max_stack_size=100
_defaultPolicy=ZopeSecurityPolicy.ZopeSecurityPolicy()
if os.environ.has_key("ZSP_OWNEROUS_SKIP"): ownerous=0
else: ownerous=1
if os.environ.has_key("ZSP_AUTHENTICATION_SKIP"): authenticated=0
else: authenticated=1
_defaultPolicy=ZopeSecurityPolicy.ZopeSecurityPolicy(ownerous=ownerous,
authenticated=authenticated)
def setSecurityPolicy(aSecurityPolicy):
"""Set the system default security policy.
......@@ -107,6 +112,7 @@ def setSecurityPolicy(aSecurityPolicy):
_defaultPolicy=aSecurityPolicy
return last
class SecurityManager:
"""A security manager provides methods for checking access and managing
executable context and policies
......@@ -116,35 +122,34 @@ class SecurityManager:
'validate': 1, 'validateValue': 1, 'checkPermission': 1,
'getUser': 1, 'calledByExecutable': 1
}
def __init__(self, thread_id, context):
self._thread_id=thread_id
self._context=context
self._policy=None
self._policy=_defaultPolicy
def validate(self, accessed=None, container=None, name=None, value=None,
roles=_noroles):
"""Validate access.
Arguments:
accessed -- the object that was being accessed
container -- the object the value was found in
name -- The name used to access the value
value -- The value retrieved though the access.
roles -- The roles of the object if already known.
The arguments may be provided as keyword arguments. Some of these
arguments may be ommitted, however, the policy may reject access
in some cases when arguments are ommitted. It is best to provide
all the values possible.
"""
policy=self._policy
if policy is None: policy=_defaultPolicy
if roles is _noroles:
return policy.validate(accessed, container, name, value,
self._context)
......@@ -153,19 +158,19 @@ class SecurityManager:
self._context, roles)
def DTMLValidate(self, accessed=None, container=None, name=None,
value=None,md=None):
value=None, md=None):
"""Validate access.
* THIS EXISTS FOR DTML COMPATIBILITY *
Arguments:
accessed -- the object that was being accessed
container -- the object the value was found in
name -- The name used to access the value
value -- The value retrieved though the access.
md -- multidict for DTML (ignored)
......@@ -177,7 +182,6 @@ class SecurityManager:
"""
policy=self._policy
if policy is None: policy=_defaultPolicy
return policy.validate(accessed, container, name, value,
self._context)
......@@ -185,7 +189,6 @@ class SecurityManager:
"""Convenience for common case of simple value validation.
"""
policy=self._policy
if policy is None: policy=_defaultPolicy
if roles is _noroles:
return policy.validate(None, None, None, value,
self._context)
......@@ -198,13 +201,12 @@ class SecurityManager:
the given object.
Arguments:
permission -- A permission name
object -- The object being accessed according to the permission
"""
policy=self._policy
if policy is None: policy=_defaultPolicy
return policy.checkPermission(permission, object,
self._context)
......@@ -218,7 +220,10 @@ class SecurityManager:
raise SystemError, 'Excessive recursion'
stack.append(anExecutableObject)
p=getattr(anExecutableObject, '_customSecurityPolicy', None)
if p is not None: p=p()
if p is not None:
p=p()
else:
p=_defaultPolicy
self._policy=p
def removeContext(self, anExecutableObject,
......@@ -245,10 +250,13 @@ class SecurityManager:
if stack:
top=stack[-1]
p=getattr(top, '_customSecurityPolicy', None)
if p is not None: p=p()
if p is not None:
p=p()
else:
p=_defaultPolicy
self._policy=p
else:
self._policy=None
self._policy=_defaultPolicy
def getUser(self):
"""Get the current authenticated user"""
......@@ -260,3 +268,17 @@ class SecurityManager:
return len(self._context.stack)
try:
#raise ImportError
import os
if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
raise ImportError # :)
from cAccessControl import SecurityManager as cSecurityManager
except ImportError:
pass
else:
class SecurityManager(cSecurityManager, SecurityManager):
"""A security manager provides methods for checking access and managing
executable context and policies
"""
......@@ -83,7 +83,7 @@
#
##############################################################################
__version__='$Revision: 1.7 $'[11:-2]
__version__='$Revision: 1.8 $'[11:-2]
from RestrictedPython.Guards import safe_builtins, _full_read_guard, \
full_write_guard
......@@ -98,29 +98,41 @@ _marker = [] # Create a new marker object.
safe_builtins = safe_builtins.copy()
safe_builtins.update(utility_builtins)
def aq_validate(inst, obj, name, v, validate):
return validate(inst, obj, name, v)
def guarded_getattr(inst, name, default=_marker):
if name[:1] != '_':
# Try to get the attribute normally so that unusual
# exceptions are caught early.
try: v = getattr(inst, name)
except AttributeError:
if default is not _marker:
return default
raise
if Containers(type(inst)):
# Simple type. Short circuit.
return v
validate = getSecurityManager().validate
# Filter out the objects we can't access.
if hasattr(inst, 'aq_acquire'):
return inst.aq_acquire(name, aq_validate, validate)
# Or just try to get the attribute directly.
if validate(inst, inst, name, v):
return v
raise Unauthorized, name
try:
#raise ImportError
import os
if os.environ.get("ZOPE_SECURITY_POLICY", None) == "PYTHON":
raise ImportError # :)
from cAccessControl import aq_validate, guarded_getattr
except ImportError:
def aq_validate(inst, obj, name, v, validate):
return validate(inst, obj, name, v)
def guarded_getattr(inst, name, default=_marker):
if name[:1] != '_':
# Try to get the attribute normally so that unusual
# exceptions are caught early.
try: v = getattr(inst, name)
except AttributeError:
if default is not _marker:
return default
raise
if Containers(type(inst)):
# Simple type. Short circuit.
return v
validate = getSecurityManager().validate
# Filter out the objects we can't access.
if hasattr(inst, 'aq_acquire'):
return inst.aq_acquire(name, aq_validate, validate)
# Or just try to get the attribute directly.
if validate(inst, inst, name, v):
return v
raise Unauthorized, name
safe_builtins['getattr'] = guarded_getattr
def guarded_hasattr(object, name):
......
......@@ -85,8 +85,8 @@
__doc__='''Define Zope\'s default security policy
$Id: ZopeSecurityPolicy.py,v 1.14 2001/10/19 15:12:25 shane Exp $'''
__version__='$Revision: 1.14 $'[11:-2]
$Id: ZopeSecurityPolicy.py,v 1.15 2001/10/26 16:07:50 matt Exp $'''
__version__='$Revision: 1.15 $'[11:-2]
_use_python_impl = 0
......@@ -119,8 +119,32 @@ if _use_python_impl:
class ZopeSecurityPolicy:
def __init__(self, ownerous=1):
def __init__(self, ownerous=1, authenticated=1):
"""Create a Zope security policy.
Two optional keyword arguments may be provided:
ownerous -- Untrusted users can create code
(e.g. Python scripts or templates),
so check that code owners can access resources.
The argument must have a truth value.
The default is true.
authenticated -- Allow access to resources based on the
privaledges of the authenticated user.
The argument must have a truth value.
The default is true.
This (somewhat experimental) option can be set
to false on sites that allow only public
(unauthenticated) access. An anticipated
scenario is a ZEO configuration in which some
clients allow only public access and other
clients allow full management.
"""
self._ownerous=ownerous
self._authenticated=authenticated
def validate(self, accessed, container, name, value, context,
roles=_noroles, None=None, type=type, IntType=type(0),
......@@ -239,7 +263,8 @@ if _use_python_impl:
try:
if context.user.allowed(value, roles): return 1
if self._authenticated and context.user.allowed(value, roles):
return 1
except AttributeError: pass
# We don't want someone to acquire if they can't get an unacquired!
......@@ -254,4 +279,3 @@ if _use_python_impl:
if type(roles) is StringType:
roles=[roles]
return context.user.allowed(object, roles)
This diff is collapsed.
......@@ -147,8 +147,8 @@ __doc__='''Conditional insertion
variable is not reevaluated.
'''
__rcs_id__='$Id: DT_If.py,v 1.16 1999/03/10 00:15:07 klm Exp $'
__version__='$Revision: 1.16 $'[11:-2]
__rcs_id__='$Id: DT_If.py,v 1.17 2001/10/26 16:07:50 matt Exp $'
__version__='$Revision: 1.17 $'[11:-2]
from DT_Util import ParseError, parse_params, name_param, str
......@@ -192,7 +192,7 @@ class If:
if elses is not None: sections.append(elses)
self.simple_form=tuple(sections)
self.simple_form=('i',)+tuple(sections)
class Unless:
name='unless'
......@@ -204,7 +204,7 @@ class Unless:
name,expr=name_param(args,'unless',1)
if expr is None: cond=name
else: cond=expr.eval
self.simple_form=(cond,None,section.blocks)
self.simple_form=('i',cond,None,section.blocks)
class Else(Unless):
# The else tag is included for backward compatibility and is deprecated.
......
......@@ -217,8 +217,8 @@ Evaluating expressions without rendering results
''' # '
__rcs_id__='$Id: DT_Var.py,v 1.44 2001/10/02 14:23:32 shane Exp $'
__version__='$Revision: 1.44 $'[11:-2]
__rcs_id__='$Id: DT_Var.py,v 1.45 2001/10/26 16:07:50 matt Exp $'
__version__='$Revision: 1.45 $'[11:-2]
from DT_Util import parse_params, name_param, str
import re, string, sys
......@@ -254,7 +254,11 @@ class Var:
if len(args)==1 and fmt=='s':
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=expr,
self.simple_form=('v', expr)
elif len(args)==2 and fmt=='s' and args.has_key('html_quote'):
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=('v', expr, 'h')
def render(self, md):
args=self.args
......@@ -353,7 +357,7 @@ class Call:
name, expr = name_param(args,'call',1)
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=expr,None
self.simple_form=('i', expr, None)
def url_quote(v, name='(Unknown name)', md={}):
......
......@@ -84,7 +84,7 @@
****************************************************************************/
static char cDocumentTemplate_module_documentation[] =
""
"\n$Id: cDocumentTemplate.c,v 1.39 2001/06/21 19:08:59 shane Exp $"
"\n$Id: cDocumentTemplate.c,v 1.40 2001/10/26 16:07:50 matt Exp $"
;
#include "ExtensionClass.h"
......@@ -94,7 +94,7 @@ static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
static PyObject *py___class__;
static PyObject *py___class__, *html_quote;
/* ----------------------------------------------------- */
......@@ -398,13 +398,29 @@ static PyObject *
MM_cget(MM *self, PyObject *key, int call)
{
long i;
PyObject *e, *rr, *tb;
PyObject *e, *rr;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while (--i >= 0)
{
e=PyList_GetItem(self->data,i);
if ((e=PyObject_GetItem(e,key)))
e=PyList_GET_ITEM(self->data,i);
if (PyDict_Check(e))
{
e=PyDict_GetItem(e, key);
Py_XINCREF(e);
}
else
{
UNLESS (e=PyObject_GetItem(e,key))
{
if (PyErr_Occurred() == PyExc_KeyError)
PyErr_Clear();
else
return NULL;
}
}
if (e)
{
if (!call) return e;
......@@ -437,17 +453,8 @@ MM_cget(MM *self, PyObject *key, int call)
}
return e;
}
PyErr_Fetch(&e, &rr, &tb);
if (e != PyExc_KeyError)
{
PyErr_Restore(e,rr,tb);
return NULL;
}
Py_XDECREF(e);
Py_XDECREF(rr);
Py_XDECREF(tb);
}
PyErr_SetObject(PyExc_KeyError,key);
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
......@@ -726,7 +733,7 @@ static int
render_blocks_(PyObject *blocks, PyObject *rendered,
PyObject *md, PyObject *mda)
{
PyObject *block;
PyObject *block, *t;
int l, i, k=0, append;
if ((l=PyList_Size(blocks)) < 0) return -1;
......@@ -735,95 +742,123 @@ render_blocks_(PyObject *blocks, PyObject *rendered,
block=PyList_GET_ITEM(((PyListObject*)blocks), i);
append=1;
if (PyTuple_Check(block))
if (PyTuple_Check(block)
&& PyTuple_GET_SIZE(block) > 1
&& PyTuple_GET_ITEM(block, 0)
&& PyString_Check(PyTuple_GET_ITEM(block, 0)))
{
int bs;
switch (PyString_AS_STRING(PyTuple_GET_ITEM(block, 0))[0])
{
case 'v': /* var */
t=PyTuple_GET_ITEM(block,1);
bs=((PyTupleObject*)block)->ob_size;
if (bs==1)
{
/* Simple var */
block=PyTuple_GET_ITEM(block,0);
if (PyString_Check(block)) block=PyObject_GetItem(md,block);
else block=PyObject_CallObject(block,mda);
if (block) ASSIGN(block, PyObject_Str(block));
UNLESS(block) return -1;
}
else
{
/* if */
int icond, m;
PyObject *cond, *n, *cache;
UNLESS(cache=PyDict_New()) return -1;
cond=PyObject_GetAttr(md,py__push);
if (cond) ASSIGN(cond, PyObject_CallFunction(cond,"O",cache));
Py_DECREF(cache);
if (cond) Py_DECREF(cond);
else return -1;
if (t == NULL) return -1;
if (PyString_Check(t)) t=PyObject_GetItem(md, t);
else t=PyObject_CallObject(t, mda);
if (t == NULL || (! PyString_Check(t)))
{
if (t) ASSIGN(t, PyObject_Str(t));
UNLESS(t) return -1;
}
if (PyString_Check(t)
&& PyTuple_GET_SIZE(block) == 3) /* html_quote */
{
if (strchr(PyString_AS_STRING(t), '&')
&& strchr(PyString_AS_STRING(t), '<')
&& strchr(PyString_AS_STRING(t), '>')
&& strchr(PyString_AS_STRING(t), '"')
)
ASSIGN(t, PyObject_CallFunction(html_quote, "O", t));
if (t == NULL) return -1;
}
block = t;
break;
case 'i': /* if */
{
int icond, m, bs;
PyObject *cond, *n, *cache;
bs = PyTuple_GET_SIZE(block) - 1; /* subtract code */
UNLESS(cache=PyDict_New()) return -1;
cond=PyObject_GetAttr(md,py__push);
if (cond) ASSIGN(cond, PyObject_CallFunction(cond,"O",cache));
Py_DECREF(cache);
if (cond) Py_DECREF(cond);
else return -1;
append=0;
m=bs-1;
for (icond=0; icond < m; icond += 2)
{
cond=PyTuple_GET_ITEM(block,icond);
if (PyString_Check(cond))
{
/* We have to be careful to handle key errors here */
n=cond;
if ((cond=PyObject_GetItem(md,cond)))
{
if (PyDict_SetItem(cache, n, cond) < 0)
{
Py_DECREF(cond);
return if_finally(md,1);
}
}
else
{
PyObject *t, *v, *tb;
PyErr_Fetch(&t, &v, &tb);
if (t != PyExc_KeyError || PyObject_Compare(v,n))
{
PyErr_Restore(t,v,tb);
return if_finally(md,1);
}
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
cond=Py_None;
Py_INCREF(cond);
}
}
else
UNLESS(cond=PyObject_CallObject(cond,mda))
return if_finally(md,1);
if (PyObject_IsTrue(cond))
{
Py_DECREF(cond);
block=PyTuple_GET_ITEM(block,icond+1);
if (block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
m=-1;
break;
}
else Py_DECREF(cond);
}
append=0;
m=bs-1;
for (icond=0; icond < m; icond += 2)
{
cond=PyTuple_GET_ITEM(block,icond+1);
if (PyString_Check(cond))
{
/* We have to be careful to handle key errors here */
n=cond;
if ((cond=PyObject_GetItem(md,cond)))
{
if (PyDict_SetItem(cache, n, cond) < 0)
{
Py_DECREF(cond);
return if_finally(md,1);
}
}
else
{
PyObject *t, *v, *tb;
PyErr_Fetch(&t, &v, &tb);
if (t != PyExc_KeyError || PyObject_Compare(v,n))
{
PyErr_Restore(t,v,tb);
return if_finally(md,1);
}
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
cond=Py_None;
Py_INCREF(cond);
}
}
else
UNLESS(cond=PyObject_CallObject(cond,mda))
return if_finally(md,1);
if (PyObject_IsTrue(cond))
{
Py_DECREF(cond);
block=PyTuple_GET_ITEM(block,icond+1+1);
if (block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
m=-1;
break;
}
else Py_DECREF(cond);
}
if (icond==m)
{
block=PyTuple_GET_ITEM(block,icond);
block=PyTuple_GET_ITEM(block,icond+1);
if (block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
}
if (if_finally(md,0) == -2) return -1;
}
}
}
break;
default:
PyErr_Format(PyExc_ValueError,
"Invalid DTML command code, %s",
PyString_AS_STRING(PyTuple_GET_ITEM(block, 0)));
return -1;
}
}
else if (PyString_Check(block))
{
Py_INCREF(block);
......@@ -904,10 +939,14 @@ void
initcDocumentTemplate(void)
{
PyObject *m, *d;
char *rev="$Revision: 1.39 $";
char *rev="$Revision: 1.40 $";
DictInstanceType.ob_type=&PyType_Type;
UNLESS (html_quote = PyImport_ImportModule("html_quote")) return;
ASSIGN(html_quote, PyObject_GetAttrString(html_quote, "html_quote"));
UNLESS (html_quote) return;
UNLESS(py_isDocTemp=PyString_FromString("isDocTemp")) return;
UNLESS(py_renderNS=PyString_FromString("__render_with_namespace__")) return;
UNLESS(py_blocks=PyString_FromString("blocks")) return;
......@@ -946,6 +985,4 @@ initcDocumentTemplate(void)
PyDict_SetItemString(d, "__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
if (PyErr_Occurred())
Py_FatalError("can't initialize module cDocumentTemplate");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment