Description
openedon Jan 24, 2015
In https://bugs.launchpad.net/zope.security/+bug/98190, Garrett Smith reported:
This is subtle problem, and a little hard to explain. I've included a sample view + ZCML to illustrate -- see below.
The problem comes up when you have a view that implements
IBrowserPublisher
and returns itself inbrowserDefault
(see sample below).The code that looks up
browserDefault
is inzope\app\publication\browser.py
starting at line 56:
if IBrowserPublisher.providedBy(ob):
# ob is already proxied, so the result of ...
return ob.browserDefault(request)
When
PermissionProxy
is used,ob
is a correctly security-proxiedPermissionProxy
instance. The permissions onob
work as expected.When
ob
returns itself inbrowserDefault
, however, it returns a security-proxied version of the base object -- not the permission proxy that owns the__Security_checker__
. Because__Security_checker__
isn't available, the security proxy uses whatever checker is registered for the view type. In the case wherezope:view
is used to register a view (see sample zcml below), there will be no checker -- and the security proxy returned bybrowserDefault
will be entirely forbidden.This problem didn't occur before because
proxify
either modified the utility's__Security_checker__
directly, or created a security proxy outright.This may not actually be a 'bug', but it's very subtle behavior -- and hard to track down if you run into it. There are a couple work-arounds in ZCML:
- Declare permissions for the view class
- Use the
zope:adapter
directive to register the view
# test.py
from zope.interface import Interface, Attribute, implements
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.app.traversing.interfaces import TraversalError
class ISampleView(IBrowserPublisher):
foo = Attribute("Sample attr.")
class SampleView(object):
implements(ISampleView)
def __init__(self, context, request):
self.context = context
self.request = request
self.foo = 'Foo'
def browserDefault(self, request):
return self, ()
def publishTraverse(self, request, name):
raise TraversalError(self, name, request)
def __call__(self):
return self.foo
<!-- test-configure.zcml -->
<configure xmlns="http://namespaces.zope.org/zope" i18n_domain="test">
<view
name="test"
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="*"
provides="test.ISampleView"
factory="test.SampleView"
permission="zope.Public"
allowed_interface="test.ISampleView" />
</configure>