Commit b1c1f7e3 authored by Evan Simpson's avatar Evan Simpson

Added skip_unauthorized functionality, utility functions.

parent 012936aa
ZTUtils changes
This file contains change information for the current release.
Change information for previous versions can be found in the
file HISTORY.txt.
Version 1.1.0
Features Added
- TreeMakers have a setChildAccess() method that you can use
to control tree construction. Child nodes can be accessed
through either an attribute name or callback function.
Children fetched by attribute name can be filtered through a
callback function.
- A new LazyFilter class allows you to filter a sequence using
Zope security and an optional filter callback function. The
security and filter tests are lazy, meaning they are
performed as late as possible.
The optional 'skip' argument determines the reaction when
access to a sequence element is refused by the Zope security
policy. The default (None) is to raise the 'Unauthorized'
exception. If a string is passed, such elements are
skipped. If the string is non-empty, it is treated as a
permission name, and the element is skipped if the user
doesn't have that permission on the element.
- The Zope versions of TreeMaker, SimpleTreeMaker, and Batch
now use LazyFilter. The TreeMakers have a setSkip() method
that can be used to set the 'skip' value. Batch has an
optional 'skip_unauthorized' argument that is passed to
LazyFilter as 'skip'.
- Utility functions make_query(), url_query(), and
make_hidden_input() have been added.
Bugs Fixed
......@@ -84,15 +84,86 @@
##############################################################################
__doc__='''Zope-specific versions of ZTUTils classes
$Id: Zope.py,v 1.1 2001/07/11 19:02:22 evan Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
$Id: Zope.py,v 1.2 2001/08/13 18:15:34 evan Exp $'''
__version__='$Revision: 1.2 $'[11:-2]
from Tree import encodeExpansion, decodeExpansion
import sys, cgi, urllib, cgi
from Tree import encodeExpansion, decodeExpansion, TreeMaker
from SimpleTree import SimpleTreeMaker
from Batch import Batch
from Products.ZCatalog.Lazy import Lazy
from AccessControl.ZopeGuards import guarded_getitem
from AccessControl import getSecurityManager, Unauthorized
from string import split, join
from types import StringType, ListType, IntType, FloatType
from DateTime import DateTime
class SimpleTreeMaker(SimpleTreeMaker):
class LazyFilter(Lazy):
# A LazyFilter that checks with the security policy
def __init__(self, seq, test=None, skip=None):
self._seq=seq
self._data=[]
self._eindex=-1
self._test=test
if not (skip is None or str(skip) == skip):
raise TypeError, 'Skip must be None or a string'
self._skip = skip
def __getitem__(self,index):
data=self._data
try: s=self._seq
except AttributeError: return data[index]
i=index
if i < 0: i=len(self)+i
if i < 0: raise IndexError, index
ind=len(data)
if i < ind: return data[i]
ind=ind-1
test=self._test
e=self._eindex
skip = self._skip
while i > ind:
try:
e=e+1
try: v = guarded_getitem(s, e)
except Unauthorized, vv:
if skip is None:
msg = '(item %s): %s' % (index, vv)
raise Unauthorized, msg, sys.exc_info()[2]
continue
if skip and not getSecurityManager().checkPermission(skip, v):
continue
if test is None or test(v):
data.append(v)
ind=ind+1
except IndexError:
del self._test
del self._seq
del self._eindex
raise IndexError, index
self._eindex=e
return data[i]
class TreeSkipMixin:
'''Mixin class to make trees test security, and allow
skipping of unauthorized objects. '''
skip = None
def setSkip(self, skip):
self.skip = skip
return self
def getChildren(self, object):
return LazyFilter(self._getChildren(object), skip=self.skip)
class TreeMaker(TreeSkipMixin, TreeMaker):
_getChildren = TreeMaker.getChildren
class SimpleTreeMaker(TreeSkipMixin, SimpleTreeMaker):
_getChildren = SimpleTreeMaker.getChildren
def cookieTree(self, root_object):
'''Make a tree with state stored in a cookie.'''
tree_pre = self.tree_pre
......@@ -121,3 +192,166 @@ class SimpleTreeMaker(SimpleTreeMaker):
rows = tree.flat()
req.RESPONSE.setCookie(state_name, encodeExpansion(rows))
return tree, rows
# Make the Batch class test security, and let it skip unauthorized.
_Batch = Batch
class Batch(Batch):
def __init__(self, sequence, size, start=0, end=0,
orphan=3, overlap=0, skip_unauthorized=None):
sequence = LazyFilter(sequence, skip=skip_unauthorized)
_Batch.__init__(self, sequence, size, start, end,
orphan, overlap)
# These functions are meant to be used together in templates that use
# trees or batches. For example, given a batch with a 'bstart' query
# argument, you would use "url_query(request, omit='bstart')" to get
# the base for the batching links, then append
# "make_query(bstart=batch.previous.first)" to one and
# "make_query(bstart=batch.end)" to the other.
def make_query(*args, **kwargs):
'''Construct a URL query string, with marshalling markup.
If there are positional arguments, they must be dictionaries.
They are combined with the dictionary of keyword arguments to form
a dictionary of query names and values.
Query names (the keys) must be strings. Values may be strings,
integers, floats, or DateTimes, and they may also be lists or
namespaces containing these types. Names and string values
should not be URL-quoted. All arguments are marshalled with
complex_marshal().
'''
d = {}
for arg in args:
d.update(arg)
d.update(kwargs)
uq = urllib.quote
qlist = complex_marshal(d.items())
for i in range(len(qlist)):
k, m, v = qlist[i]
qlist[i] = '%s%s=%s' % (uq(k), m, uq(str(v)))
return join(qlist, '&')
def make_hidden_input(*args, **kwargs):
'''Construct a set of hidden input elements, with marshalling markup.
If there are positional arguments, they must be dictionaries.
They are combined with the dictionary of keyword arguments to form
a dictionary of query names and values.
Query names (the keys) must be strings. Values may be strings,
integers, floats, or DateTimes, and they may also be lists or
namespaces containing these types. All arguments are marshalled with
complex_marshal().
'''
d = {}
for arg in args:
d.update(arg)
d.update(kwargs)
hq = cgi.escape
qlist = complex_marshal(d.items())
for i in range(len(qlist)):
k, m, v = qlist[i]
qlist[i] = ('<input type="hidden" name="%s%s" value="%s">'
% (hq(k), m, hq(str(v))))
return join(qlist, '\n')
def complex_marshal(pairs):
'''Add request marshalling information to a list of name-value pairs.
Names must be strings. Values may be strings,
integers, floats, or DateTimes, and they may also be lists or
namespaces containing these types.
The list is edited in place so that each (name, value) pair
becomes a (name, marshal, value) triple. The middle value is the
request marshalling string. Integer, float, and DateTime values
will have ":int", ":float", or ":date" as their marshal string.
Lists will be flattened, and the elements given ":list" in
addition to their simple marshal string. Dictionaries will be
flattened and marshalled using ":record".
'''
for i in range(len(pairs)):
k, v = pairs[i]
m = ''
sublist = None
if isinstance(v, StringType):
pass
elif hasattr(v, 'items'):
sublist = []
for sk, sv in v.items():
sm = simple_marshal(sv)
sublist.append(('%s.%s' % (k, sk), '%s:record' % sm, sv))
elif isinstance(v, ListType):
sublist = []
for sv in v:
sm = simple_marshal(sv)
sublist.append((k, '%s:list' % sm, sv))
else:
m = simple_marshal(v)
if sublist is None:
pairs[i] = (k, m, v)
else:
pairs[i:i + 1] = sublist
return pairs
def simple_marshal(v):
if isinstance(v, StringType):
return ''
if isinstance(v, IntType):
return ':int'
if isinstance(v, FloatType):
return ':float'
if isinstance(v, DateTime):
return ':date'
return ''
def url_query(request, req_name="URL", omit=None):
'''Construct a URL with a query string, using the current request.
request: the request object
req_name: the name, such as "URL1" or "BASEPATH1", to get from request
omit: sequence of name of query arguments to omit. If a name
contains a colon, it is treated literally. Otherwise, it will
match each argument name that starts with the name and a period or colon.
'''
base = request[req_name]
qs = request.get('QUERY_STRING', '')
if qs and omit:
qsparts = split(qs, '&')
if isinstance(omit, StringType):
omits = {omit: None}
else:
omits = {}
for name in omit:
omits[name] = None
omitted = omits.has_key
unq = urllib.unquote
for i in range(len(qsparts)):
name = unq(split(qsparts[i], '=', 1)[0])
if omitted(name):
qsparts[i] = ''
name = split(name, ':', 1)[0]
if omitted(name):
qsparts[i] = ''
name = split(name, '.', 1)[0]
if omitted(name):
qsparts[i] = ''
qs = join(filter(None, qsparts), '&')
# We alway append '?' since arguments will be appended to the URL
return '%s?%s' % (base, qs)
......@@ -84,8 +84,8 @@
##############################################################################
__doc__='''Package of template utility classes and functions.
$Id: __init__.py,v 1.3 2001/07/11 19:02:22 evan Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
$Id: __init__.py,v 1.4 2001/08/13 18:15:34 evan Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
from Batch import Batch
from Iterator import Iterator
......@@ -98,4 +98,6 @@ if sys.modules.has_key('Zope'):
__allow_access_to_unprotected_subobjects__ = 1
__roles__ = None
from Zope import Batch, SimpleTreeMaker
from Zope import Batch, TreeMaker, SimpleTreeMaker, LazyFilter
from Zope import url_query, make_query, make_hidden_input
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