Commit 96c3005c authored by Tres Seaver's avatar Tres Seaver

Synch with 2.10 branch.

parent 37462b85
...@@ -18,6 +18,14 @@ Zope Changes ...@@ -18,6 +18,14 @@ Zope Changes
Bugs Fixed Bugs Fixed
- reStructuredText/ZReST: setting raw_enabled to 0 for security
reasons
- Collector #2113: 'zopectl test' masked Ctrl-C.
- OFS Image: Image and File updated to use isinstance(data, str)
and raises TypeError upon encountering unicode objects.
- OFS Application: Updated deprecation warnings. - OFS Application: Updated deprecation warnings.
Support for '__ac_permissions__' and 'meta_types' will be removed in Support for '__ac_permissions__' and 'meta_types' will be removed in
Zope 2.11, 'methods' support might remain longer. Zope 2.11, 'methods' support might remain longer.
......
...@@ -43,7 +43,6 @@ from zExceptions import Redirect ...@@ -43,7 +43,6 @@ from zExceptions import Redirect
from cgi import escape from cgi import escape
import transaction import transaction
StringType=type('')
manage_addFileForm=DTMLFile('dtml/imageAdd', globals(),Kind='File',kind='file') manage_addFileForm=DTMLFile('dtml/imageAdd', globals(),Kind='File',kind='file')
def manage_addFile(self,id,file='',title='',precondition='', content_type='', def manage_addFile(self,id,file='',title='',precondition='', content_type='',
REQUEST=None): REQUEST=None):
...@@ -231,7 +230,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -231,7 +230,7 @@ class File(Persistent, Implicit, PropertyManager,
RESPONSE.setStatus(206) # Partial content RESPONSE.setStatus(206) # Partial content
data = self.data data = self.data
if type(data) is StringType: if isinstance(data, str):
RESPONSE.write(data[start:end]) RESPONSE.write(data[start:end])
return True return True
...@@ -302,7 +301,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -302,7 +301,7 @@ class File(Persistent, Implicit, PropertyManager,
'Content-Range: bytes %d-%d/%d\r\n\r\n' % ( 'Content-Range: bytes %d-%d/%d\r\n\r\n' % (
start, end - 1, self.size)) start, end - 1, self.size))
if type(data) is StringType: if isinstance(data, str):
RESPONSE.write(data[start:end]) RESPONSE.write(data[start:end])
else: else:
...@@ -401,7 +400,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -401,7 +400,7 @@ class File(Persistent, Implicit, PropertyManager,
self.ZCacheable_set(None) self.ZCacheable_set(None)
data=self.data data=self.data
if type(data) is type(''): if isinstance(data, str):
RESPONSE.setBase(None) RESPONSE.setBase(None)
return data return data
...@@ -428,6 +427,10 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -428,6 +427,10 @@ class File(Persistent, Implicit, PropertyManager,
security.declarePrivate('update_data') security.declarePrivate('update_data')
def update_data(self, data, content_type=None, size=None): def update_data(self, data, content_type=None, size=None):
if isinstance(data, unicode):
raise TypeError('Data can only be str or file-like. '
'Unicode objects are expressly forbidden.')
if content_type is not None: self.content_type=content_type if content_type is not None: self.content_type=content_type
if size is None: size=len(data) if size is None: size=len(data)
self.size=size self.size=size
...@@ -481,7 +484,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -481,7 +484,7 @@ class File(Persistent, Implicit, PropertyManager,
if headers and headers.has_key('content-type'): if headers and headers.has_key('content-type'):
content_type=headers['content-type'] content_type=headers['content-type']
else: else:
if type(body) is not type(''): body=body.data if not isinstance(body, str): body=body.data
content_type, enc=guess_content_type( content_type, enc=guess_content_type(
getattr(file, 'filename',id), body, content_type) getattr(file, 'filename',id), body, content_type)
return content_type return content_type
...@@ -490,7 +493,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -490,7 +493,7 @@ class File(Persistent, Implicit, PropertyManager,
n=1 << 16 n=1 << 16
if type(file) is StringType: if isinstance(file, str):
size=len(file) size=len(file)
if size < n: return file, size if size < n: return file, size
# Big string: cut it into smaller chunks # Big string: cut it into smaller chunks
...@@ -617,7 +620,7 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -617,7 +620,7 @@ class File(Persistent, Implicit, PropertyManager,
return result return result
data = self.data data = self.data
if type(data) is type(''): if isinstance(data, str):
RESPONSE.setBase(None) RESPONSE.setBase(None)
return data return data
...@@ -777,6 +780,10 @@ class Image(File): ...@@ -777,6 +780,10 @@ class Image(File):
security.declarePrivate('update_data') security.declarePrivate('update_data')
def update_data(self, data, content_type=None, size=None): def update_data(self, data, content_type=None, size=None):
if isinstance(data, unicode):
raise TypeError('Data can only be str or file-like. '
'Unicode objects are expressly forbidden.')
if size is None: size=len(data) if size is None: size=len(data)
self.size=size self.size=size
......
...@@ -237,11 +237,11 @@ class Traversable: ...@@ -237,11 +237,11 @@ class Traversable:
if not validated: if not validated:
raise Unauthorized, name raise Unauthorized, name
else: else:
if hasattr(aq_base(obj), name): if getattr(aq_base(obj), name, marker) is not marker:
if restricted: if restricted:
next = guarded_getattr(obj, name, marker) next = guarded_getattr(obj, name)
else: else:
next = _getattr(obj, name, marker) next = _getattr(obj, name)
else: else:
try: try:
next=obj[name] next=obj[name]
...@@ -249,6 +249,9 @@ class Traversable: ...@@ -249,6 +249,9 @@ class Traversable:
# Raise NotFound for easier debugging # Raise NotFound for easier debugging
# instead of AttributeError: __getitem__ # instead of AttributeError: __getitem__
raise NotFound, name raise NotFound, name
if restricted and not securityManager.validate(
obj, obj, _none, next):
raise Unauthorized, name
except (AttributeError, NotFound, KeyError), e: except (AttributeError, NotFound, KeyError), e:
# Try to look for a view # Try to look for a view
...@@ -270,13 +273,10 @@ class Traversable: ...@@ -270,13 +273,10 @@ class Traversable:
next = _getattr(obj, name, marker) next = _getattr(obj, name, marker)
except AttributeError: except AttributeError:
raise e raise e
if next is marker: if next is marker:
# Nothing found re-raise error # Nothing found re-raise error
raise e raise e
if restricted and not securityManager.validate(
obj, obj, _none, next):
raise Unauthorized, name
obj = next obj = next
return obj return obj
......
...@@ -252,7 +252,12 @@ class FileTests(unittest.TestCase): ...@@ -252,7 +252,12 @@ class FileTests(unittest.TestCase):
verifyClass(HTTPRangeInterface, File) verifyClass(HTTPRangeInterface, File)
verifyClass(WriteLockInterface, File) verifyClass(WriteLockInterface, File)
def testUnicode(self):
val = u'some unicode string here'
self.assertRaises(TypeError, self.file.manage_edit,
'foobar', 'text/plain', filedata=val)
class ImageTests(FileTests): class ImageTests(FileTests):
data = open(filedata, 'rb').read() data = open(filedata, 'rb').read()
content_type = 'image/gif' content_type = 'image/gif'
...@@ -285,7 +290,6 @@ class ImageTests(FileTests): ...@@ -285,7 +290,6 @@ class ImageTests(FileTests):
verifyClass(WriteLockInterface, Image) verifyClass(WriteLockInterface, Image)
def test_suite(): def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
unittest.makeSuite(FileTests), unittest.makeSuite(FileTests),
......
...@@ -68,6 +68,24 @@ class CruelSecurityPolicy: ...@@ -68,6 +68,24 @@ class CruelSecurityPolicy:
return 0 return 0
class ProtectedMethodSecurityPolicy:
"""Check security strictly on bound methods.
"""
def validate(self, accessed, container, name, value, *args):
if getattr(aq_base(value), 'im_self', None) is None:
return 1
# Bound method
if name is None:
raise Unauthorized
klass = value.im_self.__class__
roles = getattr(klass, name+'__roles__', object())
if roles is None: # ACCESS_PUBLIC
return 1
raise Unauthorized(name)
class UnitTestUser( Acquisition.Implicit ): class UnitTestUser( Acquisition.Implicit ):
""" """
Stubbed out manager for unit testing purposes. Stubbed out manager for unit testing purposes.
...@@ -103,6 +121,22 @@ class BoboTraversable(SimpleItem): ...@@ -103,6 +121,22 @@ class BoboTraversable(SimpleItem):
bb_status = 'screechy' bb_status = 'screechy'
class Restricted(SimpleItem):
"""Instance we'll check with ProtectedMethodSecurityPolicy
"""
getId__roles__ = None # ACCESS_PUBLIC
def getId(self):
return self.id
private__roles__ = () # ACCESS_PRIVATE
def private(self):
return 'private!'
# not protected
def ohno(self):
return 'ohno!'
class BoboTraversableWithAcquisition(SimpleItem): class BoboTraversableWithAcquisition(SimpleItem):
""" """
A BoboTraversable class which may use acquisition to find objects. A BoboTraversable class which may use acquisition to find objects.
...@@ -210,6 +244,17 @@ class TestTraverse( unittest.TestCase ): ...@@ -210,6 +244,17 @@ class TestTraverse( unittest.TestCase ):
self.failUnlessRaises( self.failUnlessRaises(
KeyError, self.folder1.unrestrictedTraverse, '/folder1/file2/' ) KeyError, self.folder1.unrestrictedTraverse, '/folder1/file2/' )
def testTraverseMethodRestricted(self):
self.root.my = Restricted('my')
my = self.root.my
my.id = 'my'
noSecurityManager()
SecurityManager.setSecurityPolicy(ProtectedMethodSecurityPolicy())
r = my.restrictedTraverse('getId')
self.assertEquals(r(), 'my')
self.assertRaises(Unauthorized, my.restrictedTraverse, 'private')
self.assertRaises(Unauthorized, my.restrictedTraverse, 'ohno')
def testBoboTraverseToWrappedSubObj(self): def testBoboTraverseToWrappedSubObj(self):
# Verify it's possible to use __bobo_traverse__ with the # Verify it's possible to use __bobo_traverse__ with the
# Zope security policy. # Zope security policy.
......
...@@ -204,6 +204,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -204,6 +204,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
'stylesheet_path' : None, 'stylesheet_path' : None,
'pub.settings.warning_stream' : Warnings(), 'pub.settings.warning_stream' : Warnings(),
'file_insertion_enabled' : 0, 'file_insertion_enabled' : 0,
'raw_enabled' : 0,
} }
self._v_formatted = docutils.core.publish_string( self._v_formatted = docutils.core.publish_string(
......
...@@ -258,14 +258,20 @@ class ZopeCmd(ZDCmd): ...@@ -258,14 +258,20 @@ class ZopeCmd(ZDCmd):
pid = os.fork() pid = os.fork()
if pid == 0: # child if pid == 0: # child
os.execv(self.options.python, args) os.execv(self.options.python, args)
else:
os.waitpid(pid, 0) # Parent process running (execv replaces process in child
while True:
try:
os.waitpid(pid, 0)
except (OSError, KeyboardInterrupt):
continue
else:
break
def help_test(self): def help_test(self):
print "test [args]+ -- run unit / functional tests." print "test [args]+ -- run unit / functional tests."
print " See $ZOPE_HOME/bin/test.py --help for syntax." print " See $ZOPE_HOME/bin/test.py --help for syntax."
def main(args=None): def main(args=None):
# This is exactly like zdctl.main(), but uses ZopeCtlOptions and # This is exactly like zdctl.main(), but uses ZopeCtlOptions and
# ZopeCmd instead of ZDCtlOptions and ZDCmd, so the default values # ZopeCmd instead of ZDCtlOptions and ZDCmd, so the default values
......
...@@ -73,6 +73,7 @@ def render(src, ...@@ -73,6 +73,7 @@ def render(src,
settings['stylesheet'] = stylesheet settings['stylesheet'] = stylesheet
settings['stylesheet_path'] = None settings['stylesheet_path'] = None
settings['file_insertion_enabled'] = 0 settings['file_insertion_enabled'] = 0
settings['raw_enabled'] = 0
if language_code: if language_code:
settings['language_code'] = language_code settings['language_code'] = language_code
settings['language_code'] = language_code settings['language_code'] = language_code
......
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