[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