Skip to content

Subtle problem with introduction of new PermissionProxy #3

Open

Description

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 in browserDefault (see sample below).

The code that looks up browserDefault is in zope\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-proxied PermissionProxy instance. The permissions on ob work as expected.

When ob returns itself in browserDefault, 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 where zope:view is used to register a view (see sample zcml below), there will be no checker -- and the security proxy returned by browserDefault 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions