[Zope-dev] Zope 2 WebDAV and acquisition
Martin Aspeli
optilude+lists at gmail.com
Mon Oct 5 11:52:51 EDT 2009
Martin Aspeli wrote:
Martin Aspeli wrote:
> Martin Aspeli wrote:
>> Hi,
>>
>> In my travails through the ZPublisher and WebDAV, I've come up with
>> another fun thing.
>>
>> As far as I can tell, traversal via acquisition doesn't make any sense
>> for a WebDAV request. If I have /foo and /bar, but not /bar/foo, then
>> traversal to /bar/foo over WebDAV should not acquire /foo and wrap it in
>> /bar.
>>
>> One reason for this (apart from being utterly confusing) is that it
>> breaks PUT requests to a NullResource: If I try to PUT to /bar/foo it
>> should create a new object there, not overwrite /foo.
>>
>> There is actually some convoluted support for detecting this in
>> ZPublisher.BaseRequest:
>>
>> # Note - no_acquire_flag is necessary to support
>> # things like DAV. We have to make sure
>> # that the target object is not acquired
>> # if the request_method is other than GET
>> # or POST. Otherwise, you could never use
>> # PUT to add a new object named 'test' if
>> # an object 'test' existed above it in the
>> # heirarchy -- you'd always get the
>> # existing object :(
>> if (no_acquire_flag and
>> hasattr(parents[1], 'aq_base') and
>> not hasattr(parents[1],'__bobo_traverse__')):
>> if not (hasattr(parents[1].aq_base, entry_name) or
>> parents[1].aq_base.has_key(entry_name)):
>> raise AttributeError, entry_name
>>
>> This doesn't really work, though. The object is acquired, and then all
>> it does is to check that the PUT() method object is a true attribute of
>> the acquired object. But even then, raising AttributeError is wrong.
>>
>> What should happen, I think, is that in
>> DefaultPublishTraverse.publishTraverse() we should *not* attempt to
>> acquire for webdav requests. Instead, we should try direct attribute
>> access and then __getitem__() access (which is the next thing it tries
>> after getattr() acquisition). This would then allow the folder to return
>> a NullResource (as OFS.ObjectManager does, for example).
>>
>> Any objections? It's a relatively simple condition in
>> DfaultPublishTraverse.publishTraverse().
>
> Actually, I found some code that may be trying to address this (although
> I think it's probably likely that not attempting to acquire in the first
> place is better).
>
> Basically, it allows acquisition during traversal, but when you get to
> the end of the path, it does this:
>
> if (no_acquire_flag and
> hasattr(object, 'aq_base') and
> not hasattr(object,'__bobo_traverse__')):
> if object.aq_parent is not object.aq_inner.aq_parent:
> from webdav.NullResource import NullResource
> object = NullResource(parents[-2], object.getId(),
> self).__of__(parents[-2])
>
> This would fix the problem, except in my case, the traversed-to object
> (/foo in the example above) happens to have a __bobo_traverse__(), as do
> all CMF types, so the third caluse of the first if statement is false.
>
> I can't see the logic here, though: why would it matter if the acquired
> object happens to have a __bobo_traverse__?
>
> Removing that condition fixes it for me. This would be an even simpler
> bug fix (though I still find it fishy that you can acquire folders
> through WebDAV).
>
> Can anyone explain why that condition is there? Otherwise, I'll rip it
> out. ;-)
>
> Martin
>
--
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book
> Martin Aspeli wrote:
>> Hi,
>>
>> In my travails through the ZPublisher and WebDAV, I've come up with
>> another fun thing.
>>
>> As far as I can tell, traversal via acquisition doesn't make any sense
>> for a WebDAV request. If I have /foo and /bar, but not /bar/foo, then
>> traversal to /bar/foo over WebDAV should not acquire /foo and wrap it in
>> /bar.
>>
>> One reason for this (apart from being utterly confusing) is that it
>> breaks PUT requests to a NullResource: If I try to PUT to /bar/foo it
>> should create a new object there, not overwrite /foo.
>>
>> There is actually some convoluted support for detecting this in
>> ZPublisher.BaseRequest:
>>
>> # Note - no_acquire_flag is necessary to support
>> # things like DAV. We have to make sure
>> # that the target object is not acquired
>> # if the request_method is other than GET
>> # or POST. Otherwise, you could never use
>> # PUT to add a new object named 'test' if
>> # an object 'test' existed above it in the
>> # heirarchy -- you'd always get the
>> # existing object :(
>> if (no_acquire_flag and
>> hasattr(parents[1], 'aq_base') and
>> not hasattr(parents[1],'__bobo_traverse__')):
>> if not (hasattr(parents[1].aq_base, entry_name) or
>> parents[1].aq_base.has_key(entry_name)):
>> raise AttributeError, entry_name
>>
>> This doesn't really work, though. The object is acquired, and then all
>> it does is to check that the PUT() method object is a true attribute of
>> the acquired object. But even then, raising AttributeError is wrong.
>>
>> What should happen, I think, is that in
>> DefaultPublishTraverse.publishTraverse() we should *not* attempt to
>> acquire for webdav requests. Instead, we should try direct attribute
>> access and then __getitem__() access (which is the next thing it tries
>> after getattr() acquisition). This would then allow the folder to return
>> a NullResource (as OFS.ObjectManager does, for example).
>>
>> Any objections? It's a relatively simple condition in
>> DfaultPublishTraverse.publishTraverse().
>
> Actually, I found some code that may be trying to address this (although
> I think it's probably likely that not attempting to acquire in the first
> place is better).
>
> Basically, it allows acquisition during traversal, but when you get to
> the end of the path, it does this:
>
> if (no_acquire_flag and
> hasattr(object, 'aq_base') and
> not hasattr(object,'__bobo_traverse__')):
> if object.aq_parent is not object.aq_inner.aq_parent:
> from webdav.NullResource import NullResource
> object = NullResource(parents[-2], object.getId(),
> self).__of__(parents[-2])
>
> This would fix the problem, except in my case, the traversed-to object
> (/foo in the example above) happens to have a __bobo_traverse__(), as do
> all CMF types, so the third caluse of the first if statement is false.
>
> I can't see the logic here, though: why would it matter if the acquired
> object happens to have a __bobo_traverse__?
>
> Removing that condition fixes it for me. This would be an even simpler
> bug fix (though I still find it fishy that you can acquire folders
> through WebDAV).
>
> Can anyone explain why that condition is there? Otherwise, I'll rip it
> out. ;-)
One more thing to note: __bobo_traverse__() is eventually called on the
(wrongly acquired) object, to traverse to the PUT() HTTP verb handler
method.
I suppose it's possible it could want to do something wildly different,
thus making the "don't acquire" condition that returns a NullResource
above incorrect but this still seems very convoluted.
It would need to do a check like "if aq_parent(self) is not
aq_parent(aq_inner(self)) and is_webdav_request: return
NullResource(aq_parent(self), self.getId(), request)" even if the name
is actually available on the object (in this case, the PUT() method).
Demanding that everyone that implements a __bobo_traverse__ does this
seems really onerous and error prone. I can't see any situations where
you'd want to be able to do this on a resource that was acquired from
somewhere other than its containment parent.
Martin
--
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book
More information about the Zope-Dev
mailing list