[Zope-dev] restrictedTraverse() security problem

Terry Hancock hancock@anansispaceworks.com
Tue, 6 May 2003 10:41:58 -0700


[Resending to Zope-dev, because on reflection I think it
belongs there -- is there such a thing as a Zope product/
component developer list, or is this it?]

Hi All,
I have a call like this in restricted code:

Home.restrictedTraverse(username).profile()

which should call a method "profile" on a "UserPB" object
(this method is public information, and should be visible
to anyone).

The catch is that UserPB is a wrapper on a database
record, which I'm adding by using a __bobo_traverse__
method:

class Home(Folder):
    meta_type = 'Narya-Home'
    __implements__ = HomeAPI
    security = ClassSecurityInfo()
    security.setDefaultAccess("allow") 
#     ... 
    def __bobo_traverse__(self, REQUEST=None, name=None):
	"""Get a UserFolder if it exists, or a UserPB."""
	try:
	    return getattr(self, name)
	except:
	    return self.getUser(name)

    def getUser(self, username):
	"""Retrieve UserPB object from username."""
	return UserPB(self.DB._getUser(username=username)[0]).__of__(self)

class UserPB(Acquisition.Explicit, Item):
    """Basic wrapper for user data from database."""
    __implements__ = UserPBI
#   ...
    def __init__(self, sqlres):
	"""
	Create a UserPB object by wrapping the SQL search result.
	"""
	for k in sqlres.__record_schema__.keys():
	    setattr(self, k, getattr(sqlres, k))

Accessing this causes an error
"""
Error Type: Unauthorized
Error Value: You are not allowed to access foo in this context
"""
(where "foo" is the actual username).
There is such a user, and the wrapper works perfectly when called
from unrestricted code, so the problem is definitely the security.

Here's the tail end of the traceback:
"""
  File /usr/local/narya/z2.5.1/lib/python/DocumentTemplate/DT_Util.py, line 159, in eval
    (Object: Home.restrictedTraverse(username).post_score(nposts, nrecent, nmarkup, ndoc, nfaq, nlink, ngallery, nproj))
    (Info: username)
  File <string>, line 0, in ?
  File /usr/local/narya/z2.5.1/lib/python/OFS/Traversable.py, line 163, in restrictedTraverse
    (Object: Home)
  File /usr/local/narya/z2.5.1/lib/python/OFS/Traversable.py, line 130, in unrestrictedTraverse
    (Object: Home)
Unauthorized: (see above)
"""
Which indeed is the security check:

[OFS/Traversable.py]:
"""
if (not securityManager.validate(object, container, name, o)):
    raise Unauthorized, name
"""

So it seems I need to do something to "call off the dogs" here. I've
already set my default policy in "Home" to "allow" -- what's missing?
Do I have to explicitly call a security method to do that, or is
there an attribute or something needed?  Also, is it really the Home
or the UserPB object whose security is not right?

I have not yet been able to figure out where "validate()" is actually
defined -- it seems the securityManager object expects to inherit
or acquire it from somewhere.

I realize there's probably another way to do the large task here,
but there are a number of reasons why it's more convenient to do
this mimicry trick to make a database record look like a simple
child object of Home -- not least of which is that I want to handle
them in parallel with persistent Zope objects ("UserFolder") which
implement an identical "UserPBI" interface.

Any suggestions or ideas would be very much appreciated!

Thanks,
Terry

--
Terry Hancock ( hancock at anansispaceworks.com )
Anansi Spaceworks  http://www.anansispaceworks.com