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