[Zope] External Methods, Proxy Roles, and Executable Security

Dieter Maurer dieter at handshake.de
Sat Nov 19 14:10:53 EST 2005


George Lee wrote at 2005-11-19 00:46 -0500:
>In CMFCore 1.5.4:
>
>If a low-security-clearance user calls an external method that pastes
>an object from a PortalFolder, he gets an error because the following
>line in CMFCore.PortalFolder fails:
>
>if not sm.checkPermission(DeleteObjects, parent):
>   raise AccessControl_Unauthorized
>
>This is even the case if "sm.checkPermission" is changed to
>"_checkPermission", which takes into account proxy roles. The external
>method does not allow proxy roles attached, so I can't just add a
>"Manager" proxy role.
>
>Because I called the pasting in an external method, I expected it to
>go through without security problems! Is this a right expectation /
>and a bug, or a wrong expectation?

It is the fate induced by explicit security checks.
It will get much worse when the Zope 3 security comes into
Zope 2 land: then even trusted code will have to deal with
security proxied objects.


We currently work around the problem that trusted code
cannot have proxy roles with the following class:

class ProxyContext:
  def __init__(self, proxy_roles):
    self._proxy_roles = tuple(proxy_roles)

  def getOwner(self): return None
  getWrappedOwner = getOwner

This class emulates an object with proxy roles and can be pushed
onto the "SecurityManager"s "context" stack like so:

    sm = getSecurityManager()
    context = ProxyContext(proxy_roles)
    sm.addContext(context)
    try:
        # do something with "proxy_roles"
	...
    finally: sm.removeContext(context)


Note, that I had to fix (in a local copy) CMF's "_checkPermission"
for this to work:

   It had decided to emulate Zope's proxy role checking only
   approximately -- incorrectly for a "None" owner.

My fix looks like this:

security.declarePrivate('_checkPermission')
def _checkPermission(permission, obj):
    """ Check if the current user has the permission on the given object.
    """
    # this code is ported from ZopeSecurityPolicy.checkPermission
    roles = rolesForPermissionOn(permission, obj)
    if isinstance(roles, basestring):
        roles = [roles]
    context = getSecurityManager()._context

    # check executable owner and proxy roles
    # this code is ported from ZopeSecurityPolicy.validate
    stack = context.stack
    if stack:
        eo = stack[-1]
        owner = eo.getOwner()
        if owner is not None:
            if not owner.allowed(obj, roles):
                return 0
            # DM 2005-09-07: no reason to do it differently from Zope
            #   It accepts "proxy_roles" even for a None owner
##            proxy_roles = getattr(eo, '_proxy_roles', None)
##            if proxy_roles:
##                if obj is not aq_base(obj):
##                    if not owner._check_context(obj):
##                        return 0
##                for r in proxy_roles:
##                    if r in roles:
##                         return 1
##                return 0
        proxy_roles = getattr(eo, '_proxy_roles', None)
        if proxy_roles:
            if obj is not aq_base(obj):
                # DM 2005-09-07: do it as Zope does
                #if not owner._check_context(obj):
                if owner is not None and not owner._check_context(obj):
                    return 0
            for r in proxy_roles:
                if r in roles:
                     return 1
            return 0

    return context.user.allowed(obj, roles)


If you are interested in using this approach, you
should probably file another CMF bug report about the
wrong handling of proxy roles in "_checkPermission".
I explicitely allow you to attach the fix given above.


-- 
Dieter


More information about the Zope mailing list