Commit f25d5315 authored by Shane Hathaway's avatar Shane Hathaway

Based on some semi-formal performance tests, read guards turned out to be

slower than the old code.  With this change, we're using simple function
calls again to perform security checks.  But the calling sequence is
intended to be easier to comprehend than the old code.  Now instead of
DT_String.String subclasses having a validate() method attached to them, they
subclass AccessControl.DTML.RestrictedDTML, which provides a guarded_getattr()
method and a guarded_getitem() method.

Note that the functionality of guarded_getattr() used to be implemented
both in C and Python (in cDocumentTemplate and DT_Util), but now it's in
one place, ZopeGuards.py.  Thus it's not only reusable but easy to
optimize.

I ran all the tests and ran the new code through the profiler again.  The
change sped up restricted code a little more than expected, which is
definitely a good thing, but that may indicate that nested scopes
have a hidden speed penalty.

Also, RestrictedPython is now restrictive about printing to targets and
two forms of augmented assignment had to be forbidden.
parent 80ff818e
......@@ -184,7 +184,8 @@ class DTMLFile(Bindings, Explicit, ClassicHTMLFile):
cns = bound_data['caller_namespace']
ns = self._Bindings_ns_class()
push = ns._push
ns.read_guard = None
ns.guarded_getattr = None
ns.guarded_getitem = None
req = None
kw_bind = kw
......
......@@ -402,8 +402,8 @@
''' #'
__rcs_id__='$Id: DT_In.py,v 1.51 2001/05/16 19:07:02 evan Exp $'
__version__='$Revision: 1.51 $'[11:-2]
__rcs_id__='$Id: DT_In.py,v 1.52 2001/06/21 17:45:12 shane Exp $'
__version__='$Revision: 1.52 $'[11:-2]
import sys
from DT_Util import ParseError, parse_params, name_param, str
......@@ -608,7 +608,7 @@ class InClass:
else:
result = []
append=result.append
read_guard = md.read_guard
guarded_getitem = getattr(md, 'guarded_getitem', None)
for index in range(first,end):
# preset
pkw['previous-sequence']= 0
......@@ -638,8 +638,8 @@ class InClass:
if index==last: pkw['sequence-end']=1
if read_guard is not None:
try: client = read_guard(sequence)[index]
if guarded_getitem is not None:
try: client = guarded_getitem(sequence, index)
except ValidationError, vv:
if (params.has_key('skip_unauthorized') and
params['skip_unauthorized']):
......@@ -727,11 +727,11 @@ class InClass:
try:
result = []
append=result.append
read_guard = md.read_guard
guarded_getitem = getattr(md, 'guarded_getitem', None)
for index in range(l):
if index==last: pkw['sequence-end']=1
if read_guard is not None:
try: client = read_guard(sequence)[index]
if guarded_getitem is not None:
try: client = guarded_getitem(sequence, index)
except ValidationError, vv:
if (self.args.has_key('skip_unauthorized') and
self.args['skip_unauthorized']):
......
......@@ -82,7 +82,7 @@
# attributions are listed in the accompanying credits file.
#
##############################################################################
"$Id: DT_String.py,v 1.45 2001/05/30 15:57:30 andreas Exp $"
"$Id: DT_String.py,v 1.46 2001/06/21 17:45:12 shane Exp $"
from string import split, strip
import thread,re,exceptions,os
......@@ -505,7 +505,8 @@ class String:
if globals: push(globals)
if mapping:
push(mapping)
md.read_guard=self.read_guard
md.guarded_getattr=self.guarded_getattr
md.guarded_getitem=self.guarded_getitem
if client is not None:
if type(client)==type(()):
md.this=client[-1]
......@@ -550,8 +551,8 @@ class String:
if pushed: md._pop(pushed) # Get rid of circular reference!
md.level=level # Restore previous level
read_guard__roles__=()
read_guard=None
guarded_getattr=None
guarded_getitem=None
def __str__(self):
return self.read()
......
......@@ -82,8 +82,8 @@
# attributions are listed in the accompanying credits file.
#
##############################################################################
'''$Id: DT_Util.py,v 1.79 2001/06/18 18:44:45 chrism Exp $'''
__version__='$Revision: 1.79 $'[11:-2]
'''$Id: DT_Util.py,v 1.80 2001/06/21 17:45:12 shane Exp $'''
__version__='$Revision: 1.80 $'[11:-2]
import re, os
from html_quote import html_quote # for import by other modules, dont remove!
......@@ -143,19 +143,26 @@ if LIMITED_BUILTINS:
_marker = [] # Create a new marker object.
def careful_getattr(md, inst, name, default=_marker):
read_guard = md.read_guard
if read_guard is not None:
inst = read_guard(inst)
if default is _marker:
return getattr(inst, name)
else:
return getattr(inst, name, default)
get = md.guarded_getattr
if get is None:
get = getattr
try:
return get(inst, name)
except AttributeError:
if default is _marker:
raise
return default
def careful_hasattr(md, inst, name):
read_guard = md.read_guard
if read_guard is not None:
inst = read_guard(inst)
return hasattr(inst, name)
get = md.guarded_getattr
if get is None:
get = getattr
try:
get(inst, name)
except (AttributeError, ValidationError):
return 0
else:
return 1
d['getattr']=careful_getattr
d['hasattr']=careful_hasattr
......@@ -195,12 +202,15 @@ d['render']=render
class Eval(RestrictionCapableEval):
def eval(self, md):
guard = getattr(md, 'read_guard', None)
if guard is not None:
gattr = getattr(md, 'guarded_getattr', None)
if gattr is not None:
gitem = getattr(md, 'guarded_getitem', None)
self.prepRestrictedCode()
code = self.rcode
d = {'_': md, '_vars': md,
'_read_': guard, '__builtins__': None}
'_getattr_': gattr,
'_getitem_': gitem,
'__builtins__': None}
else:
self.prepUnrestrictedCode()
code = self.ucode
......
......@@ -105,8 +105,8 @@
'''
__rcs_id__='$Id: DT_With.py,v 1.12 2001/04/27 20:27:39 shane Exp $'
__version__='$Revision: 1.12 $'[11:-2]
__rcs_id__='$Id: DT_With.py,v 1.13 2001/06/21 17:45:12 shane Exp $'
__version__='$Revision: 1.13 $'[11:-2]
from DT_Util import parse_params, name_param, InstanceDict, render_blocks, str
from DT_Util import TemplateDict
......@@ -139,8 +139,10 @@ class With:
if self.only:
_md=md
md=TemplateDict()
if hasattr(_md, 'read_guard'):
md.read_guard = _md.read_guard
if hasattr(_md, 'guarded_getattr'):
md.guarded_getattr = _md.guarded_getattr
if hasattr(_md, 'guarded_getitem'):
md.guarded_getitem = _md.guarded_getitem
md._push(v)
try: return render_blocks(self.section, md)
......
......@@ -146,14 +146,13 @@ Access Control
Document templates provide a basic level of access control by
preventing access to names beginning with an underscore.
Additional control may be provided by providing document templates
with a 'read_guard' method. This would typically be done by
subclassing one or more of the DocumentTemplate classes.
with a 'guarded_getattr' and 'guarded_getitem' method. This would
typically be done by subclassing one or more of the DocumentTemplate
classes.
If provided, the the 'read_guard' method will be called when objects
are accessed as accessed as instance attributes or when they are
accessed through keyed access in an expression.. The 'read_guard'
method will be called with the containing object. It can
return a wrapper object from which the attribute will be accessed.
If provided, the the 'guarded_getattr' method will be called when
objects are accessed as instance attributes or when they are
accessed through keyed access in an expression.
Document Templates may be created 4 ways:
......@@ -178,7 +177,7 @@ Document Templates may be created 4 ways:
'''
__version__='$Revision: 1.11 $'[11:-2]
__version__='$Revision: 1.12 $'[11:-2]
ParseError='Document Template Parse Error'
......
......@@ -84,7 +84,7 @@
****************************************************************************/
static char cDocumentTemplate_module_documentation[] =
""
"\n$Id: cDocumentTemplate.c,v 1.37 2001/05/23 18:20:03 shane Exp $"
"\n$Id: cDocumentTemplate.c,v 1.38 2001/06/21 17:45:12 shane Exp $"
;
#include "ExtensionClass.h"
......@@ -92,7 +92,7 @@ static char cDocumentTemplate_module_documentation[] =
static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire;
static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_read_guard;
static PyObject *py_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
/* ----------------------------------------------------- */
......@@ -108,7 +108,7 @@ typedef struct {
PyObject *inst;
PyObject *cache;
PyObject *namespace;
PyObject *read_guard;
PyObject *guarded_getattr;
} InstanceDictobject;
staticforward PyExtensionClass InstanceDictType;
......@@ -116,18 +116,19 @@ staticforward PyExtensionClass InstanceDictType;
static PyObject *
InstanceDict___init__(InstanceDictobject *self, PyObject *args)
{
self->read_guard=NULL;
self->guarded_getattr=NULL;
UNLESS(PyArg_ParseTuple(args, "OO|O",
&(self->inst),
&(self->namespace),
&(self->read_guard)))
&(self->guarded_getattr)))
return NULL;
Py_INCREF(self->inst);
Py_INCREF(self->namespace);
if (self->read_guard)
Py_INCREF(self->read_guard);
if (self->guarded_getattr)
Py_INCREF(self->guarded_getattr);
else
UNLESS(self->read_guard=PyObject_GetAttr(self->namespace, py_read_guard))
UNLESS(self->guarded_getattr=PyObject_GetAttr(self->namespace,
py_guarded_getattr))
return NULL;
UNLESS(self->cache=PyDict_New()) return NULL;
......@@ -150,7 +151,7 @@ InstanceDict_dealloc(InstanceDictobject *self)
Py_XDECREF(self->inst);
Py_XDECREF(self->cache);
Py_XDECREF(self->namespace);
Py_XDECREF(self->read_guard);
Py_XDECREF(self->guarded_getattr);
Py_DECREF(self->ob_type);
PyMem_DEL(self);
}
......@@ -193,16 +194,13 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key)
return PyObject_Str(self->inst);
}
if (self->read_guard != Py_None) {
r = PyObject_CallFunction(self->read_guard, "O", self->inst);
if (!r) return NULL;
if (self->guarded_getattr != Py_None) {
r = PyObject_CallFunction(self->guarded_getattr, "OO", self->inst, key);
}
else {
r = self->inst;
Py_INCREF(r);
r = PyObject_GetAttr(self->inst, key);
}
ASSIGN(r, PyObject_GetAttr(r, key));
if (!r) {
PyObject *tb;
......@@ -877,7 +875,7 @@ void
initcDocumentTemplate(void)
{
PyObject *m, *d;
char *rev="$Revision: 1.37 $";
char *rev="$Revision: 1.38 $";
DictInstanceType.ob_type=&PyType_Type;
......@@ -889,7 +887,7 @@ initcDocumentTemplate(void)
UNLESS(py___roles__=PyString_FromString("__roles__")) return;
UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return;
UNLESS(py_hasRole=PyString_FromString("hasRole")) return;
UNLESS(py_read_guard=PyString_FromString("read_guard")) return;
UNLESS(py_guarded_getattr=PyString_FromString("guarded_getattr")) return;
UNLESS(py__push=PyString_FromString("_push")) return;
UNLESS(py__pop=PyString_FromString("_pop")) return;
UNLESS(py_aq_base=PyString_FromString("aq_base")) return;
......
......@@ -85,8 +85,8 @@
__doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.28 2001/04/27 20:27:39 shane Exp $'''
__version__='$Revision: 1.28 $'[11:-2]
$Id: pDocumentTemplate.py,v 1.29 2001/06/21 17:45:12 shane Exp $'''
__version__='$Revision: 1.29 $'[11:-2]
import string, sys, types
from string import join
......@@ -117,14 +117,15 @@ isSimpleType=isSimpleType.has_key
class InstanceDict:
read_guard=None
guarded_getattr=None
def __init__(self,o,namespace,read_guard=None):
def __init__(self,o,namespace,guarded_getattr=None):
self.self=o
self.cache={}
self.namespace=namespace
if read_guard is None: self.read_guard=namespace.read_guard
else: self.read_guard=read_guard
if guarded_getattr is None:
self.guarded_getattr = namespace.guarded_getattr
else: self.guarded_getattr = guarded_getattr
def has_key(self,key):
return hasattr(self.self,key)
......@@ -147,11 +148,11 @@ class InstanceDict:
else:
return str(inst)
read_guard = self.read_guard
if read_guard is not None:
inst = read_guard(inst)
get = self.guarded_getattr
if get is None:
get = getattr
try: r = getattr(inst, key)
try: r = get(inst, key)
except AttributeError: raise KeyError, key
self.cache[key]=r
......
......@@ -84,7 +84,7 @@
##############################################################################
"""DTML Method objects."""
__version__='$Revision: 1.66 $'[11:-2]
__version__='$Revision: 1.67 $'[11:-2]
import History
from Globals import HTML, DTMLFile, MessageDialog
......@@ -100,12 +100,13 @@ from ZDOM import ElementWithTitle
from DateTime.DateTime import DateTime
from urllib import quote
import Globals, sys, Acquisition
from AccessControl import getSecurityManager, full_read_guard
from AccessControl import getSecurityManager
from AccessControl.DTML import RestrictedDTML
from Cache import Cacheable
_marker = [] # Create a new marker object.
class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
class DTMLMethod(RestrictedDTML, HTML, Acquisition.Implicit, RoleManager,
ElementWithTitle, Item_w__name__,
History.Historical,
Cacheable,
......@@ -261,9 +262,6 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
# deprecated; use get_size!
getSize=get_size
def read_guard(self, ob):
return full_read_guard(ob)
manage_editForm=DTMLFile('dtml/documentEdit', globals())
manage_editForm._setName('manage_editForm')
......
......@@ -83,7 +83,7 @@
#
##############################################################################
__doc__="""Find support"""
__version__='$Revision: 1.24 $'[11:-2]
__version__='$Revision: 1.25 $'[11:-2]
import sys, os, string, time, Globals, ExtensionClass
......@@ -93,7 +93,7 @@ from Globals import DTMLFile
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from DateTime import DateTime
from string import find
from AccessControl import getSecurityManager, full_read_guard
from AccessControl.DTML import RestrictedDTML
class FindSupport(ExtensionClass.Base):
"""Find support for Zope Folders"""
......@@ -305,10 +305,8 @@ class FindSupport(ExtensionClass.Base):
class td(TemplateDict):
def read_guard(self, ob):
return full_read_guard(ob)
class td(RestrictedDTML, TemplateDict):
pass
def expr_match(ob, ed, c=InstanceDict, r=0):
......
......@@ -89,7 +89,7 @@ This product provides support for Script objects containing restricted
Python code.
"""
__version__='$Revision: 1.32 $'[11:-2]
__version__='$Revision: 1.33 $'[11:-2]
import sys, os, traceback, re, marshal
from Globals import DTMLFile, MessageDialog, package_home
......@@ -103,7 +103,8 @@ from Shared.DC.Scripts.Script import Script, BindingsUI, defaultBindings
from AccessControl import getSecurityManager
from OFS.History import Historical, html_diff
from OFS.Cache import Cacheable
from AccessControl import full_read_guard, full_write_guard, safe_builtins
from AccessControl import full_write_guard, safe_builtins
from AccessControl.ZopeGuards import guarded_getattr, guarded_getitem
from zLOG import LOG, ERROR, INFO, PROBLEM
# Track the Python bytecode version
......@@ -112,7 +113,7 @@ Python_magic = imp.get_magic()
del imp
# This should only be incremented to force recompilation.
Script_magic = 1
Script_magic = 2
manage_addPythonScriptForm = DTMLFile('www/pyScriptAdd', globals())
_default_file = os.path.join(package_home(globals()),
......@@ -303,7 +304,8 @@ class PythonScript(Script, Historical, Cacheable):
def _newfun(self, code):
g = {'__debug__': __debug__,
'__builtins__': safe_builtins,
'_read_': full_read_guard,
'_getattr_': guarded_getattr,
'_getitem_': guarded_getitem,
'_write_': full_write_guard,
'_print_': RestrictedPython.PrintCollector
}
......
......@@ -90,7 +90,7 @@ Scripts. It can be accessed from Python with the statement
"import Products.PythonScripts.standard"
"""
__version__='$Revision: 1.5 $'[11:-2]
__version__='$Revision: 1.6 $'[11:-2]
from AccessControl import ModuleSecurityInfo, getSecurityManager
security = ModuleSecurityInfo()
......@@ -105,10 +105,10 @@ from DocumentTemplate.DT_Var import special_formats, \
html_quote, url_quote, url_quote_plus, newline_to_br, thousands_commas
from Globals import HTML
from AccessControl import full_read_guard
from AccessControl.DTML import RestrictedDTML
security.declarePublic('DTML')
class DTML(HTML):
class DTML(RestrictedDTML, HTML):
"""DTML objects are DocumentTemplate.HTML objects that allow
dynamic, temporary creation of restricted DTML."""
def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
......@@ -122,8 +122,5 @@ class DTML(HTML):
finally: security.removeContext(self)
def read_guard(self, ob):
return full_read_guard(ob)
security.apply(globals())
......@@ -97,7 +97,8 @@ from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from DocumentTemplate.DT_Util import Eval
from AccessControl.Permission import name_trans
from Catalog import Catalog, CatalogError
from AccessControl import getSecurityManager, full_read_guard
from AccessControl import getSecurityManager
from AccessControl.DTML import RestrictedDTML
from zLOG import LOG, ERROR
from ZCatalogIndexes import ZCatalogIndexes
from Products.PluginIndexes.common.PluggableIndex import PluggableIndexInterface
......@@ -846,10 +847,8 @@ def absattr(attr):
return attr
class td(TemplateDict):
def read_guard(self, ob):
return full_read_guard(ob)
class td(RestrictedDTML, TemplateDict):
pass
def expr_match(ob, ed, c=InstanceDict, r=0):
e, md, push, pop=ed
......
......@@ -84,8 +84,8 @@
##############################################################################
"""Restricted Python Expressions
"""
__rcs_id__='$Id: Eval.py,v 1.2 2001/04/27 20:27:51 shane Exp $'
__version__='$Revision: 1.2 $'[11:-2]
__rcs_id__='$Id: Eval.py,v 1.3 2001/06/21 17:45:13 shane Exp $'
__version__='$Revision: 1.3 $'[11:-2]
from string import translate, strip
import string
......@@ -93,9 +93,11 @@ compile_restricted_eval = None
nltosp = string.maketrans('\r\n',' ')
def default_read_guard(ob):
default_guarded_getattr = getattr # No restrictions.
def default_guarded_getitem(ob, index):
# No restrictions.
return ob
return ob[index]
PROFILE = 0
......@@ -172,7 +174,8 @@ class RestrictionCapableEval:
# This is meant to be overridden.
self.prepRestrictedCode()
code = self.rcode
d = {'_read_': default_read_guard}
d = {'_getattr_': default_guarded_getattr,
'_getitem_': default_guarded_getitem}
d.update(self.globals)
has_key = d.has_key
for name in self.used:
......
......@@ -87,7 +87,7 @@ RestrictionMutator modifies a tree produced by
compiler.transformer.Transformer, restricting and enhancing the
code in various ways before sending it to pycodegen.
'''
__version__='$Revision: 1.4 $'[11:-2]
__version__='$Revision: 1.5 $'[11:-2]
from compiler import ast
from compiler.transformer import parse
......@@ -114,22 +114,27 @@ def exprNode(txt):
'''Make a "clean" expression node'''
return stmtNode(txt).expr
# There should be three objects in the global namespace.
# There should be up to four objects in the global namespace.
# If a wrapper function or print target is needed in a particular
# module or function, it is obtained from one of these objects.
# It is stored in a variable with the same name as the global
# object, but without a single trailing underscore. This variable is
# local, and therefore efficient to access, in function scopes.
_print_target_name = ast.Name('_print')
_read_guard_name = ast.Name('_read')
_getattr_name = ast.Name('_getattr')
_getitem_name = ast.Name('_getitem')
_write_guard_name = ast.Name('_write')
# Constants.
_None_const = ast.Const(None)
_write_const = ast.Const('write')
# Example prep code:
#
# global _read_
# _read = _read_
# global _getattr_
# _getattr = _getattr_
_prep_code = {}
for _n in ('read', 'write', 'print'):
for _n in ('getattr', 'getitem', 'write', 'print'):
_prep_code[_n] = [ast.Global(['_%s_' % _n]),
stmtNode('_%s = _%s_' % (_n, _n))]
# Call the global _print instead of copying it.
......@@ -142,7 +147,8 @@ _printed_expr = exprNode('_print()')
class FuncInfo:
_print_used = 0
_printed_used = 0
_read_used = 0
_getattr_used = 0
_getitem_used = 0
_write_used = 0
......@@ -189,8 +195,10 @@ class RestrictionMutator:
elif not info._print_used:
self.warnings.append(
"Doesn't print, but reads 'printed' variable.")
if info._read_used:
body[0:0] = _prep_code['read']
if info._getattr_used:
body[0:0] = _prep_code['getattr']
if info._getitem_used:
body[0:0] = _prep_code['getitem']
if info._write_used:
body[0:0] = _prep_code['write']
......@@ -217,7 +225,13 @@ class RestrictionMutator:
if node.dest is None:
node.dest = _print_target_name
else:
node.dest = ast.Or([node.dest, _print_target_name])
self.funcinfo._getattr_used = 1
# Pre-validate access to the "write" attribute.
# "print >> ob, x" becomes
# "print >> (_getattr(ob, 'write') and ob), x"
node.dest = ast.And([
ast.CallFunc(_getattr_name, [node.dest, _write_const]),
node.dest])
return node
visitPrintnl = visitPrint
......@@ -238,26 +252,49 @@ class RestrictionMutator:
def visitGetattr(self, node, walker):
self.checkAttrName(node)
node = walker.defaultVisitNode(node)
expr = [node.expr]
if getattr(node, 'use_dual_guard', 0):
if getattr(node, 'in_aug_assign', 0):
# We're in an augmented assignment
expr.append(_write_guard_name)
self.funcinfo._write_used = 1
node.expr = ast.CallFunc(_read_guard_name, expr)
self.funcinfo._read_used = 1
return node
# We might support this later...
self.error(node, 'Augmented assignment of '
'attributes is not allowed.')
#expr.append(_write_guard_name)
#self.funcinfo._write_used = 1
self.funcinfo._getattr_used = 1
return ast.CallFunc(_getattr_name,
[node.expr, ast.Const(node.attrname)])
def visitSubscript(self, node, walker):
node = walker.defaultVisitNode(node)
if node.flags == OP_APPLY:
# get subscript or slice
expr = [node.expr]
if getattr(node, 'use_dual_guard', 0):
if getattr(node, 'in_aug_assign', 0):
# We're in an augmented assignment
expr.append(_write_guard_name)
self.funcinfo._write_used = 1
node.expr = ast.CallFunc(_read_guard_name, expr)
self.funcinfo._read_used = 1
# We might support this later...
self.error(node, 'Augmented assignment of '
'object items and slices is not allowed.')
#expr.append(_write_guard_name)
#self.funcinfo._write_used = 1
self.funcinfo._getitem_used = 1
if hasattr(node, 'subs'):
# Subscript.
subs = node.subs
if len(subs) > 1:
# example: ob[1,2]
subs = ast.Tuple(subs)
else:
# example: ob[1]
subs = subs[0]
else:
# Slice.
# example: obj[0:2]
lower = node.lower
if lower is None:
lower = _None_const
upper = node.upper
if upper is None:
upper = _None_const
subs = ast.Sliceobj([lower, upper])
return ast.CallFunc(_getitem_name, [node.expr, subs])
elif node.flags in (OP_DELETE, OP_ASSIGN):
# set or remove subscript or slice
node.expr = ast.CallFunc(_write_guard_name, [node.expr])
......@@ -287,75 +324,6 @@ class RestrictionMutator:
return node
def visitAugAssign(self, node, walker):
node.node.use_dual_guard = 1
node.node.in_aug_assign = 1
return walker.defaultVisitNode(node)
if __name__ == '__main__':
# A minimal test.
from compiler import visitor, pycodegen
class Noisy:
'''Test guard class that babbles about accesses'''
def __init__(self, _ob):
self.__dict__['_ob'] = _ob
# Read guard methods
def __len__(self):
# This is called by the interpreter before __getslice__().
_ob = self.__dict__['_ob']
print '__len__', `_ob`
return len(_ob)
def __getattr__(self, name):
_ob = self.__dict__['_ob']
print '__getattr__', `_ob`, name
return getattr(_ob, name)
def __getitem__(self, index):
# Can receive an Ellipsis or "slice" instance.
_ob = self.__dict__['_ob']
print '__getitem__', `_ob`, index
return _ob[index]
def __getslice__(self, lo, hi):
_ob = self.__dict__['_ob']
print '__getslice__', `_ob`, lo, hi
return _ob[lo:hi]
# Write guard methods
def __setattr__(self, name, value):
_ob = self.__dict__['_ob']
print '__setattr__', `_ob`, name, value
setattr(_ob, name, value)
def __setitem__(self, index, value):
_ob = self.__dict__['_ob']
print '__setitem__', `_ob`, index, value
_ob[index] = value
def __setslice__(self, lo, hi, value):
_ob = self.__dict__['_ob']
print '__setslice__', `_ob`, lo, hi, value
_ob[lo:hi] = value
tree = parse('''
def f():
print "Hello",
print "... wOrLd!".lower()
x = {}
x['p'] = printed[1:-1]
x['p'] += (lambda ob: ob * 2)(printed)
return x['p']
''')
MutatingWalker.walk(tree, RestrictionMutator())
print tree
gen = pycodegen.NestedScopeModuleCodeGenerator('some_python_script')
visitor.walk(tree, gen, verbose=1)
code = gen.getCode()
dict = {'__builtins__': None}
exec code in dict
from PrintCollector import PrintCollector
f = dict['f']
f.func_globals.update({
'_print_target_class': PrintCollector,
'_read_guard_': Noisy,
'_write_guard_': Noisy,
})
print f()
#import dis
#dis.dis(f.func_code)
......@@ -8,7 +8,7 @@ def print1():
print 'world!',
return printed
def print2():
def printToNone():
x = None
print >>x, 'Hello, world!',
return printed
......@@ -53,21 +53,26 @@ def allowed_simple():
def allowed_write(ob):
ob.writeable = 1
ob.writeable += 1
#ob.writeable += 1
[1 for ob.writeable in 1,2]
ob['safe'] = 2
ob['safe'] += 2
#ob['safe'] += 2
[1 for ob['safe'] in 1,2]
def denied_print(ob):
print >> ob, 'Hello, world!',
def denied_getattr(ob):
ob.disallowed += 1
#ob.disallowed += 1
ob.disallowed = 1
return ob.disallowed
def denied_setattr(ob):
ob.allowed = -1
def denied_setattr2(ob):
ob.allowed += -1
#ob.allowed += -1
ob.allowed = -1
def denied_setattr3(ob):
[1 for ob.allowed in 1,2]
......@@ -76,13 +81,15 @@ def denied_getitem(ob):
ob[1]
def denied_getitem2(ob):
ob[1] += 1
#ob[1] += 1
ob[1]
def denied_setitem(ob):
ob['x'] = 2
def denied_setitem2(ob):
ob[0] += 2
#ob[0] += 2
ob['x'] = 2
def denied_setitem3(ob):
[1 for ob['x'] in 1,2]
......@@ -91,16 +98,17 @@ def denied_setslice(ob):
ob[0:1] = 'a'
def denied_setslice2(ob):
ob[0:1] += 'a'
#ob[0:1] += 'a'
ob[0:1] = 'a'
def denied_setslice3(ob):
[1 for ob[0:1] in 1,2]
def strange_attribute():
# If a guard has attributes with names that don't start with an
# underscore, those attributes appear to be an attribute of
# anything.
return [].attribute_of_anything
##def strange_attribute():
## # If a guard has attributes with names that don't start with an
## # underscore, those attributes appear to be an attribute of
## # anything.
## return [].attribute_of_anything
def order_of_operations():
return 3 * 4 * -2 + 2 * 12
......
......@@ -4,20 +4,20 @@
# Each function in this module is compiled using compile_restricted().
def overrideGuardWithFunction():
def _guard(o): return o
def _getattr(o): return o
def overrideGuardWithLambda():
lambda o, _guard=None: o
lambda o, _getattr=None: o
def overrideGuardWithClass():
class _guard:
class _getattr:
pass
def overrideGuardWithName():
_guard = None
_getattr = None
def overrideGuardWithArgument():
def f(_guard=None):
def f(_getattr=None):
pass
def reserved_names():
......
......@@ -106,35 +106,38 @@ class RestrictedObject:
def __setslice__(self, lo, hi, value):
raise AccessDenied
write = DisallowedObject
def guarded_getattr(ob, name):
v = getattr(ob, name)
if v is DisallowedObject:
raise AccessDenied
return v
SliceType = type(slice(0))
def guarded_getitem(ob, index):
if type(index) is SliceType and index.step is None:
start = index.start
stop = index.stop
if start is None:
start = 0
if stop is None:
v = ob[start:]
else:
v = ob[start:stop]
else:
v = ob[index]
if v is DisallowedObject:
raise AccessDenied
return v
class TestGuard:
'''A guard class'''
def __init__(self, _ob, write=None):
self.__dict__['_ob'] = _ob
# Read guard methods
def __len__(self):
return len(self.__dict__['_ob'])
def __getattr__(self, name):
_ob = self.__dict__['_ob']
v = getattr(_ob, name)
if v is DisallowedObject:
raise AccessDenied
return v
def __getitem__(self, index):
# Can receive an Ellipsis or "slice" instance.
_ob = self.__dict__['_ob']
v = _ob[index]
if v is DisallowedObject:
raise AccessDenied
return v
def __getslice__(self, lo, hi):
_ob = self.__dict__['_ob']
return _ob[lo:hi]
# Write guard methods
def __setattr__(self, name, value):
......@@ -154,21 +157,31 @@ class TestGuard:
_ob = self.__dict__['_ob']
_ob[lo:hi] = value
attribute_of_anything = 98.6
## attribute_of_anything = 98.6
class RestrictionTests(unittest.TestCase):
def execFunc(self, name, *args, **kw):
func = rmodule[name]
func.func_globals.update({'_read_': TestGuard,
func.func_globals.update({'_getattr_': guarded_getattr,
'_getitem_': guarded_getitem,
'_write_': TestGuard,
'_print_': PrintCollector})
return func(*args, **kw)
def checkPrint(self):
for i in range(3):
for i in range(2):
res = self.execFunc('print%s' % i)
assert res == 'Hello, world!', res
def checkPrintToNone(self):
try:
res = self.execFunc('printToNone')
except AttributeError:
# Passed. "None" has no "write" attribute.
pass
else:
assert 0, res
def checkPrintLines(self):
res = self.execFunc('printLines')
assert res == '0 1 2\n3 4 5\n6 7 8\n', res
......@@ -221,9 +234,9 @@ class RestrictionTests(unittest.TestCase):
else:
raise AssertionError, '%s should not have compiled' % k
def checkStrangeAttribute(self):
res = self.execFunc('strange_attribute')
assert res == 98.6, res
## def checkStrangeAttribute(self):
## res = self.execFunc('strange_attribute')
## assert res == 98.6, res
def checkOrderOfOperations(self):
res = self.execFunc('order_of_operations')
......
......@@ -85,7 +85,7 @@
__doc__='''Generic Database adapter'''
__version__='$Revision: 1.102 $'[11:-2]
__version__='$Revision: 1.103 $'[11:-2]
import OFS.SimpleItem, Aqueduct, RDB, re
import DocumentTemplate, marshal, md5, base64, Acquisition, os
......@@ -105,7 +105,8 @@ import DocumentTemplate.DT_Util
from cPickle import dumps, loads
from Results import Results
from App.Extensions import getBrain
from AccessControl import getSecurityManager, full_read_guard
from AccessControl import getSecurityManager
from AccessControl.DTML import RestrictedDTML
from webdav.Resource import Resource
from webdav.Lockable import ResourceLockedError
try: from IOBTree import Bucket
......@@ -123,11 +124,10 @@ class nvSQL(DocumentTemplate.HTML):
_proxy_roles=()
class SQL(ExtensionClass.Base, nvSQL):
class SQL(RestrictedDTML, ExtensionClass.Base, nvSQL):
# Validating SQL template for Zope SQL Methods.
pass
def read_guard(self, ob):
return full_read_guard(ob)
class DA(
Aqueduct.BaseQuery,Acquisition.Implicit,
......
......@@ -84,8 +84,8 @@
##############################################################################
"""Rendering object hierarchies as Trees
"""
__rcs_id__='$Id: TreeTag.py,v 1.47 2001/05/16 19:07:06 evan Exp $'
__version__='$Revision: 1.47 $'[11:-2]
__rcs_id__='$Id: TreeTag.py,v 1.48 2001/06/21 17:45:14 shane Exp $'
__version__='$Revision: 1.48 $'[11:-2]
from DocumentTemplate.DT_Util import *
from DocumentTemplate.DT_String import String
......@@ -215,11 +215,10 @@ def tpRender(self, md, section, args,
have_arg=args.has_key
if have_arg('branches'):
def get_items(node, branches=args['branches'], md=md):
read_guard = md.read_guard
if read_guard is None:
items = getattr(node, branches)
else:
items = getattr(read_guard(node), branches)
get = md.guarded_getattr
if get is None:
get = getattr
items = get(node, branches)
return items()
elif have_arg('branches_expr'):
def get_items(node, branches_expr=args['branches_expr'], md=md):
......@@ -314,15 +313,14 @@ def tpRenderTABLE(self, id, root_url, url, state, substate, diff, data,
break
if not exp: items=1
read_guard = md.read_guard
get=md.guarded_getattr
if get is None:
get = getattr
if items is None:
if have_arg('branches') and hasattr(self, args['branches']):
if read_guard is None:
items = getattr(self, args['branches'])
else:
items = getattr(read_guard(self), args['branches'])
items=items()
items = get(self, args['branches'])
items = items()
elif have_arg('branches_expr'):
items=args['branches_expr'](md)
......@@ -330,12 +328,12 @@ def tpRenderTABLE(self, id, root_url, url, state, substate, diff, data,
if items and items != 1:
if read_guard is not None:
getitem = getattr(md, 'guarded_getitem', None)
if getitem is not None:
unauth=[]
guarded_items = read_guard(items)
for index in range(len(items)):
try:
guarded_items[index]
getitem(items, index)
except ValidationError:
unauth.append(index)
if unauth:
......
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